4 #include "chainparams.h"
5 #include "httpserver.h"
6 #include "rpcprotocol.h"
11 #include "utilstrencodings.h"
12 #include "ui_interface.h"
14 #include <boost/algorithm/string.hpp> // boost::trim
16 /** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
19 class HTTPRPCTimer : public RPCTimerBase
22 HTTPRPCTimer(struct event_base* eventBase, boost::function<void(void)>& func, int64_t seconds) : ev(eventBase, false, new Handler(func))
24 struct timeval tv = {seconds, 0};
30 class Handler : public HTTPClosure
33 Handler(const boost::function<void(void)>& func) : func(func)
37 boost::function<void(void)> func;
38 void operator()() { func(); }
42 class HTTPRPCTimerInterface : public RPCTimerInterface
45 HTTPRPCTimerInterface(struct event_base* base) : base(base)
52 RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t seconds)
54 return new HTTPRPCTimer(base, func, seconds);
57 struct event_base* base;
61 /* Pre-base64-encoded authentication token */
62 static std::string strRPCUserColonPass;
63 /* Stored RPC timer interface (for unregistration) */
64 static HTTPRPCTimerInterface* httpRPCTimerInterface = 0;
66 static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
68 // Send error reply from json-rpc error object
69 int nStatus = HTTP_INTERNAL_SERVER_ERROR;
70 int code = find_value(objError, "code").get_int();
72 if (code == RPC_INVALID_REQUEST)
73 nStatus = HTTP_BAD_REQUEST;
74 else if (code == RPC_METHOD_NOT_FOUND)
75 nStatus = HTTP_NOT_FOUND;
77 std::string strReply = JSONRPCReply(NullUniValue, objError, id);
79 req->WriteHeader("Content-Type", "application/json");
80 req->WriteReply(nStatus, strReply);
83 static bool RPCAuthorized(const std::string& strAuth)
85 if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
87 if (strAuth.substr(0, 6) != "Basic ")
89 std::string strUserPass64 = strAuth.substr(6);
90 boost::trim(strUserPass64);
91 std::string strUserPass = DecodeBase64(strUserPass64);
92 return TimingResistantEqual(strUserPass, strRPCUserColonPass);
95 static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
97 // JSONRPC handles only POST
98 if (req->GetRequestMethod() != HTTPRequest::POST) {
99 req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
102 // Check authorization
103 std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
104 if (!authHeader.first) {
105 req->WriteReply(HTTP_UNAUTHORIZED);
109 if (!RPCAuthorized(authHeader.second)) {
110 LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());
112 /* Deter brute-forcing
113 If this results in a DoS the user really
114 shouldn't have their RPC port exposed. */
117 req->WriteReply(HTTP_UNAUTHORIZED);
125 if (!valRequest.read(req->ReadBody()))
126 throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
128 std::string strReply;
130 if (valRequest.isObject()) {
131 jreq.parse(valRequest);
133 UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);
136 strReply = JSONRPCReply(result, NullUniValue, jreq.id);
139 } else if (valRequest.isArray())
140 strReply = JSONRPCExecBatch(valRequest.get_array());
142 throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
144 req->WriteHeader("Content-Type", "application/json");
145 req->WriteReply(HTTP_OK, strReply);
146 } catch (const UniValue& objError) {
147 JSONErrorReply(req, objError, jreq.id);
149 } catch (const std::exception& e) {
150 JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
156 static bool InitRPCAuthentication()
158 if (mapArgs["-rpcpassword"] == "")
160 LogPrintf("No rpcpassword set - using random cookie authentication\n");
161 if (!GenerateAuthCookie(&strRPCUserColonPass)) {
162 uiInterface.ThreadSafeMessageBox(
163 _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
164 "", CClientUIInterface::MSG_ERROR);
168 strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
175 LogPrint("rpc", "Starting HTTP RPC server\n");
176 if (!InitRPCAuthentication())
179 RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
182 httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
183 RPCRegisterTimerInterface(httpRPCTimerInterface);
187 void InterruptHTTPRPC()
189 LogPrint("rpc", "Interrupting HTTP RPC server\n");
194 LogPrint("rpc", "Stopping HTTP RPC server\n");
195 UnregisterHTTPHandler("/", true);
196 if (httpRPCTimerInterface) {
197 RPCUnregisterTimerInterface(httpRPCTimerInterface);
198 delete httpRPCTimerInterface;
199 httpRPCTimerInterface = 0;