Bitcoin Core  31.0.0
P2P Digital Currency
request.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-present The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include <rpc/request.h>
7 
8 #include <common/args.h>
9 #include <logging.h>
10 #include <random.h>
11 #include <rpc/protocol.h>
12 #include <util/fs.h>
13 #include <util/fs_helpers.h>
14 #include <util/strencodings.h>
15 
16 #include <fstream>
17 #include <stdexcept>
18 #include <string>
19 #include <vector>
20 
41 UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id)
42 {
43  UniValue request(UniValue::VOBJ);
44  request.pushKV("method", strMethod);
45  request.pushKV("params", params);
46  request.pushKV("id", id);
47  request.pushKV("jsonrpc", "2.0");
48  return request;
49 }
50 
51 UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional<UniValue> id, JSONRPCVersion jsonrpc_version)
52 {
53  UniValue reply(UniValue::VOBJ);
54  // Add JSON-RPC version number field in v2 only.
55  if (jsonrpc_version == JSONRPCVersion::V2) reply.pushKV("jsonrpc", "2.0");
56 
57  // Add both result and error fields in v1, even though one will be null.
58  // Omit the null field in v2.
59  if (error.isNull()) {
60  reply.pushKV("result", std::move(result));
61  if (jsonrpc_version == JSONRPCVersion::V1_LEGACY) reply.pushKV("error", NullUniValue);
62  } else {
63  if (jsonrpc_version == JSONRPCVersion::V1_LEGACY) reply.pushKV("result", NullUniValue);
64  reply.pushKV("error", std::move(error));
65  }
66  if (id.has_value()) reply.pushKV("id", std::move(id.value()));
67  return reply;
68 }
69 
70 UniValue JSONRPCError(int code, const std::string& message)
71 {
72  UniValue error(UniValue::VOBJ);
73  error.pushKV("code", code);
74  error.pushKV("message", message);
75  return error;
76 }
77 
81 static const std::string COOKIEAUTH_USER = "__cookie__";
83 static const char* const COOKIEAUTH_FILE = ".cookie";
84 
86 static fs::path GetAuthCookieFile(bool temp=false)
87 {
88  fs::path arg = gArgs.GetPathArg("-rpccookiefile", COOKIEAUTH_FILE);
89  if (arg.empty()) {
90  return {}; // -norpccookiefile was specified
91  }
92  if (temp) {
93  arg += ".tmp";
94  }
95  return AbsPathForConfigVal(gArgs, arg);
96 }
97 
98 static bool g_generated_cookie = false;
99 
100 GenerateAuthCookieResult GenerateAuthCookie(const std::optional<fs::perms>& cookie_perms,
101  std::string& user,
102  std::string& pass)
103 {
104  const size_t COOKIE_SIZE = 32;
105  unsigned char rand_pwd[COOKIE_SIZE];
106  GetRandBytes(rand_pwd);
107  const std::string rand_pwd_hex{HexStr(rand_pwd)};
108 
112  std::ofstream file;
113  fs::path filepath_tmp = GetAuthCookieFile(true);
114  if (filepath_tmp.empty()) {
115  return GenerateAuthCookieResult::DISABLED; // -norpccookiefile
116  }
117  file.open(filepath_tmp.std_path());
118  if (!file.is_open()) {
119  LogWarning("Unable to open cookie authentication file %s for writing", fs::PathToString(filepath_tmp));
121  }
122  file << COOKIEAUTH_USER << ":" << rand_pwd_hex;
123  file.close();
124 
125  fs::path filepath = GetAuthCookieFile(false);
126  if (!RenameOver(filepath_tmp, filepath)) {
127  LogWarning("Unable to rename cookie authentication file %s to %s", fs::PathToString(filepath_tmp), fs::PathToString(filepath));
129  }
130  if (cookie_perms) {
131  std::error_code code;
132  fs::permissions(filepath, cookie_perms.value(), fs::perm_options::replace, code);
133  if (code) {
134  LogWarning("Unable to set permissions on cookie authentication file %s", fs::PathToString(filepath));
136  }
137  }
138 
139  g_generated_cookie = true;
140  LogInfo("Generated RPC authentication cookie %s\n", fs::PathToString(filepath));
141  LogInfo("Permissions used for cookie: %s\n", PermsToSymbolicString(fs::status(filepath).permissions()));
142 
143  user = COOKIEAUTH_USER;
144  pass = rand_pwd_hex;
146 }
147 
148 bool GetAuthCookie(std::string *cookie_out)
149 {
150  std::ifstream file;
151  std::string cookie;
152  fs::path filepath = GetAuthCookieFile();
153  if (filepath.empty()) {
154  return true; // -norpccookiefile
155  }
156  file.open(filepath.std_path());
157  if (!file.is_open())
158  return false;
159  std::getline(file, cookie);
160  file.close();
161 
162  if (cookie_out)
163  *cookie_out = cookie;
164  return true;
165 }
166 
168 {
169  try {
170  if (g_generated_cookie) {
171  // Delete the cookie file if it was generated by this process
172  fs::remove(GetAuthCookieFile());
173  }
174  } catch (const fs::filesystem_error& e) {
175  LogWarning("Unable to remove random auth cookie file %s: %s\n", fs::PathToString(e.path1()), e.code().message());
176  }
177 }
178 
179 std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in)
180 {
181  if (!in.isArray()) {
182  throw std::runtime_error("Batch must be an array");
183  }
184  const size_t num {in.size()};
185  std::vector<UniValue> batch(num);
186  for (const UniValue& rec : in.getValues()) {
187  if (!rec.isObject()) {
188  throw std::runtime_error("Batch member must be an object");
189  }
190  size_t id = rec["id"].getInt<int>();
191  if (id >= num) {
192  throw std::runtime_error("Batch member id is larger than batch size");
193  }
194  batch[id] = rec;
195  }
196  return batch;
197 }
198 
199 void JSONRPCRequest::parse(const UniValue& valRequest)
200 {
201  // Parse request
202  if (!valRequest.isObject())
203  throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
204  const UniValue& request = valRequest.get_obj();
205 
206  // Parse id now so errors from here on will have the id
207  if (request.exists("id")) {
208  id = request.find_value("id");
209  } else {
210  id = std::nullopt;
211  }
212 
213  // Check for JSON-RPC 2.0 (default 1.1)
215  const UniValue& jsonrpc_version = request.find_value("jsonrpc");
216  if (!jsonrpc_version.isNull()) {
217  if (!jsonrpc_version.isStr()) {
218  throw JSONRPCError(RPC_INVALID_REQUEST, "jsonrpc field must be a string");
219  }
220  // The "jsonrpc" key was added in the 2.0 spec, but some older documentation
221  // incorrectly included {"jsonrpc":"1.0"} in a request object, so we
222  // maintain that for backwards compatibility.
223  if (jsonrpc_version.get_str() == "1.0") {
225  } else if (jsonrpc_version.get_str() == "2.0") {
227  } else {
228  throw JSONRPCError(RPC_INVALID_REQUEST, "JSON-RPC version not supported");
229  }
230  }
231 
232  // Parse method
233  const UniValue& valMethod{request.find_value("method")};
234  if (valMethod.isNull())
235  throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
236  if (!valMethod.isStr())
237  throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
238  strMethod = valMethod.get_str();
239  if (fLogIPs)
240  LogDebug(BCLog::RPC, "ThreadRPCServer method=%s user=%s peeraddr=%s\n", SanitizeString(strMethod),
241  this->authUser, this->peerAddr);
242  else
243  LogDebug(BCLog::RPC, "ThreadRPCServer method=%s user=%s\n", SanitizeString(strMethod), this->authUser);
244 
245  // Parse params
246  const UniValue& valParams{request.find_value("params")};
247  if (valParams.isArray() || valParams.isObject())
248  params = valParams;
249  else if (valParams.isNull())
251  else
252  throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
253 }
static const char *const COOKIEAUTH_FILE
Default name for auth cookie file.
Definition: request.cpp:83
GenerateAuthCookieResult GenerateAuthCookie(const std::optional< fs::perms > &cookie_perms, std::string &user, std::string &pass)
Generate a new RPC authentication cookie and write it to disk.
Definition: request.cpp:100
fs::path AbsPathForConfigVal(const ArgsManager &args, const fs::path &path, bool net_specific=true)
Most paths passed as configuration arguments are treated as relative to the datadir if they are not a...
Definition: config.cpp:226
bool isObject() const
Definition: univalue.h:88
fs::path GetPathArg(std::string arg, const fs::path &default_value={}) const
Return path argument or default value.
Definition: args.cpp:276
const std::vector< UniValue > & getValues() const
#define LogWarning(...)
Definition: log.h:96
const std::string & get_str() const
bool isStr() const
Definition: univalue.h:85
void DeleteAuthCookie()
Delete RPC authentication cookie from disk.
Definition: request.cpp:167
bool RenameOver(fs::path src, fs::path dest)
Rename src to dest.
Definition: fs_helpers.cpp:243
const UniValue & find_value(std::string_view key) const
Definition: univalue.cpp:232
std::string strMethod
Definition: request.h:56
std::vector< UniValue > JSONRPCProcessBatchReply(const UniValue &in)
Parse JSON-RPC batch reply into a vector.
Definition: request.cpp:179
std::string SanitizeString(std::string_view str, int rule)
Remove unsafe chars.
std::string peerAddr
Definition: request.h:61
void GetRandBytes(std::span< unsigned char > bytes) noexcept
Generate random data via the internal PRNG.
Definition: random.cpp:601
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:70
UniValue params
Definition: request.h:57
#define LogInfo(...)
Definition: log.h:95
bool exists(const std::string &key) const
Definition: univalue.h:79
bool isNull() const
Definition: univalue.h:81
UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional< UniValue > id, JSONRPCVersion jsonrpc_version)
Definition: request.cpp:51
ArgsManager gArgs
Definition: args.cpp:40
JSONRPCVersion
Definition: request.h:16
#define LogDebug(category,...)
Definition: log.h:115
void parse(const UniValue &valRequest)
Definition: request.cpp:199
auto result
Definition: common-types.h:74
static bool g_generated_cookie
Definition: request.cpp:98
JSONRPCVersion m_json_version
Definition: request.h:63
void pushKV(std::string key, UniValue val)
Definition: univalue.cpp:125
const UniValue & get_obj() const
bool fLogIPs
Definition: logging.cpp:47
std::string authUser
Definition: request.h:60
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:157
UniValue JSONRPCRequestObj(const std::string &strMethod, const UniValue &params, const UniValue &id)
JSON-RPC protocol.
Definition: request.cpp:41
const UniValue NullUniValue
Definition: univalue.cpp:15
GenerateAuthCookieResult
Definition: request.h:26
static fs::path GetAuthCookieFile(bool temp=false)
Get name of RPC authentication cookie file.
Definition: request.cpp:86
Standard JSON-RPC 2.0 errors.
Definition: protocol.h:29
size_t size() const
Definition: univalue.h:71
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:33
bool GetAuthCookie(std::string *cookie_out)
Read the RPC authentication cookie from disk.
Definition: request.cpp:148
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:30
bool isArray() const
Definition: univalue.h:87
static const std::string COOKIEAUTH_USER
Username used when cookie authentication is in use (arbitrary, only for recognizability in debugging/...
Definition: request.cpp:81
std::string PermsToSymbolicString(fs::perms p)
Convert fs::perms to symbolic string of the form &#39;rwxrwxrwx&#39;.
Definition: fs_helpers.cpp:268
std::filesystem::path & std_path()
Definition: fs.h:39