Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
request.cpp
Go to the documentation of this file.
1// Copyright (c) 2010 Satoshi Nakamoto
2// Copyright (c) 2009-2022 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
41UniValue 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
51UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional<UniValue> id, JSONRPCVersion jsonrpc_version)
52{
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
70UniValue JSONRPCError(int code, const std::string& message)
71{
73 error.pushKV("code", code);
74 error.pushKV("message", message);
75 return error;
76}
77
81static const std::string COOKIEAUTH_USER = "__cookie__";
83static const char* const COOKIEAUTH_FILE = ".cookie";
84
86static fs::path GetAuthCookieFile(bool temp=false)
87{
88 fs::path arg = gArgs.GetPathArg("-rpccookiefile", COOKIEAUTH_FILE);
89 if (temp) {
90 arg += ".tmp";
91 }
92 return AbsPathForConfigVal(gArgs, arg);
93}
94
95static bool g_generated_cookie = false;
96
97bool GenerateAuthCookie(std::string* cookie_out, std::optional<fs::perms> cookie_perms)
98{
99 const size_t COOKIE_SIZE = 32;
100 unsigned char rand_pwd[COOKIE_SIZE];
101 GetRandBytes(rand_pwd);
102 std::string cookie = COOKIEAUTH_USER + ":" + HexStr(rand_pwd);
103
107 std::ofstream file;
108 fs::path filepath_tmp = GetAuthCookieFile(true);
109 file.open(filepath_tmp);
110 if (!file.is_open()) {
111 LogInfo("Unable to open cookie authentication file %s for writing\n", fs::PathToString(filepath_tmp));
112 return false;
113 }
114 file << cookie;
115 file.close();
116
117 fs::path filepath = GetAuthCookieFile(false);
118 if (!RenameOver(filepath_tmp, filepath)) {
119 LogInfo("Unable to rename cookie authentication file %s to %s\n", fs::PathToString(filepath_tmp), fs::PathToString(filepath));
120 return false;
121 }
122 if (cookie_perms) {
123 std::error_code code;
124 fs::permissions(filepath, cookie_perms.value(), fs::perm_options::replace, code);
125 if (code) {
126 LogInfo("Unable to set permissions on cookie authentication file %s\n", fs::PathToString(filepath_tmp));
127 return false;
128 }
129 }
130
131 g_generated_cookie = true;
132 LogInfo("Generated RPC authentication cookie %s\n", fs::PathToString(filepath));
133 LogInfo("Permissions used for cookie: %s\n", PermsToSymbolicString(fs::status(filepath).permissions()));
134
135 if (cookie_out)
136 *cookie_out = cookie;
137 return true;
138}
139
140bool GetAuthCookie(std::string *cookie_out)
141{
142 std::ifstream file;
143 std::string cookie;
144 fs::path filepath = GetAuthCookieFile();
145 file.open(filepath);
146 if (!file.is_open())
147 return false;
148 std::getline(file, cookie);
149 file.close();
150
151 if (cookie_out)
152 *cookie_out = cookie;
153 return true;
154}
155
157{
158 try {
159 if (g_generated_cookie) {
160 // Delete the cookie file if it was generated by this process
161 fs::remove(GetAuthCookieFile());
162 }
163 } catch (const fs::filesystem_error& e) {
164 LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
165 }
166}
167
168std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in)
169{
170 if (!in.isArray()) {
171 throw std::runtime_error("Batch must be an array");
172 }
173 const size_t num {in.size()};
174 std::vector<UniValue> batch(num);
175 for (const UniValue& rec : in.getValues()) {
176 if (!rec.isObject()) {
177 throw std::runtime_error("Batch member must be an object");
178 }
179 size_t id = rec["id"].getInt<int>();
180 if (id >= num) {
181 throw std::runtime_error("Batch member id is larger than batch size");
182 }
183 batch[id] = rec;
184 }
185 return batch;
186}
187
188void JSONRPCRequest::parse(const UniValue& valRequest)
189{
190 // Parse request
191 if (!valRequest.isObject())
192 throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
193 const UniValue& request = valRequest.get_obj();
194
195 // Parse id now so errors from here on will have the id
196 if (request.exists("id")) {
197 id = request.find_value("id");
198 } else {
199 id = std::nullopt;
200 }
201
202 // Check for JSON-RPC 2.0 (default 1.1)
204 const UniValue& jsonrpc_version = request.find_value("jsonrpc");
205 if (!jsonrpc_version.isNull()) {
206 if (!jsonrpc_version.isStr()) {
207 throw JSONRPCError(RPC_INVALID_REQUEST, "jsonrpc field must be a string");
208 }
209 // The "jsonrpc" key was added in the 2.0 spec, but some older documentation
210 // incorrectly included {"jsonrpc":"1.0"} in a request object, so we
211 // maintain that for backwards compatibility.
212 if (jsonrpc_version.get_str() == "1.0") {
214 } else if (jsonrpc_version.get_str() == "2.0") {
216 } else {
217 throw JSONRPCError(RPC_INVALID_REQUEST, "JSON-RPC version not supported");
218 }
219 }
220
221 // Parse method
222 const UniValue& valMethod{request.find_value("method")};
223 if (valMethod.isNull())
224 throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
225 if (!valMethod.isStr())
226 throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
227 strMethod = valMethod.get_str();
228 if (fLogIPs)
229 LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s peeraddr=%s\n", SanitizeString(strMethod),
230 this->authUser, this->peerAddr);
231 else
232 LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s\n", SanitizeString(strMethod), this->authUser);
233
234 // Parse params
235 const UniValue& valParams{request.find_value("params")};
236 if (valParams.isArray() || valParams.isObject())
237 params = valParams;
238 else if (valParams.isNull())
240 else
241 throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
242}
ArgsManager gArgs
Definition args.cpp:41
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:214
fs::path GetPathArg(std::string arg, const fs::path &default_value={}) const
Return path argument or default value.
Definition args.cpp:271
UniValue params
Definition request.h:40
std::string strMethod
Definition request.h:39
JSONRPCVersion m_json_version
Definition request.h:46
std::string peerAddr
Definition request.h:44
void parse(const UniValue &valRequest)
Definition request.cpp:188
std::string authUser
Definition request.h:43
const std::string & get_str() const
bool isArray() const
Definition univalue.h:85
const UniValue & find_value(std::string_view key) const
Definition univalue.cpp:233
bool isNull() const
Definition univalue.h:79
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:83
bool exists(const std::string &key) const
Definition univalue.h:77
void pushKV(std::string key, UniValue val)
Definition univalue.cpp:126
bool isObject() const
Definition univalue.h:86
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition fs.h:33
bool RenameOver(fs::path src, fs::path dest)
Rename src to dest.
std::string PermsToSymbolicString(fs::perms p)
Convert fs::perms to symbolic string of the form 'rwxrwxrwx'.
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition hex_base.cpp:29
bool fLogIPs
Definition logging.cpp:44
#define LogPrint(category,...)
Definition logging.h:293
#define LogInfo(...)
Definition logging.h:269
#define LogPrintf(...)
Definition logging.h:274
@ RPC
Definition logging.h:49
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition fs.h:151
std::string get_filesystem_error_message(const fs::filesystem_error &e)
Definition fs.cpp:118
void GetRandBytes(Span< unsigned char > bytes) noexcept
Generate random data via the internal PRNG.
Definition random.cpp:675
static fs::path GetAuthCookieFile(bool temp=false)
Get name of RPC authentication cookie file.
Definition request.cpp:86
std::vector< UniValue > JSONRPCProcessBatchReply(const UniValue &in)
Parse JSON-RPC batch reply into a vector.
Definition request.cpp:168
bool GetAuthCookie(std::string *cookie_out)
Read the RPC authentication cookie from disk.
Definition request.cpp:140
UniValue JSONRPCRequestObj(const std::string &strMethod, const UniValue &params, const UniValue &id)
JSON-RPC protocol.
Definition request.cpp:41
UniValue JSONRPCError(int code, const std::string &message)
Definition request.cpp:70
static const char *const COOKIEAUTH_FILE
Default name for auth cookie file.
Definition request.cpp:83
UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional< UniValue > id, JSONRPCVersion jsonrpc_version)
Definition request.cpp:51
bool GenerateAuthCookie(std::string *cookie_out, std::optional< fs::perms > cookie_perms)
Generate a new RPC authentication cookie and write it to disk.
Definition request.cpp:97
void DeleteAuthCookie()
Delete RPC authentication cookie from disk.
Definition request.cpp:156
static const std::string COOKIEAUTH_USER
Username used when cookie authentication is in use (arbitrary, only for recognizability in debugging/...
Definition request.cpp:81
static bool g_generated_cookie
Definition request.cpp:95
UniValue JSONRPCError(int code, const std::string &message)
Definition request.cpp:70
JSONRPCVersion
Definition request.h:16
@ RPC_INVALID_REQUEST
Standard JSON-RPC 2.0 errors.
Definition protocol.h:29
const UniValue NullUniValue
Definition univalue.cpp:16
std::string SanitizeString(std::string_view str, int rule)
Remove unsafe chars.