]> Git Repo - VerusCoin.git/blob - src/httprpc.cpp
evhttpd implementation
[VerusCoin.git] / src / httprpc.cpp
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 class HTTPRPCTimer : public RPCTimerBase
20 {
21 public:
22     HTTPRPCTimer(struct event_base* eventBase, boost::function<void(void)>& func, int64_t seconds) : ev(eventBase, false, new Handler(func))
23     {
24         struct timeval tv = {seconds, 0};
25         ev.trigger(&tv);
26     }
27 private:
28     HTTPEvent ev;
29
30     class Handler : public HTTPClosure
31     {
32     public:
33         Handler(const boost::function<void(void)>& func) : func(func)
34         {
35         }
36     private:
37         boost::function<void(void)> func;
38         void operator()() { func(); }
39     };
40 };
41
42 class HTTPRPCTimerInterface : public RPCTimerInterface
43 {
44 public:
45     HTTPRPCTimerInterface(struct event_base* base) : base(base)
46     {
47     }
48     const char* Name()
49     {
50         return "HTTP";
51     }
52     RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t seconds)
53     {
54         return new HTTPRPCTimer(base, func, seconds);
55     }
56 private:
57     struct event_base* base;
58 };
59
60
61 /* Pre-base64-encoded authentication token */
62 static std::string strRPCUserColonPass;
63 /* Stored RPC timer interface (for unregistration) */
64 static HTTPRPCTimerInterface* httpRPCTimerInterface = 0;
65
66 static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
67 {
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();
71
72     if (code == RPC_INVALID_REQUEST)
73         nStatus = HTTP_BAD_REQUEST;
74     else if (code == RPC_METHOD_NOT_FOUND)
75         nStatus = HTTP_NOT_FOUND;
76
77     std::string strReply = JSONRPCReply(NullUniValue, objError, id);
78
79     req->WriteHeader("Content-Type", "application/json");
80     req->WriteReply(nStatus, strReply);
81 }
82
83 static bool RPCAuthorized(const std::string& strAuth)
84 {
85     if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
86         return false;
87     if (strAuth.substr(0, 6) != "Basic ")
88         return false;
89     std::string strUserPass64 = strAuth.substr(6);
90     boost::trim(strUserPass64);
91     std::string strUserPass = DecodeBase64(strUserPass64);
92     return TimingResistantEqual(strUserPass, strRPCUserColonPass);
93 }
94
95 static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
96 {
97     // JSONRPC handles only POST
98     if (req->GetRequestMethod() != HTTPRequest::POST) {
99         req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
100         return false;
101     }
102     // Check authorization
103     std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
104     if (!authHeader.first) {
105         req->WriteReply(HTTP_UNAUTHORIZED);
106         return false;
107     }
108
109     if (!RPCAuthorized(authHeader.second)) {
110         LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());
111
112         /* Deter brute-forcing
113            If this results in a DoS the user really
114            shouldn't have their RPC port exposed. */
115         MilliSleep(250);
116
117         req->WriteReply(HTTP_UNAUTHORIZED);
118         return false;
119     }
120
121     JSONRequest jreq;
122     try {
123         // Parse request
124         UniValue valRequest;
125         if (!valRequest.read(req->ReadBody()))
126             throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
127
128         std::string strReply;
129         // singleton request
130         if (valRequest.isObject()) {
131             jreq.parse(valRequest);
132
133             UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);
134
135             // Send reply
136             strReply = JSONRPCReply(result, NullUniValue, jreq.id);
137
138         // array of requests
139         } else if (valRequest.isArray())
140             strReply = JSONRPCExecBatch(valRequest.get_array());
141         else
142             throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
143
144         req->WriteHeader("Content-Type", "application/json");
145         req->WriteReply(HTTP_OK, strReply);
146     } catch (const UniValue& objError) {
147         JSONErrorReply(req, objError, jreq.id);
148         return false;
149     } catch (const std::exception& e) {
150         JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
151         return false;
152     }
153     return true;
154 }
155
156 static bool InitRPCAuthentication()
157 {
158     if (mapArgs["-rpcpassword"] == "")
159     {
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);
165             return false;
166         }
167     } else {
168         strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
169     }
170     return true;
171 }
172
173 bool StartHTTPRPC()
174 {
175     LogPrint("rpc", "Starting HTTP RPC server\n");
176     if (!InitRPCAuthentication())
177         return false;
178
179     RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
180
181     assert(EventBase());
182     httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
183     RPCRegisterTimerInterface(httpRPCTimerInterface);
184     return true;
185 }
186
187 void InterruptHTTPRPC()
188 {
189     LogPrint("rpc", "Interrupting HTTP RPC server\n");
190 }
191
192 void StopHTTPRPC()
193 {
194     LogPrint("rpc", "Stopping HTTP RPC server\n");
195     UnregisterHTTPHandler("/", true);
196     if (httpRPCTimerInterface) {
197         RPCUnregisterTimerInterface(httpRPCTimerInterface);
198         delete httpRPCTimerInterface;
199         httpRPCTimerInterface = 0;
200     }
201 }
This page took 0.038545 seconds and 4 git commands to generate.