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