Line data Source code
1 : #include "httprpc.h"
2 :
3 : #include "base58.h"
4 : #include "chainparams.h"
5 : #include "httpserver.h"
6 : #include "rpcprotocol.h"
7 : #include "rpcserver.h"
8 : #include "random.h"
9 : #include "sync.h"
10 : #include "util.h"
11 : #include "utilstrencodings.h"
12 : #include "ui_interface.h"
13 :
14 : #include <boost/algorithm/string.hpp> // boost::trim
15 :
16 : /** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
17 : * re-lock the wellet.
18 : */
19 2 : class HTTPRPCTimer : public RPCTimerBase
20 : {
21 : public:
22 1 : HTTPRPCTimer(struct event_base* eventBase, boost::function<void(void)>& func, int64_t millis) :
23 2 : ev(eventBase, false, func)
24 : {
25 : struct timeval tv;
26 1 : tv.tv_sec = millis/1000;
27 1 : tv.tv_usec = (millis%1000)*1000;
28 1 : ev.trigger(&tv);
29 1 : }
30 : private:
31 : HTTPEvent ev;
32 : };
33 :
34 188 : class HTTPRPCTimerInterface : public RPCTimerInterface
35 : {
36 : public:
37 188 : HTTPRPCTimerInterface(struct event_base* base) : base(base)
38 : {
39 0 : }
40 1 : const char* Name()
41 : {
42 1 : return "HTTP";
43 : }
44 1 : RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis)
45 : {
46 1 : return new HTTPRPCTimer(base, func, millis);
47 : }
48 : private:
49 : struct event_base* base;
50 : };
51 :
52 :
53 : /* Pre-base64-encoded authentication token */
54 95 : static std::string strRPCUserColonPass;
55 : /* Stored RPC timer interface (for unregistration) */
56 : static HTTPRPCTimerInterface* httpRPCTimerInterface = 0;
57 :
58 12 : static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
59 : {
60 : // Send error reply from json-rpc error object
61 12 : int nStatus = HTTP_INTERNAL_SERVER_ERROR;
62 36 : int code = find_value(objError, "code").get_int();
63 :
64 12 : if (code == RPC_INVALID_REQUEST)
65 : nStatus = HTTP_BAD_REQUEST;
66 12 : else if (code == RPC_METHOD_NOT_FOUND)
67 0 : nStatus = HTTP_NOT_FOUND;
68 :
69 12 : std::string strReply = JSONRPCReply(NullUniValue, objError, id);
70 :
71 60 : req->WriteHeader("Content-Type", "application/json");
72 12 : req->WriteReply(nStatus, strReply);
73 12 : }
74 :
75 3519 : static bool RPCAuthorized(const std::string& strAuth)
76 : {
77 3519 : if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
78 : return false;
79 10557 : if (strAuth.substr(0, 6) != "Basic ")
80 : return false;
81 3519 : std::string strUserPass64 = strAuth.substr(6);
82 3519 : boost::trim(strUserPass64);
83 3519 : std::string strUserPass = DecodeBase64(strUserPass64);
84 3519 : return TimingResistantEqual(strUserPass, strRPCUserColonPass);
85 : }
86 :
87 3519 : static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
88 : {
89 : // JSONRPC handles only POST
90 3519 : if (req->GetRequestMethod() != HTTPRequest::POST) {
91 0 : req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
92 0 : return false;
93 : }
94 : // Check authorization
95 10557 : std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
96 3519 : if (!authHeader.first) {
97 0 : req->WriteReply(HTTP_UNAUTHORIZED);
98 0 : return false;
99 : }
100 :
101 3519 : if (!RPCAuthorized(authHeader.second)) {
102 0 : LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());
103 :
104 : /* Deter brute-forcing
105 : If this results in a DoS the user really
106 : shouldn't have their RPC port exposed. */
107 0 : MilliSleep(250);
108 :
109 0 : req->WriteReply(HTTP_UNAUTHORIZED);
110 0 : return false;
111 : }
112 :
113 7038 : JSONRequest jreq;
114 : try {
115 : // Parse request
116 : UniValue valRequest;
117 10557 : if (!valRequest.read(req->ReadBody()))
118 0 : throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
119 :
120 : std::string strReply;
121 : // singleton request
122 3519 : if (valRequest.isObject()) {
123 3519 : jreq.parse(valRequest);
124 :
125 3519 : UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);
126 :
127 : // Send reply
128 7014 : strReply = JSONRPCReply(result, NullUniValue, jreq.id);
129 :
130 : // array of requests
131 0 : } else if (valRequest.isArray())
132 0 : strReply = JSONRPCExecBatch(valRequest.get_array());
133 : else
134 0 : throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
135 :
136 17547 : req->WriteHeader("Content-Type", "application/json");
137 7026 : req->WriteReply(HTTP_OK, strReply);
138 24 : } catch (const UniValue& objError) {
139 12 : JSONErrorReply(req, objError, jreq.id);
140 : return false;
141 0 : } catch (const std::exception& e) {
142 0 : JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
143 : return false;
144 : }
145 3507 : return true;
146 : }
147 :
148 94 : static bool InitRPCAuthentication()
149 : {
150 376 : if (mapArgs["-rpcpassword"] == "")
151 : {
152 0 : LogPrintf("No rpcpassword set - using random cookie authentication\n");
153 0 : if (!GenerateAuthCookie(&strRPCUserColonPass)) {
154 : uiInterface.ThreadSafeMessageBox(
155 : _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
156 0 : "", CClientUIInterface::MSG_ERROR);
157 0 : return false;
158 : }
159 : } else {
160 658 : strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
161 : }
162 : return true;
163 : }
164 :
165 94 : bool StartHTTPRPC()
166 : {
167 94 : LogPrint("rpc", "Starting HTTP RPC server\n");
168 94 : if (!InitRPCAuthentication())
169 : return false;
170 :
171 376 : RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
172 :
173 94 : assert(EventBase());
174 188 : httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
175 94 : RPCRegisterTimerInterface(httpRPCTimerInterface);
176 94 : return true;
177 : }
178 :
179 94 : void InterruptHTTPRPC()
180 : {
181 94 : LogPrint("rpc", "Interrupting HTTP RPC server\n");
182 94 : }
183 :
184 94 : void StopHTTPRPC()
185 : {
186 94 : LogPrint("rpc", "Stopping HTTP RPC server\n");
187 282 : UnregisterHTTPHandler("/", true);
188 94 : if (httpRPCTimerInterface) {
189 94 : RPCUnregisterTimerInterface(httpRPCTimerInterface);
190 94 : delete httpRPCTimerInterface;
191 94 : httpRPCTimerInterface = 0;
192 : }
193 379 : }
|