]>
Commit | Line | Data |
---|---|---|
69d605f4 | 1 | // Copyright (c) 2010 Satoshi Nakamoto |
57702541 | 2 | // Copyright (c) 2009-2014 The Bitcoin developers |
69d605f4 | 3 | // Distributed under the MIT/X11 software license, see the accompanying |
3a25a2b9 | 4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
69d605f4 | 5 | |
fb78cc23 | 6 | #include "rpcserver.h" |
51ed9ec9 BD |
7 | |
8 | #include "base58.h" | |
0eeb4f5d | 9 | #include "init.h" |
51ed9ec9 | 10 | #include "main.h" |
48ba56cd | 11 | #include "ui_interface.h" |
c037531d | 12 | #include "util.h" |
48ba56cd | 13 | #ifdef ENABLE_WALLET |
51ed9ec9 | 14 | #include "wallet.h" |
48ba56cd | 15 | #endif |
51ed9ec9 | 16 | |
92f2c1fe | 17 | #include <boost/algorithm/string.hpp> |
69d605f4 | 18 | #include <boost/asio.hpp> |
92f2c1fe | 19 | #include <boost/asio/ssl.hpp> |
914dc012 | 20 | #include <boost/bind.hpp> |
95d888a6 | 21 | #include <boost/filesystem.hpp> |
a0780ba0 | 22 | #include <boost/foreach.hpp> |
69d605f4 WL |
23 | #include <boost/iostreams/concepts.hpp> |
24 | #include <boost/iostreams/stream.hpp> | |
914dc012 | 25 | #include <boost/shared_ptr.hpp> |
ad49c256 | 26 | #include <boost/thread.hpp> |
51ed9ec9 | 27 | #include "json/json_spirit_writer_template.h" |
5ce4c2a2 | 28 | |
69d605f4 WL |
29 | using namespace boost; |
30 | using namespace boost::asio; | |
31 | using namespace json_spirit; | |
40a158e1 | 32 | using namespace std; |
69d605f4 | 33 | |
f81ce5bd GA |
34 | static std::string strRPCUserColonPass; |
35 | ||
ff6a7af1 | 36 | static bool fRPCRunning = false; |
21eb5ada GA |
37 | // These are created by StartRPCThreads, destroyed in StopRPCThreads |
38 | static asio::io_service* rpc_io_service = NULL; | |
92f2c1fe | 39 | static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers; |
21eb5ada GA |
40 | static ssl::context* rpc_ssl_context = NULL; |
41 | static boost::thread_group* rpc_worker_group = NULL; | |
a8db31c8 | 42 | static boost::asio::io_service::work *rpc_dummy_work = NULL; |
ee219125 | 43 | static std::vector<CSubNet> rpc_allow_subnets; //!< List of subnets to allow RPC connections from |
cef44941 | 44 | static std::vector< boost::shared_ptr<ip::tcp::acceptor> > rpc_acceptors; |
e9205293 | 45 | |
899d373b | 46 | void RPCTypeCheck(const Array& params, |
cc6dfd1f GA |
47 | const list<Value_type>& typesExpected, |
48 | bool fAllowNull) | |
899d373b | 49 | { |
dab9fa7f | 50 | unsigned int i = 0; |
899d373b GA |
51 | BOOST_FOREACH(Value_type t, typesExpected) |
52 | { | |
53 | if (params.size() <= i) | |
54 | break; | |
55 | ||
cc6dfd1f GA |
56 | const Value& v = params[i]; |
57 | if (!((v.type() == t) || (fAllowNull && (v.type() == null_type)))) | |
899d373b GA |
58 | { |
59 | string err = strprintf("Expected type %s, got %s", | |
60 | Value_type_name[t], Value_type_name[v.type()]); | |
738835d7 | 61 | throw JSONRPCError(RPC_TYPE_ERROR, err); |
899d373b GA |
62 | } |
63 | i++; | |
64 | } | |
65 | } | |
66 | ||
67 | void RPCTypeCheck(const Object& o, | |
cc6dfd1f GA |
68 | const map<string, Value_type>& typesExpected, |
69 | bool fAllowNull) | |
899d373b GA |
70 | { |
71 | BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected) | |
72 | { | |
73 | const Value& v = find_value(o, t.first); | |
cc6dfd1f | 74 | if (!fAllowNull && v.type() == null_type) |
7d9d134b | 75 | throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); |
cc6dfd1f GA |
76 | |
77 | if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type)))) | |
899d373b GA |
78 | { |
79 | string err = strprintf("Expected type %s for %s, got %s", | |
7d9d134b | 80 | Value_type_name[t.second], t.first, Value_type_name[v.type()]); |
738835d7 | 81 | throw JSONRPCError(RPC_TYPE_ERROR, err); |
899d373b GA |
82 | } |
83 | } | |
84 | } | |
85 | ||
ad49c256 WL |
86 | static inline int64_t roundint64(double d) |
87 | { | |
88 | return (int64_t)(d > 0 ? d + 0.5 : d - 0.5); | |
89 | } | |
90 | ||
51ed9ec9 | 91 | int64_t AmountFromValue(const Value& value) |
69d605f4 WL |
92 | { |
93 | double dAmount = value.get_real(); | |
94 | if (dAmount <= 0.0 || dAmount > 21000000.0) | |
738835d7 | 95 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); |
51ed9ec9 | 96 | int64_t nAmount = roundint64(dAmount * COIN); |
69d605f4 | 97 | if (!MoneyRange(nAmount)) |
738835d7 | 98 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); |
69d605f4 WL |
99 | return nAmount; |
100 | } | |
101 | ||
51ed9ec9 | 102 | Value ValueFromAmount(int64_t amount) |
69d605f4 WL |
103 | { |
104 | return (double)amount / (double)COIN; | |
105 | } | |
106 | ||
463c9710 PT |
107 | uint256 ParseHashV(const Value& v, string strName) |
108 | { | |
109 | string strHex; | |
110 | if (v.type() == str_type) | |
111 | strHex = v.get_str(); | |
112 | if (!IsHex(strHex)) // Note: IsHex("") is false | |
113 | throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); | |
114 | uint256 result; | |
115 | result.SetHex(strHex); | |
116 | return result; | |
117 | } | |
118 | uint256 ParseHashO(const Object& o, string strKey) | |
119 | { | |
120 | return ParseHashV(find_value(o, strKey), strKey); | |
121 | } | |
122 | vector<unsigned char> ParseHexV(const Value& v, string strName) | |
123 | { | |
124 | string strHex; | |
125 | if (v.type() == str_type) | |
126 | strHex = v.get_str(); | |
127 | if (!IsHex(strHex)) | |
128 | throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); | |
129 | return ParseHex(strHex); | |
130 | } | |
131 | vector<unsigned char> ParseHexO(const Object& o, string strKey) | |
132 | { | |
133 | return ParseHexV(find_value(o, strKey), strKey); | |
134 | } | |
69d605f4 | 135 | |
74335bd3 | 136 | |
69d605f4 WL |
137 | /// |
138 | /// Note: This interface may still be subject to change. | |
139 | /// | |
140 | ||
9862229d | 141 | string CRPCTable::help(string strCommand) const |
69d605f4 | 142 | { |
69d605f4 | 143 | string strRet; |
6b5b7cbf | 144 | string category; |
69d605f4 | 145 | set<rpcfn_type> setDone; |
6b5b7cbf CL |
146 | vector<pair<string, const CRPCCommand*> > vCommands; |
147 | ||
9862229d | 148 | for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) |
6b5b7cbf CL |
149 | vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second)); |
150 | sort(vCommands.begin(), vCommands.end()); | |
151 | ||
152 | BOOST_FOREACH(const PAIRTYPE(string, const CRPCCommand*)& command, vCommands) | |
69d605f4 | 153 | { |
6b5b7cbf CL |
154 | const CRPCCommand *pcmd = command.second; |
155 | string strMethod = pcmd->name; | |
69d605f4 | 156 | // We already filter duplicates, but these deprecated screw up the sort order |
47f48a65 | 157 | if (strMethod.find("label") != string::npos) |
69d605f4 WL |
158 | continue; |
159 | if (strCommand != "" && strMethod != strCommand) | |
160 | continue; | |
48ba56cd | 161 | #ifdef ENABLE_WALLET |
b0730874 JG |
162 | if (pcmd->reqWallet && !pwalletMain) |
163 | continue; | |
48ba56cd | 164 | #endif |
b0730874 | 165 | |
69d605f4 WL |
166 | try |
167 | { | |
168 | Array params; | |
dc42bf52 | 169 | rpcfn_type pfn = pcmd->actor; |
69d605f4 WL |
170 | if (setDone.insert(pfn).second) |
171 | (*pfn)(params, true); | |
172 | } | |
173 | catch (std::exception& e) | |
174 | { | |
175 | // Help text is returned in an exception | |
176 | string strHelp = string(e.what()); | |
177 | if (strCommand == "") | |
6b5b7cbf | 178 | { |
ab9dc75a | 179 | if (strHelp.find('\n') != string::npos) |
69d605f4 | 180 | strHelp = strHelp.substr(0, strHelp.find('\n')); |
6b5b7cbf CL |
181 | |
182 | if (category != pcmd->category) | |
183 | { | |
184 | if (!category.empty()) | |
185 | strRet += "\n"; | |
186 | category = pcmd->category; | |
187 | string firstLetter = category.substr(0,1); | |
188 | boost::to_upper(firstLetter); | |
189 | strRet += "== " + firstLetter + category.substr(1) + " ==\n"; | |
190 | } | |
191 | } | |
69d605f4 WL |
192 | strRet += strHelp + "\n"; |
193 | } | |
194 | } | |
195 | if (strRet == "") | |
7d9d134b | 196 | strRet = strprintf("help: unknown command: %s\n", strCommand); |
69d605f4 WL |
197 | strRet = strRet.substr(0,strRet.size()-1); |
198 | return strRet; | |
199 | } | |
200 | ||
9862229d PW |
201 | Value help(const Array& params, bool fHelp) |
202 | { | |
203 | if (fHelp || params.size() > 1) | |
204 | throw runtime_error( | |
a6099ef3 | 205 | "help ( \"command\" )\n" |
206 | "\nList all commands, or get help for a specified command.\n" | |
207 | "\nArguments:\n" | |
208 | "1. \"command\" (string, optional) The command to get help on\n" | |
209 | "\nResult:\n" | |
210 | "\"text\" (string) The help text\n" | |
211 | ); | |
9862229d PW |
212 | |
213 | string strCommand; | |
214 | if (params.size() > 0) | |
215 | strCommand = params[0].get_str(); | |
216 | ||
217 | return tableRPC.help(strCommand); | |
218 | } | |
219 | ||
69d605f4 WL |
220 | |
221 | Value stop(const Array& params, bool fHelp) | |
222 | { | |
6e65420b | 223 | // Accept the deprecated and ignored 'detach' boolean argument |
3731f578 | 224 | if (fHelp || params.size() > 1) |
69d605f4 | 225 | throw runtime_error( |
92467073 | 226 | "stop\n" |
a6099ef3 | 227 | "\nStop Bitcoin server."); |
69d605f4 | 228 | // Shutdown will take long enough that the response should get back |
9247134e | 229 | StartShutdown(); |
ff0ee876 | 230 | return "Bitcoin server stopping"; |
69d605f4 WL |
231 | } |
232 | ||
233 | ||
69d605f4 WL |
234 | |
235 | // | |
236 | // Call Table | |
237 | // | |
238 | ||
dc42bf52 | 239 | |
e46704dd | 240 | static const CRPCCommand vRPCCommands[] = |
6b5b7cbf CL |
241 | { // category name actor (function) okSafeMode threadSafe reqWallet |
242 | // --------------------- ------------------------ ----------------------- ---------- ---------- --------- | |
ab88ed93 | 243 | /* Overall control/query calls */ |
6b5b7cbf CL |
244 | { "control", "getinfo", &getinfo, true, false, false }, /* uses wallet if enabled */ |
245 | { "control", "help", &help, true, true, false }, | |
246 | { "control", "stop", &stop, true, true, false }, | |
ab88ed93 WL |
247 | |
248 | /* P2P networking */ | |
6b5b7cbf CL |
249 | { "network", "getnetworkinfo", &getnetworkinfo, true, false, false }, |
250 | { "network", "addnode", &addnode, true, true, false }, | |
251 | { "network", "getaddednodeinfo", &getaddednodeinfo, true, true, false }, | |
252 | { "network", "getconnectioncount", &getconnectioncount, true, false, false }, | |
253 | { "network", "getnettotals", &getnettotals, true, true, false }, | |
254 | { "network", "getpeerinfo", &getpeerinfo, true, false, false }, | |
255 | { "network", "ping", &ping, true, false, false }, | |
ab88ed93 WL |
256 | |
257 | /* Block chain and UTXO */ | |
6b5b7cbf CL |
258 | { "blockchain", "getblockchaininfo", &getblockchaininfo, true, false, false }, |
259 | { "blockchain", "getbestblockhash", &getbestblockhash, true, false, false }, | |
260 | { "blockchain", "getblockcount", &getblockcount, true, false, false }, | |
261 | { "blockchain", "getblock", &getblock, true, false, false }, | |
262 | { "blockchain", "getblockhash", &getblockhash, true, false, false }, | |
263 | { "blockchain", "getchaintips", &getchaintips, true, false, false }, | |
264 | { "blockchain", "getdifficulty", &getdifficulty, true, false, false }, | |
6f2c26a4 | 265 | { "blockchain", "getmempoolinfo", &getmempoolinfo, true, true, false }, |
6b5b7cbf CL |
266 | { "blockchain", "getrawmempool", &getrawmempool, true, false, false }, |
267 | { "blockchain", "gettxout", &gettxout, true, false, false }, | |
268 | { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, false, false }, | |
269 | { "blockchain", "verifychain", &verifychain, true, false, false }, | |
48ba56cd | 270 | |
4a85e067 | 271 | /* Mining */ |
6b5b7cbf CL |
272 | { "mining", "getblocktemplate", &getblocktemplate, true, false, false }, |
273 | { "mining", "getmininginfo", &getmininginfo, true, false, false }, | |
274 | { "mining", "getnetworkhashps", &getnetworkhashps, true, false, false }, | |
275 | { "mining", "prioritisetransaction", &prioritisetransaction, true, false, false }, | |
276 | { "mining", "submitblock", &submitblock, true, true, false }, | |
277 | ||
278 | #ifdef ENABLE_WALLET | |
279 | /* Coin generation */ | |
280 | { "generating", "getgenerate", &getgenerate, true, false, false }, | |
281 | { "generating", "gethashespersec", &gethashespersec, true, false, false }, | |
282 | { "generating", "setgenerate", &setgenerate, true, true, false }, | |
283 | #endif | |
ab88ed93 WL |
284 | |
285 | /* Raw transactions */ | |
6b5b7cbf CL |
286 | { "rawtransactions", "createrawtransaction", &createrawtransaction, true, false, false }, |
287 | { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, false, false }, | |
288 | { "rawtransactions", "decodescript", &decodescript, true, false, false }, | |
289 | { "rawtransactions", "getrawtransaction", &getrawtransaction, true, false, false }, | |
290 | { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, false, false }, | |
291 | { "rawtransactions", "signrawtransaction", &signrawtransaction, false, false, false }, /* uses wallet if enabled */ | |
ab88ed93 WL |
292 | |
293 | /* Utility functions */ | |
6b5b7cbf CL |
294 | { "util", "createmultisig", &createmultisig, true, true , false }, |
295 | { "util", "validateaddress", &validateaddress, true, false, false }, /* uses wallet if enabled */ | |
296 | { "util", "verifymessage", &verifymessage, true, false, false }, | |
297 | { "util", "estimatefee", &estimatefee, true, true, false }, | |
298 | { "util", "estimatepriority", &estimatepriority, true, true, false }, | |
4a85e067 WL |
299 | |
300 | #ifdef ENABLE_WALLET | |
301 | /* Wallet */ | |
6b5b7cbf CL |
302 | { "wallet", "addmultisigaddress", &addmultisigaddress, true, false, true }, |
303 | { "wallet", "backupwallet", &backupwallet, true, false, true }, | |
304 | { "wallet", "dumpprivkey", &dumpprivkey, true, false, true }, | |
305 | { "wallet", "dumpwallet", &dumpwallet, true, false, true }, | |
306 | { "wallet", "encryptwallet", &encryptwallet, true, false, true }, | |
307 | { "wallet", "getaccountaddress", &getaccountaddress, true, false, true }, | |
308 | { "wallet", "getaccount", &getaccount, true, false, true }, | |
309 | { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true, false, true }, | |
310 | { "wallet", "getbalance", &getbalance, false, false, true }, | |
311 | { "wallet", "getnewaddress", &getnewaddress, true, false, true }, | |
312 | { "wallet", "getrawchangeaddress", &getrawchangeaddress, true, false, true }, | |
313 | { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, false, true }, | |
314 | { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, false, true }, | |
315 | { "wallet", "gettransaction", &gettransaction, false, false, true }, | |
316 | { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, false, true }, | |
317 | { "wallet", "getwalletinfo", &getwalletinfo, false, false, true }, | |
318 | { "wallet", "importprivkey", &importprivkey, true, false, true }, | |
319 | { "wallet", "importwallet", &importwallet, true, false, true }, | |
320 | { "wallet", "importaddress", &importaddress, true, false, true }, | |
321 | { "wallet", "keypoolrefill", &keypoolrefill, true, false, true }, | |
322 | { "wallet", "listaccounts", &listaccounts, false, false, true }, | |
323 | { "wallet", "listaddressgroupings", &listaddressgroupings, false, false, true }, | |
324 | { "wallet", "listlockunspent", &listlockunspent, false, false, true }, | |
325 | { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, false, true }, | |
326 | { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, false, true }, | |
327 | { "wallet", "listsinceblock", &listsinceblock, false, false, true }, | |
328 | { "wallet", "listtransactions", &listtransactions, false, false, true }, | |
329 | { "wallet", "listunspent", &listunspent, false, false, true }, | |
330 | { "wallet", "lockunspent", &lockunspent, true, false, true }, | |
331 | { "wallet", "move", &movecmd, false, false, true }, | |
332 | { "wallet", "sendfrom", &sendfrom, false, false, true }, | |
333 | { "wallet", "sendmany", &sendmany, false, false, true }, | |
334 | { "wallet", "sendtoaddress", &sendtoaddress, false, false, true }, | |
335 | { "wallet", "setaccount", &setaccount, true, false, true }, | |
336 | { "wallet", "settxfee", &settxfee, true, false, true }, | |
337 | { "wallet", "signmessage", &signmessage, true, false, true }, | |
338 | { "wallet", "walletlock", &walletlock, true, false, true }, | |
339 | { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, false, true }, | |
340 | { "wallet", "walletpassphrase", &walletpassphrase, true, false, true }, | |
48ba56cd | 341 | #endif // ENABLE_WALLET |
69d605f4 | 342 | }; |
69d605f4 | 343 | |
9862229d | 344 | CRPCTable::CRPCTable() |
dc42bf52 | 345 | { |
dc42bf52 JG |
346 | unsigned int vcidx; |
347 | for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) | |
348 | { | |
e46704dd | 349 | const CRPCCommand *pcmd; |
69d605f4 | 350 | |
dc42bf52 JG |
351 | pcmd = &vRPCCommands[vcidx]; |
352 | mapCommands[pcmd->name] = pcmd; | |
353 | } | |
354 | } | |
69d605f4 | 355 | |
9862229d PW |
356 | const CRPCCommand *CRPCTable::operator[](string name) const |
357 | { | |
358 | map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name); | |
359 | if (it == mapCommands.end()) | |
360 | return NULL; | |
361 | return (*it).second; | |
362 | } | |
69d605f4 | 363 | |
69d605f4 | 364 | |
69d605f4 WL |
365 | bool HTTPAuthorized(map<string, string>& mapHeaders) |
366 | { | |
367 | string strAuth = mapHeaders["authorization"]; | |
368 | if (strAuth.substr(0,6) != "Basic ") | |
369 | return false; | |
370 | string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); | |
371 | string strUserPass = DecodeBase64(strUserPass64); | |
42656ea2 | 372 | return TimingResistantEqual(strUserPass, strRPCUserColonPass); |
69d605f4 WL |
373 | } |
374 | ||
69d605f4 WL |
375 | void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) |
376 | { | |
377 | // Send error reply from json-rpc error object | |
285746d3 | 378 | int nStatus = HTTP_INTERNAL_SERVER_ERROR; |
69d605f4 | 379 | int code = find_value(objError, "code").get_int(); |
285746d3 WL |
380 | if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST; |
381 | else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND; | |
69d605f4 | 382 | string strReply = JSONRPCReply(Value::null, objError, id); |
96c52695 | 383 | stream << HTTPReply(nStatus, strReply, false) << std::flush; |
69d605f4 WL |
384 | } |
385 | ||
21bf3d25 | 386 | CNetAddr BoostAsioToCNetAddr(boost::asio::ip::address address) |
69d605f4 | 387 | { |
ee219125 | 388 | CNetAddr netaddr; |
43b6dafa GS |
389 | // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses |
390 | if (address.is_v6() | |
391 | && (address.to_v6().is_v4_compatible() | |
392 | || address.to_v6().is_v4_mapped())) | |
ee219125 WL |
393 | address = address.to_v6().to_v4(); |
394 | ||
395 | if(address.is_v4()) | |
396 | { | |
397 | boost::asio::ip::address_v4::bytes_type bytes = address.to_v4().to_bytes(); | |
398 | netaddr.SetRaw(NET_IPV4, &bytes[0]); | |
399 | } | |
400 | else | |
401 | { | |
402 | boost::asio::ip::address_v6::bytes_type bytes = address.to_v6().to_bytes(); | |
403 | netaddr.SetRaw(NET_IPV6, &bytes[0]); | |
404 | } | |
405 | return netaddr; | |
406 | } | |
407 | ||
408 | bool ClientAllowed(const boost::asio::ip::address& address) | |
409 | { | |
410 | CNetAddr netaddr = BoostAsioToCNetAddr(address); | |
411 | BOOST_FOREACH(const CSubNet &subnet, rpc_allow_subnets) | |
412 | if (subnet.Match(netaddr)) | |
69d605f4 WL |
413 | return true; |
414 | return false; | |
415 | } | |
416 | ||
a0780ba0 GS |
417 | template <typename Protocol> |
418 | class AcceptedConnectionImpl : public AcceptedConnection | |
419 | { | |
420 | public: | |
421 | AcceptedConnectionImpl( | |
422 | asio::io_service& io_service, | |
423 | ssl::context &context, | |
424 | bool fUseSSL) : | |
425 | sslStream(io_service, context), | |
426 | _d(sslStream, fUseSSL), | |
427 | _stream(_d) | |
428 | { | |
429 | } | |
430 | ||
431 | virtual std::iostream& stream() | |
432 | { | |
433 | return _stream; | |
434 | } | |
435 | ||
436 | virtual std::string peer_address_to_string() const | |
437 | { | |
438 | return peer.address().to_string(); | |
439 | } | |
e9205293 | 440 | |
a0780ba0 GS |
441 | virtual void close() |
442 | { | |
443 | _stream.close(); | |
444 | } | |
445 | ||
446 | typename Protocol::endpoint peer; | |
447 | asio::ssl::stream<typename Protocol::socket> sslStream; | |
448 | ||
449 | private: | |
450 | SSLIOStreamDevice<Protocol> _d; | |
451 | iostreams::stream< SSLIOStreamDevice<Protocol> > _stream; | |
e9205293 DJS |
452 | }; |
453 | ||
21eb5ada | 454 | void ServiceConnection(AcceptedConnection *conn); |
69d605f4 | 455 | |
914dc012 | 456 | // Forward declaration required for RPCListen |
a0780ba0 GS |
457 | template <typename Protocol, typename SocketAcceptorService> |
458 | static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor, | |
914dc012 GS |
459 | ssl::context& context, |
460 | bool fUseSSL, | |
1a445225 | 461 | boost::shared_ptr< AcceptedConnection > conn, |
914dc012 GS |
462 | const boost::system::error_code& error); |
463 | ||
464 | /** | |
465 | * Sets up I/O resources to accept and handle a new connection. | |
466 | */ | |
a0780ba0 GS |
467 | template <typename Protocol, typename SocketAcceptorService> |
468 | static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor, | |
914dc012 GS |
469 | ssl::context& context, |
470 | const bool fUseSSL) | |
471 | { | |
914dc012 | 472 | // Accept connection |
1a445225 | 473 | boost::shared_ptr< AcceptedConnectionImpl<Protocol> > conn(new AcceptedConnectionImpl<Protocol>(acceptor->get_io_service(), context, fUseSSL)); |
914dc012 GS |
474 | |
475 | acceptor->async_accept( | |
476 | conn->sslStream.lowest_layer(), | |
477 | conn->peer, | |
a0780ba0 | 478 | boost::bind(&RPCAcceptHandler<Protocol, SocketAcceptorService>, |
914dc012 GS |
479 | acceptor, |
480 | boost::ref(context), | |
481 | fUseSSL, | |
482 | conn, | |
0a0cd345 | 483 | _1)); |
914dc012 GS |
484 | } |
485 | ||
fb78cc23 | 486 | |
914dc012 GS |
487 | /** |
488 | * Accept and handle incoming connection. | |
489 | */ | |
a0780ba0 GS |
490 | template <typename Protocol, typename SocketAcceptorService> |
491 | static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor, | |
914dc012 GS |
492 | ssl::context& context, |
493 | const bool fUseSSL, | |
1a445225 | 494 | boost::shared_ptr< AcceptedConnection > conn, |
914dc012 GS |
495 | const boost::system::error_code& error) |
496 | { | |
814efd6f | 497 | // Immediately start accepting new connections, except when we're cancelled or our socket is closed. |
21eb5ada | 498 | if (error != asio::error::operation_aborted && acceptor->is_open()) |
ad25804f | 499 | RPCListen(acceptor, context, fUseSSL); |
914dc012 | 500 | |
1a445225 | 501 | AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn.get()); |
a0780ba0 | 502 | |
914dc012 GS |
503 | if (error) |
504 | { | |
0a0cd345 WL |
505 | // TODO: Actually handle errors |
506 | LogPrintf("%s: Error: %s\n", __func__, error.message()); | |
914dc012 | 507 | } |
914dc012 GS |
508 | // Restrict callers by IP. It is important to |
509 | // do this before starting client thread, to filter out | |
510 | // certain DoS and misbehaving clients. | |
21eb5ada | 511 | else if (tcp_conn && !ClientAllowed(tcp_conn->peer.address())) |
914dc012 GS |
512 | { |
513 | // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. | |
514 | if (!fUseSSL) | |
16f33f16 | 515 | conn->stream() << HTTPError(HTTP_FORBIDDEN, false) << std::flush; |
1a445225 | 516 | conn->close(); |
914dc012 | 517 | } |
21eb5ada | 518 | else { |
1a445225 | 519 | ServiceConnection(conn.get()); |
21eb5ada | 520 | conn->close(); |
914dc012 | 521 | } |
914dc012 GS |
522 | } |
523 | ||
deb3572a WL |
524 | static ip::tcp::endpoint ParseEndpoint(const std::string &strEndpoint, int defaultPort) |
525 | { | |
526 | std::string addr; | |
527 | int port = defaultPort; | |
528 | SplitHostPort(strEndpoint, port, addr); | |
529 | return ip::tcp::endpoint(asio::ip::address::from_string(addr), port); | |
530 | } | |
531 | ||
21eb5ada | 532 | void StartRPCThreads() |
69d605f4 | 533 | { |
ee219125 WL |
534 | rpc_allow_subnets.clear(); |
535 | rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet | |
536 | rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost | |
537 | if (mapMultiArgs.count("-rpcallowip")) | |
538 | { | |
539 | const vector<string>& vAllow = mapMultiArgs["-rpcallowip"]; | |
540 | BOOST_FOREACH(string strAllow, vAllow) | |
541 | { | |
542 | CSubNet subnet(strAllow); | |
543 | if(!subnet.IsValid()) | |
544 | { | |
545 | uiInterface.ThreadSafeMessageBox( | |
4278b1df | 546 | strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow), |
ee219125 WL |
547 | "", CClientUIInterface::MSG_ERROR); |
548 | StartShutdown(); | |
549 | return; | |
550 | } | |
551 | rpc_allow_subnets.push_back(subnet); | |
552 | } | |
553 | } | |
554 | std::string strAllowed; | |
555 | BOOST_FOREACH(const CSubNet &subnet, rpc_allow_subnets) | |
556 | strAllowed += subnet.ToString() + " "; | |
557 | LogPrint("rpc", "Allowing RPC connections from: %s\n", strAllowed); | |
558 | ||
f81ce5bd | 559 | strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; |
0e4b3175 MH |
560 | if (((mapArgs["-rpcpassword"] == "") || |
561 | (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) && Params().RequireRPCPassword()) | |
69d605f4 | 562 | { |
b04f301c | 563 | unsigned char rand_pwd[32]; |
001a53d7 | 564 | GetRandBytes(rand_pwd, 32); |
69d605f4 WL |
565 | string strWhatAmI = "To use bitcoind"; |
566 | if (mapArgs.count("-server")) | |
567 | strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); | |
568 | else if (mapArgs.count("-daemon")) | |
569 | strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); | |
ab1b288f | 570 | uiInterface.ThreadSafeMessageBox(strprintf( |
90bd933e PK |
571 | _("%s, you must set a rpcpassword in the configuration file:\n" |
572 | "%s\n" | |
5a60b66a WL |
573 | "It is recommended you use the following random password:\n" |
574 | "rpcuser=bitcoinrpc\n" | |
575 | "rpcpassword=%s\n" | |
576 | "(you do not need to remember this password)\n" | |
7e1610d5 | 577 | "The username and password MUST NOT be the same.\n" |
3d9d2d42 GA |
578 | "If the file does not exist, create it with owner-readable-only file permissions.\n" |
579 | "It is also recommended to set alertnotify so you are notified of problems;\n" | |
580 | "for example: alertnotify=echo %%s | mail -s \"Bitcoin Alert\" [email protected]\n"), | |
7d9d134b WL |
581 | strWhatAmI, |
582 | GetConfigFile().string(), | |
583 | EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32)), | |
5350ea41 | 584 | "", CClientUIInterface::MSG_ERROR); |
9247134e | 585 | StartShutdown(); |
69d605f4 WL |
586 | return; |
587 | } | |
588 | ||
21eb5ada GA |
589 | assert(rpc_io_service == NULL); |
590 | rpc_io_service = new asio::io_service(); | |
591 | rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23); | |
69d605f4 | 592 | |
3260b4c0 | 593 | const bool fUseSSL = GetBoolArg("-rpcssl", false); |
fbf9df2e | 594 | |
69d605f4 WL |
595 | if (fUseSSL) |
596 | { | |
21eb5ada | 597 | rpc_ssl_context->set_options(ssl::context::no_sslv2); |
93fb7489 PK |
598 | |
599 | filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); | |
600 | if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; | |
21eb5ada | 601 | if (filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); |
7d9d134b | 602 | else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string()); |
93fb7489 PK |
603 | |
604 | filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); | |
605 | if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; | |
21eb5ada | 606 | if (filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); |
7d9d134b | 607 | else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string()); |
93fb7489 | 608 | |
1728bf08 | 609 | string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); |
21eb5ada | 610 | SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str()); |
69d605f4 | 611 | } |
69d605f4 | 612 | |
deb3572a WL |
613 | std::vector<ip::tcp::endpoint> vEndpoints; |
614 | bool bBindAny = false; | |
84ce18ca | 615 | int defaultPort = GetArg("-rpcport", BaseParams().RPCPort()); |
deb3572a WL |
616 | if (!mapArgs.count("-rpcallowip")) // Default to loopback if not allowing external IPs |
617 | { | |
618 | vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v6::loopback(), defaultPort)); | |
619 | vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v4::loopback(), defaultPort)); | |
620 | if (mapArgs.count("-rpcbind")) | |
621 | { | |
622 | LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); | |
623 | } | |
624 | } else if (mapArgs.count("-rpcbind")) // Specific bind address | |
625 | { | |
626 | BOOST_FOREACH(const std::string &addr, mapMultiArgs["-rpcbind"]) | |
627 | { | |
628 | try { | |
629 | vEndpoints.push_back(ParseEndpoint(addr, defaultPort)); | |
630 | } | |
631 | catch(boost::system::system_error &e) | |
632 | { | |
633 | uiInterface.ThreadSafeMessageBox( | |
634 | strprintf(_("Could not parse -rpcbind value %s as network address"), addr), | |
635 | "", CClientUIInterface::MSG_ERROR); | |
636 | StartShutdown(); | |
637 | return; | |
638 | } | |
639 | } | |
640 | } else { // No specific bind address specified, bind to any | |
641 | vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v6::any(), defaultPort)); | |
642 | vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v4::any(), defaultPort)); | |
643 | // Prefer making the socket dual IPv6/IPv4 instead of binding | |
644 | // to both addresses seperately. | |
645 | bBindAny = true; | |
646 | } | |
7cf3d2cc | 647 | |
c1d79812 PW |
648 | bool fListening = false; |
649 | std::string strerr; | |
deb3572a | 650 | BOOST_FOREACH(const ip::tcp::endpoint &endpoint, vEndpoints) |
c1ecab81 | 651 | { |
deb3572a WL |
652 | asio::ip::address bindAddress = endpoint.address(); |
653 | LogPrintf("Binding RPC on address %s port %i (IPv4+IPv6 bind any: %i)\n", bindAddress.to_string(), endpoint.port(), bBindAny); | |
654 | boost::system::error_code v6_only_error; | |
cef44941 | 655 | boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service)); |
c1ecab81 | 656 | |
deb3572a | 657 | try { |
ad25804f GS |
658 | acceptor->open(endpoint.protocol()); |
659 | acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); | |
deb3572a WL |
660 | |
661 | // Try making the socket dual IPv6/IPv4 when listening on the IPv6 "any" address | |
662 | acceptor->set_option(boost::asio::ip::v6_only( | |
663 | !bBindAny || bindAddress != asio::ip::address_v6::any()), v6_only_error); | |
664 | ||
ad25804f GS |
665 | acceptor->bind(endpoint); |
666 | acceptor->listen(socket_base::max_connections); | |
667 | ||
21eb5ada | 668 | RPCListen(acceptor, *rpc_ssl_context, fUseSSL); |
c1d79812 PW |
669 | |
670 | fListening = true; | |
6afa4932 | 671 | rpc_acceptors.push_back(acceptor); |
deb3572a WL |
672 | // If dual IPv6/IPv4 bind succesful, skip binding to IPv4 separately |
673 | if(bBindAny && bindAddress == asio::ip::address_v6::any() && !v6_only_error) | |
674 | break; | |
675 | } | |
676 | catch(boost::system::system_error &e) | |
677 | { | |
678 | LogPrintf("ERROR: Binding RPC on address %s port %i failed: %s\n", bindAddress.to_string(), endpoint.port(), e.what()); | |
679 | strerr = strprintf(_("An error occurred while setting up the RPC address %s port %u for listening: %s"), bindAddress.to_string(), endpoint.port(), e.what()); | |
c1ecab81 | 680 | } |
c1d79812 PW |
681 | } |
682 | ||
683 | if (!fListening) { | |
5350ea41 | 684 | uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR); |
07368a9e | 685 | StartShutdown(); |
c1ecab81 GS |
686 | return; |
687 | } | |
69d605f4 | 688 | |
21eb5ada GA |
689 | rpc_worker_group = new boost::thread_group(); |
690 | for (int i = 0; i < GetArg("-rpcthreads", 4); i++) | |
691 | rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); | |
ff6a7af1 | 692 | fRPCRunning = true; |
21eb5ada GA |
693 | } |
694 | ||
a8db31c8 WL |
695 | void StartDummyRPCThread() |
696 | { | |
697 | if(rpc_io_service == NULL) | |
698 | { | |
699 | rpc_io_service = new asio::io_service(); | |
700 | /* Create dummy "work" to keep the thread from exiting when no timeouts active, | |
701 | * see http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.stopping_the_io_service_from_running_out_of_work */ | |
702 | rpc_dummy_work = new asio::io_service::work(*rpc_io_service); | |
703 | rpc_worker_group = new boost::thread_group(); | |
704 | rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); | |
ff6a7af1 | 705 | fRPCRunning = true; |
a8db31c8 WL |
706 | } |
707 | } | |
708 | ||
21eb5ada GA |
709 | void StopRPCThreads() |
710 | { | |
711 | if (rpc_io_service == NULL) return; | |
ff6a7af1 LD |
712 | // Set this to false first, so that longpolling loops will exit when woken up |
713 | fRPCRunning = false; | |
21eb5ada | 714 | |
cef44941 WL |
715 | // First, cancel all timers and acceptors |
716 | // This is not done automatically by ->stop(), and in some cases the destructor of | |
717 | // asio::io_service can hang if this is skipped. | |
33e5b429 | 718 | boost::system::error_code ec; |
cef44941 | 719 | BOOST_FOREACH(const boost::shared_ptr<ip::tcp::acceptor> &acceptor, rpc_acceptors) |
33e5b429 WL |
720 | { |
721 | acceptor->cancel(ec); | |
722 | if (ec) | |
723 | LogPrintf("%s: Warning: %s when cancelling acceptor", __func__, ec.message()); | |
724 | } | |
cef44941 WL |
725 | rpc_acceptors.clear(); |
726 | BOOST_FOREACH(const PAIRTYPE(std::string, boost::shared_ptr<deadline_timer>) &timer, deadlineTimers) | |
33e5b429 WL |
727 | { |
728 | timer.second->cancel(ec); | |
729 | if (ec) | |
730 | LogPrintf("%s: Warning: %s when cancelling timer", __func__, ec.message()); | |
731 | } | |
92f2c1fe | 732 | deadlineTimers.clear(); |
cef44941 | 733 | |
21eb5ada | 734 | rpc_io_service->stop(); |
ff6a7af1 | 735 | cvBlockChange.notify_all(); |
b2ba55c4 GA |
736 | if (rpc_worker_group != NULL) |
737 | rpc_worker_group->join_all(); | |
a8db31c8 | 738 | delete rpc_dummy_work; rpc_dummy_work = NULL; |
21eb5ada GA |
739 | delete rpc_worker_group; rpc_worker_group = NULL; |
740 | delete rpc_ssl_context; rpc_ssl_context = NULL; | |
741 | delete rpc_io_service; rpc_io_service = NULL; | |
e9205293 DJS |
742 | } |
743 | ||
ff6a7af1 LD |
744 | bool IsRPCRunning() |
745 | { | |
746 | return fRPCRunning; | |
747 | } | |
748 | ||
92f2c1fe GA |
749 | void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func) |
750 | { | |
751 | if (!err) | |
752 | func(); | |
753 | } | |
754 | ||
51ed9ec9 | 755 | void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds) |
92f2c1fe GA |
756 | { |
757 | assert(rpc_io_service != NULL); | |
758 | ||
759 | if (deadlineTimers.count(name) == 0) | |
760 | { | |
761 | deadlineTimers.insert(make_pair(name, | |
762 | boost::shared_ptr<deadline_timer>(new deadline_timer(*rpc_io_service)))); | |
763 | } | |
764 | deadlineTimers[name]->expires_from_now(posix_time::seconds(nSeconds)); | |
765 | deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func)); | |
766 | } | |
767 | ||
c6494d82 JG |
768 | class JSONRequest |
769 | { | |
770 | public: | |
771 | Value id; | |
772 | string strMethod; | |
773 | Array params; | |
774 | ||
775 | JSONRequest() { id = Value::null; } | |
776 | void parse(const Value& valRequest); | |
777 | }; | |
778 | ||
779 | void JSONRequest::parse(const Value& valRequest) | |
780 | { | |
781 | // Parse request | |
782 | if (valRequest.type() != obj_type) | |
738835d7 | 783 | throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); |
c6494d82 JG |
784 | const Object& request = valRequest.get_obj(); |
785 | ||
786 | // Parse id now so errors from here on will have the id | |
787 | id = find_value(request, "id"); | |
788 | ||
789 | // Parse method | |
790 | Value valMethod = find_value(request, "method"); | |
791 | if (valMethod.type() == null_type) | |
738835d7 | 792 | throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); |
c6494d82 | 793 | if (valMethod.type() != str_type) |
738835d7 | 794 | throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); |
c6494d82 | 795 | strMethod = valMethod.get_str(); |
cf0c47b2 | 796 | if (strMethod != "getblocktemplate") |
7d9d134b | 797 | LogPrint("rpc", "ThreadRPCServer method=%s\n", strMethod); |
c6494d82 JG |
798 | |
799 | // Parse params | |
800 | Value valParams = find_value(request, "params"); | |
801 | if (valParams.type() == array_type) | |
802 | params = valParams.get_array(); | |
803 | else if (valParams.type() == null_type) | |
804 | params = Array(); | |
805 | else | |
738835d7 | 806 | throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); |
c6494d82 JG |
807 | } |
808 | ||
fb78cc23 | 809 | |
61338901 JG |
810 | static Object JSONRPCExecOne(const Value& req) |
811 | { | |
812 | Object rpc_result; | |
813 | ||
814 | JSONRequest jreq; | |
815 | try { | |
816 | jreq.parse(req); | |
817 | ||
818 | Value result = tableRPC.execute(jreq.strMethod, jreq.params); | |
819 | rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id); | |
820 | } | |
821 | catch (Object& objError) | |
822 | { | |
823 | rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id); | |
824 | } | |
825 | catch (std::exception& e) | |
826 | { | |
827 | rpc_result = JSONRPCReplyObj(Value::null, | |
738835d7 | 828 | JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); |
61338901 JG |
829 | } |
830 | ||
831 | return rpc_result; | |
832 | } | |
833 | ||
834 | static string JSONRPCExecBatch(const Array& vReq) | |
835 | { | |
836 | Array ret; | |
837 | for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) | |
838 | ret.push_back(JSONRPCExecOne(vReq[reqIdx])); | |
839 | ||
0db9a805 | 840 | return write_string(Value(ret), false) + "\n"; |
61338901 JG |
841 | } |
842 | ||
854d0130 JG |
843 | static bool HTTPReq_JSONRPC(AcceptedConnection *conn, |
844 | string& strRequest, | |
845 | map<string, string>& mapHeaders, | |
846 | bool fRun) | |
847 | { | |
848 | // Check authorization | |
849 | if (mapHeaders.count("authorization") == 0) | |
850 | { | |
16f33f16 | 851 | conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush; |
854d0130 JG |
852 | return false; |
853 | } | |
854 | ||
855 | if (!HTTPAuthorized(mapHeaders)) | |
856 | { | |
857 | LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string()); | |
01094bd0 | 858 | /* Deter brute-forcing |
854d0130 JG |
859 | If this results in a DoS the user really |
860 | shouldn't have their RPC port exposed. */ | |
01094bd0 | 861 | MilliSleep(250); |
854d0130 | 862 | |
16f33f16 | 863 | conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush; |
854d0130 JG |
864 | return false; |
865 | } | |
866 | ||
867 | JSONRequest jreq; | |
868 | try | |
869 | { | |
870 | // Parse request | |
871 | Value valRequest; | |
872 | if (!read_string(strRequest, valRequest)) | |
873 | throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); | |
874 | ||
875 | string strReply; | |
876 | ||
877 | // singleton request | |
878 | if (valRequest.type() == obj_type) { | |
879 | jreq.parse(valRequest); | |
880 | ||
881 | Value result = tableRPC.execute(jreq.strMethod, jreq.params); | |
882 | ||
883 | // Send reply | |
884 | strReply = JSONRPCReply(result, Value::null, jreq.id); | |
885 | ||
886 | // array of requests | |
887 | } else if (valRequest.type() == array_type) | |
888 | strReply = JSONRPCExecBatch(valRequest.get_array()); | |
889 | else | |
890 | throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); | |
891 | ||
e17151ad | 892 | conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, strReply.size()) << strReply << std::flush; |
854d0130 JG |
893 | } |
894 | catch (Object& objError) | |
895 | { | |
896 | ErrorReply(conn->stream(), objError, jreq.id); | |
897 | return false; | |
898 | } | |
899 | catch (std::exception& e) | |
900 | { | |
901 | ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); | |
902 | return false; | |
903 | } | |
904 | return true; | |
905 | } | |
906 | ||
21eb5ada | 907 | void ServiceConnection(AcceptedConnection *conn) |
e9205293 | 908 | { |
96c52695 | 909 | bool fRun = true; |
d138598f | 910 | while (fRun && !ShutdownRequested()) |
21eb5ada | 911 | { |
2306dc4b | 912 | int nProto = 0; |
69d605f4 | 913 | map<string, string> mapHeaders; |
fcf234fc JG |
914 | string strRequest, strMethod, strURI; |
915 | ||
916 | // Read HTTP request line | |
917 | if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI)) | |
918 | break; | |
69d605f4 | 919 | |
2306dc4b | 920 | // Read HTTP message headers and body |
733177eb | 921 | ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto, MAX_SIZE); |
69d605f4 | 922 | |
854d0130 | 923 | // HTTP Keep-Alive is false; close connection immediately |
96c52695 DJS |
924 | if (mapHeaders["connection"] == "close") |
925 | fRun = false; | |
69d605f4 | 926 | |
854d0130 JG |
927 | if (strURI == "/") { |
928 | if (!HTTPReq_JSONRPC(conn, strRequest, mapHeaders, fRun)) | |
929 | break; | |
40a158e1 | 930 | } else { |
16f33f16 | 931 | conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush; |
e9205293 | 932 | break; |
69d605f4 WL |
933 | } |
934 | } | |
935 | } | |
936 | ||
460c51fd WL |
937 | json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const |
938 | { | |
939 | // Find method | |
940 | const CRPCCommand *pcmd = tableRPC[strMethod]; | |
941 | if (!pcmd) | |
738835d7 | 942 | throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); |
48ba56cd | 943 | #ifdef ENABLE_WALLET |
b0730874 JG |
944 | if (pcmd->reqWallet && !pwalletMain) |
945 | throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); | |
48ba56cd | 946 | #endif |
69d605f4 | 947 | |
460c51fd WL |
948 | // Observe safe mode |
949 | string strWarning = GetWarnings("rpc"); | |
3260b4c0 | 950 | if (strWarning != "" && !GetBoolArg("-disablesafemode", false) && |
460c51fd | 951 | !pcmd->okSafeMode) |
738835d7 | 952 | throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); |
460c51fd WL |
953 | |
954 | try | |
955 | { | |
956 | // Execute | |
957 | Value result; | |
958 | { | |
10ef3611 | 959 | if (pcmd->threadSafe) |
0e1d3551 | 960 | result = pcmd->actor(params, false); |
48ba56cd | 961 | #ifdef ENABLE_WALLET |
b0730874 JG |
962 | else if (!pwalletMain) { |
963 | LOCK(cs_main); | |
964 | result = pcmd->actor(params, false); | |
965 | } else { | |
0e1d3551 JG |
966 | LOCK2(cs_main, pwalletMain->cs_wallet); |
967 | result = pcmd->actor(params, false); | |
968 | } | |
48ba56cd WL |
969 | #else // ENABLE_WALLET |
970 | else { | |
971 | LOCK(cs_main); | |
972 | result = pcmd->actor(params, false); | |
973 | } | |
974 | #endif // !ENABLE_WALLET | |
460c51fd WL |
975 | } |
976 | return result; | |
977 | } | |
978 | catch (std::exception& e) | |
979 | { | |
738835d7 | 980 | throw JSONRPCError(RPC_MISC_ERROR, e.what()); |
460c51fd WL |
981 | } |
982 | } | |
69d605f4 | 983 | |
bbb09365 WL |
984 | std::string HelpExampleCli(string methodname, string args){ |
985 | return "> bitcoin-cli " + methodname + " " + args + "\n"; | |
986 | } | |
987 | ||
988 | std::string HelpExampleRpc(string methodname, string args){ | |
989 | return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " | |
990 | "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; | |
991 | } | |
992 | ||
e46704dd | 993 | const CRPCTable tableRPC; |