]> Git Repo - VerusCoin.git/blame - src/httprpc.cpp
Cleanup and complete basic currency launch changes
[VerusCoin.git] / src / httprpc.cpp
CommitLineData
afd64f76
WL
1#include "httprpc.h"
2
afd64f76
WL
3#include "chainparams.h"
4#include "httpserver.h"
3d31e09c 5#include "key_io.h"
4519a766
DC
6#include "rpc/protocol.h"
7#include "rpc/server.h"
afd64f76
WL
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
b5dc10bc 16// WWW-Authenticate to present with 401 Unauthorized response
17static const char *WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
18
afd64f76 19/** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
c938fb1f 20 * re-lock the wallet.
afd64f76
WL
21 */
22class HTTPRPCTimer : public RPCTimerBase
23{
24public:
858afa1a
WL
25 HTTPRPCTimer(struct event_base* eventBase, boost::function<void(void)>& func, int64_t millis) :
26 ev(eventBase, false, func)
afd64f76 27 {
858afa1a
WL
28 struct timeval tv;
29 tv.tv_sec = millis/1000;
30 tv.tv_usec = (millis%1000)*1000;
afd64f76
WL
31 ev.trigger(&tv);
32 }
33private:
34 HTTPEvent ev;
afd64f76
WL
35};
36
37class HTTPRPCTimerInterface : public RPCTimerInterface
38{
39public:
40 HTTPRPCTimerInterface(struct event_base* base) : base(base)
41 {
42 }
43 const char* Name()
44 {
45 return "HTTP";
46 }
858afa1a 47 RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis)
afd64f76 48 {
858afa1a 49 return new HTTPRPCTimer(base, func, millis);
afd64f76
WL
50 }
51private:
52 struct event_base* base;
53};
54
55
56/* Pre-base64-encoded authentication token */
57static std::string strRPCUserColonPass;
58/* Stored RPC timer interface (for unregistration) */
59static HTTPRPCTimerInterface* httpRPCTimerInterface = 0;
60
61static 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
78static 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
90static 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) {
b5dc10bc 100 req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
afd64f76
WL
101 req->WriteReply(HTTP_UNAUTHORIZED);
102 return false;
103 }
104
578a9891 105 if (!RPCAuthorized(authHeader.second)) {
afd64f76
WL
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
b5dc10bc 113 req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
afd64f76
WL
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);
59d405e8 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 }
afd64f76 138
b90dccec 139 extern bool printoutAPI;
140 if (printoutAPI == true)
141 {
375195f3 142 printf("%s %s\n", jreq.strMethod.c_str(), jreq.params.write().c_str());
b90dccec 143 }
144
afd64f76
WL
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
168static 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
185bool 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
199void InterruptHTTPRPC()
200{
201 LogPrint("rpc", "Interrupting HTTP RPC server\n");
202}
203
204void 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.156547 seconds and 4 git commands to generate.