Merge #10502: scripted-diff: Remove BOOST_FOREACH, Q_FOREACH and PAIRTYPE
[bitcoinplatinum.git] / src / httprpc.cpp
blob053702f843e3151f6fc734d39bfab71cd08162ed
1 // Copyright (c) 2015-2016 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 #include "httprpc.h"
7 #include "base58.h"
8 #include "chainparams.h"
9 #include "httpserver.h"
10 #include "rpc/protocol.h"
11 #include "rpc/server.h"
12 #include "random.h"
13 #include "sync.h"
14 #include "util.h"
15 #include "utilstrencodings.h"
16 #include "ui_interface.h"
17 #include "crypto/hmac_sha256.h"
18 #include <stdio.h>
20 #include <boost/algorithm/string.hpp> // boost::trim
21 #include <boost/foreach.hpp>
23 /** WWW-Authenticate to present with 401 Unauthorized response */
24 static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
26 /** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
27 * re-lock the wallet.
29 class HTTPRPCTimer : public RPCTimerBase
31 public:
32 HTTPRPCTimer(struct event_base* eventBase, std::function<void(void)>& func, int64_t millis) :
33 ev(eventBase, false, func)
35 struct timeval tv;
36 tv.tv_sec = millis/1000;
37 tv.tv_usec = (millis%1000)*1000;
38 ev.trigger(&tv);
40 private:
41 HTTPEvent ev;
44 class HTTPRPCTimerInterface : public RPCTimerInterface
46 public:
47 HTTPRPCTimerInterface(struct event_base* _base) : base(_base)
50 const char* Name()
52 return "HTTP";
54 RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis)
56 return new HTTPRPCTimer(base, func, millis);
58 private:
59 struct event_base* base;
63 /* Pre-base64-encoded authentication token */
64 static std::string strRPCUserColonPass;
65 /* Stored RPC timer interface (for unregistration) */
66 static HTTPRPCTimerInterface* httpRPCTimerInterface = 0;
68 static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
70 // Send error reply from json-rpc error object
71 int nStatus = HTTP_INTERNAL_SERVER_ERROR;
72 int code = find_value(objError, "code").get_int();
74 if (code == RPC_INVALID_REQUEST)
75 nStatus = HTTP_BAD_REQUEST;
76 else if (code == RPC_METHOD_NOT_FOUND)
77 nStatus = HTTP_NOT_FOUND;
79 std::string strReply = JSONRPCReply(NullUniValue, objError, id);
81 req->WriteHeader("Content-Type", "application/json");
82 req->WriteReply(nStatus, strReply);
85 //This function checks username and password against -rpcauth
86 //entries from config file.
87 static bool multiUserAuthorized(std::string strUserPass)
89 if (strUserPass.find(":") == std::string::npos) {
90 return false;
92 std::string strUser = strUserPass.substr(0, strUserPass.find(":"));
93 std::string strPass = strUserPass.substr(strUserPass.find(":") + 1);
95 if (gArgs.IsArgSet("-rpcauth")) {
96 //Search for multi-user login/pass "rpcauth" from config
97 for (std::string strRPCAuth : gArgs.GetArgs("-rpcauth"))
99 std::vector<std::string> vFields;
100 boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
101 if (vFields.size() != 3) {
102 //Incorrect formatting in config file
103 continue;
106 std::string strName = vFields[0];
107 if (!TimingResistantEqual(strName, strUser)) {
108 continue;
111 std::string strSalt = vFields[1];
112 std::string strHash = vFields[2];
114 static const unsigned int KEY_SIZE = 32;
115 unsigned char out[KEY_SIZE];
117 CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out);
118 std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
119 std::string strHashFromPass = HexStr(hexvec);
121 if (TimingResistantEqual(strHashFromPass, strHash)) {
122 return true;
126 return false;
129 static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut)
131 if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
132 return false;
133 if (strAuth.substr(0, 6) != "Basic ")
134 return false;
135 std::string strUserPass64 = strAuth.substr(6);
136 boost::trim(strUserPass64);
137 std::string strUserPass = DecodeBase64(strUserPass64);
139 if (strUserPass.find(":") != std::string::npos)
140 strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(":"));
142 //Check if authorized under single-user field
143 if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
144 return true;
146 return multiUserAuthorized(strUserPass);
149 static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
151 // JSONRPC handles only POST
152 if (req->GetRequestMethod() != HTTPRequest::POST) {
153 req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
154 return false;
156 // Check authorization
157 std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
158 if (!authHeader.first) {
159 req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
160 req->WriteReply(HTTP_UNAUTHORIZED);
161 return false;
164 JSONRPCRequest jreq;
165 if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
166 LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());
168 /* Deter brute-forcing
169 If this results in a DoS the user really
170 shouldn't have their RPC port exposed. */
171 MilliSleep(250);
173 req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
174 req->WriteReply(HTTP_UNAUTHORIZED);
175 return false;
178 try {
179 // Parse request
180 UniValue valRequest;
181 if (!valRequest.read(req->ReadBody()))
182 throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
184 // Set the URI
185 jreq.URI = req->GetURI();
187 std::string strReply;
188 // singleton request
189 if (valRequest.isObject()) {
190 jreq.parse(valRequest);
192 UniValue result = tableRPC.execute(jreq);
194 // Send reply
195 strReply = JSONRPCReply(result, NullUniValue, jreq.id);
197 // array of requests
198 } else if (valRequest.isArray())
199 strReply = JSONRPCExecBatch(valRequest.get_array());
200 else
201 throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
203 req->WriteHeader("Content-Type", "application/json");
204 req->WriteReply(HTTP_OK, strReply);
205 } catch (const UniValue& objError) {
206 JSONErrorReply(req, objError, jreq.id);
207 return false;
208 } catch (const std::exception& e) {
209 JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
210 return false;
212 return true;
215 static bool InitRPCAuthentication()
217 if (GetArg("-rpcpassword", "") == "")
219 LogPrintf("No rpcpassword set - using random cookie authentication\n");
220 if (!GenerateAuthCookie(&strRPCUserColonPass)) {
221 uiInterface.ThreadSafeMessageBox(
222 _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
223 "", CClientUIInterface::MSG_ERROR);
224 return false;
226 } else {
227 LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcuser for rpcauth auth generation.\n");
228 strRPCUserColonPass = GetArg("-rpcuser", "") + ":" + GetArg("-rpcpassword", "");
230 return true;
233 bool StartHTTPRPC()
235 LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
236 if (!InitRPCAuthentication())
237 return false;
239 RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
241 assert(EventBase());
242 httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
243 RPCSetTimerInterface(httpRPCTimerInterface);
244 return true;
247 void InterruptHTTPRPC()
249 LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
252 void StopHTTPRPC()
254 LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
255 UnregisterHTTPHandler("/", true);
256 if (httpRPCTimerInterface) {
257 RPCUnsetTimerInterface(httpRPCTimerInterface);
258 delete httpRPCTimerInterface;
259 httpRPCTimerInterface = 0;