Bitcoin Core 32.99.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 <crypto/hex_base.h>
10#include <logging.h>
11#include <random.h>
12#include <rpc/protocol.h>
13#include <util/fs.h>
14#include <util/fs_helpers.h>
15#include <util/strencodings.h>
16
17#include <cstddef>
18#include <fstream>
19#include <span>
20#include <stdexcept>
21#include <string>
22#include <system_error>
23#include <utility>
24#include <vector>
25
46UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id)
47{
48 UniValue request(UniValue::VOBJ);
49 request.pushKV("method", strMethod);
50 request.pushKV("params", params);
51 request.pushKV("id", id);
52 request.pushKV("jsonrpc", "2.0");
53 return request;
54}
55
56UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional<UniValue> id, JSONRPCVersion jsonrpc_version)
57{
59 // Add JSON-RPC version number field in v2 only.
60 if (jsonrpc_version == JSONRPCVersion::V2) reply.pushKV("jsonrpc", "2.0");
61
62 // Add both result and error fields in v1, even though one will be null.
63 // Omit the null field in v2.
64 if (error.isNull()) {
65 reply.pushKV("result", std::move(result));
66 if (jsonrpc_version == JSONRPCVersion::V1_LEGACY) reply.pushKV("error", NullUniValue);
67 } else {
68 if (jsonrpc_version == JSONRPCVersion::V1_LEGACY) reply.pushKV("result", NullUniValue);
69 reply.pushKV("error", std::move(error));
70 }
71 if (id.has_value()) reply.pushKV("id", std::move(id.value()));
72 return reply;
73}
74
75UniValue JSONRPCError(int code, const std::string& message)
76{
78 error.pushKV("code", code);
79 error.pushKV("message", message);
80 return error;
81}
82
86static const std::string COOKIEAUTH_USER = "__cookie__";
88static const char* const COOKIEAUTH_FILE = ".cookie";
89
91static fs::path GetAuthCookieFile(bool temp=false)
92{
93 fs::path arg = gArgs.GetPathArg("-rpccookiefile", COOKIEAUTH_FILE);
94 if (arg.empty()) {
95 return {}; // -norpccookiefile was specified
96 }
97 if (temp) {
98 arg += ".tmp";
99 }
100 return AbsPathForConfigVal(gArgs, arg);
101}
102
103static bool g_generated_cookie = false;
104
105AuthCookieResult GenerateAuthCookie(const std::optional<fs::perms>& cookie_perms,
106 std::string& user,
107 std::string& pass)
108{
109 const size_t COOKIE_SIZE = 32;
110 unsigned char rand_pwd[COOKIE_SIZE];
111 GetRandBytes(rand_pwd);
112 const std::string rand_pwd_hex{HexStr(rand_pwd)};
113
117 std::ofstream file;
118 fs::path filepath_tmp = GetAuthCookieFile(true);
119 if (filepath_tmp.empty()) {
120 return AuthCookieResult::Disabled; // -norpccookiefile
121 }
122 file.open(filepath_tmp.std_path());
123 if (!file.is_open()) {
124 LogWarning("Unable to open cookie authentication file %s for writing", fs::PathToString(filepath_tmp));
126 }
127 file << COOKIEAUTH_USER << ":" << rand_pwd_hex;
128 file.close();
129
130 fs::path filepath = GetAuthCookieFile(false);
131 if (!RenameOver(filepath_tmp, filepath)) {
132 LogWarning("Unable to rename cookie authentication file %s to %s", fs::PathToString(filepath_tmp), fs::PathToString(filepath));
134 }
135 if (cookie_perms) {
136 std::error_code code;
137 fs::permissions(filepath, cookie_perms.value(), fs::perm_options::replace, code);
138 if (code) {
139 LogWarning("Unable to set permissions on cookie authentication file %s", fs::PathToString(filepath));
141 }
142 }
143
144 g_generated_cookie = true;
145 LogInfo("Generated RPC authentication cookie %s\n", fs::PathToString(filepath));
146 LogInfo("Permissions used for cookie: %s\n", PermsToSymbolicString(fs::status(filepath).permissions()));
147
148 user = COOKIEAUTH_USER;
149 pass = rand_pwd_hex;
151}
152
153AuthCookieResult GetAuthCookie(std::string& cookie_out)
154{
155 std::ifstream file;
156 fs::path filepath = GetAuthCookieFile();
157 if (filepath.empty()) {
158 return AuthCookieResult::Disabled; // -norpccookiefile
159 }
160 file.open(filepath.std_path());
161 if (!file.is_open()) {
163 }
164 std::getline(file, cookie_out);
165 file.close();
167}
168
170{
171 try {
172 if (g_generated_cookie) {
173 // Delete the cookie file if it was generated by this process
174 fs::remove(GetAuthCookieFile());
175 }
176 } catch (const fs::filesystem_error& e) {
177 LogWarning("Unable to remove random auth cookie file %s: %s\n", fs::PathToString(e.path1()), e.code().message());
178 }
179}
180
181std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in)
182{
183 if (!in.isArray()) {
184 throw std::runtime_error("Batch must be an array");
185 }
186 const size_t num {in.size()};
187 std::vector<UniValue> batch(num);
188 for (const UniValue& rec : in.getValues()) {
189 if (!rec.isObject()) {
190 throw std::runtime_error("Batch member must be an object");
191 }
192 size_t id = rec["id"].getInt<int>();
193 if (id >= num) {
194 throw std::runtime_error("Batch member id is larger than batch size");
195 }
196 batch[id] = rec;
197 }
198 return batch;
199}
200
201void JSONRPCRequest::parse(const UniValue& valRequest)
202{
203 // Parse request
204 if (!valRequest.isObject())
205 throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
206 const UniValue& request = valRequest.get_obj();
207
208 // Parse id now so errors from here on will have the id
209 if (request.exists("id")) {
210 id = request.find_value("id");
211 } else {
212 id = std::nullopt;
213 }
214
215 // Check for JSON-RPC 2.0 (default 1.1)
217 const UniValue& jsonrpc_version = request.find_value("jsonrpc");
218 if (!jsonrpc_version.isNull()) {
219 if (!jsonrpc_version.isStr()) {
220 throw JSONRPCError(RPC_INVALID_REQUEST, "jsonrpc field must be a string");
221 }
222 // The "jsonrpc" key was added in the 2.0 spec, but some older documentation
223 // incorrectly included {"jsonrpc":"1.0"} in a request object, so we
224 // maintain that for backwards compatibility.
225 if (jsonrpc_version.get_str() == "1.0") {
227 } else if (jsonrpc_version.get_str() == "2.0") {
229 } else {
230 throw JSONRPCError(RPC_INVALID_REQUEST, "JSON-RPC version not supported");
231 }
232 }
233
234 // Parse method
235 const UniValue& valMethod{request.find_value("method")};
236 if (valMethod.isNull())
237 throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
238 if (!valMethod.isStr())
239 throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
240 strMethod = valMethod.get_str();
241 const std::string log_id{id && !id->isNull() ? SanitizeString(id->getValStr()) : ""};
242 if (fLogIPs)
243 LogDebug(BCLog::RPC, "ThreadRPCServer method=%s user=%s peeraddr=%s id=%s", SanitizeString(strMethod),
244 this->authUser, this->peerAddr, log_id);
245 else
246 LogDebug(BCLog::RPC, "ThreadRPCServer method=%s user=%s id=%s", SanitizeString(strMethod), this->authUser,
247 log_id);
248
249 // Parse params
250 const UniValue& valParams{request.find_value("params")};
251 if (valParams.isArray() || valParams.isObject())
252 params = valParams;
253 else if (valParams.isNull())
255 else
256 throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
257}
ArgsManager gArgs
Definition: args.cpp:38
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:237
fs::path GetPathArg(std::string arg, const fs::path &default_value={}) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return path argument or default value.
Definition: args.cpp:292
UniValue params
Definition: request.h:59
std::string strMethod
Definition: request.h:58
JSONRPCVersion m_json_version
Definition: request.h:65
std::string peerAddr
Definition: request.h:63
void parse(const UniValue &valRequest)
Definition: request.cpp:201
std::optional< UniValue > id
Definition: request.h:57
std::string authUser
Definition: request.h:62
const std::string & get_str() const
bool isArray() const
Definition: univalue.h:87
const UniValue & find_value(std::string_view key) const
Definition: univalue.cpp:232
@ VOBJ
Definition: univalue.h:24
@ VARR
Definition: univalue.h:24
bool isNull() const
Definition: univalue.h:81
const UniValue & get_obj() const
size_t size() const
Definition: univalue.h:71
const std::vector< UniValue > & getValues() const
bool isStr() const
Definition: univalue.h:85
bool exists(const std::string &key) const
Definition: univalue.h:79
void pushKV(std::string key, UniValue val)
Definition: univalue.cpp:125
bool isObject() const
Definition: univalue.h:88
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:160
bool RenameOver(fs::path src, fs::path dest)
Rename src to dest.
Definition: fs_helpers.cpp:262
std::string PermsToSymbolicString(fs::perms p)
Convert fs::perms to symbolic string of the form 'rwxrwxrwx'.
Definition: fs_helpers.cpp:287
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
#define LogWarning(...)
Definition: log.h:126
#define LogInfo(...)
Definition: log.h:125
#define LogDebug(category,...)
Definition: log.h:143
bool fLogIPs
Definition: logging.cpp:47
@ RPC
Definition: categories.h:23
void GetRandBytes(std::span< unsigned char > bytes) noexcept
Generate random data via the internal PRNG.
Definition: random.cpp:601
static fs::path GetAuthCookieFile(bool temp=false)
Get name of RPC authentication cookie file.
Definition: request.cpp:91
std::vector< UniValue > JSONRPCProcessBatchReply(const UniValue &in)
Parse JSON-RPC batch reply into a vector.
Definition: request.cpp:181
UniValue JSONRPCRequestObj(const std::string &strMethod, const UniValue &params, const UniValue &id)
JSON-RPC protocol.
Definition: request.cpp:46
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:75
static const char *const COOKIEAUTH_FILE
Default name for auth cookie file.
Definition: request.cpp:88
AuthCookieResult GetAuthCookie(std::string &cookie_out)
Read the RPC authentication cookie from disk.
Definition: request.cpp:153
AuthCookieResult 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:105
UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional< UniValue > id, JSONRPCVersion jsonrpc_version)
Definition: request.cpp:56
void DeleteAuthCookie()
Delete RPC authentication cookie from disk.
Definition: request.cpp:169
static const std::string COOKIEAUTH_USER
Username used when cookie authentication is in use (arbitrary, only for recognizability in debugging/...
Definition: request.cpp:86
static bool g_generated_cookie
Definition: request.cpp:103
JSONRPCVersion
Definition: request.h:18
AuthCookieResult
Definition: request.h:28
@ RPC_INVALID_REQUEST
Standard JSON-RPC 2.0 errors.
Definition: protocol.h:54
const UniValue NullUniValue
Definition: univalue.cpp:15
std::string SanitizeString(std::string_view str, int rule)
Remove unsafe chars.