]> Git Repo - VerusCoin.git/blame - src/rpcserver.cpp
Merge pull request #4697
[VerusCoin.git] / src / rpcserver.cpp
CommitLineData
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
29using namespace boost;
30using namespace boost::asio;
31using namespace json_spirit;
40a158e1 32using namespace std;
69d605f4 33
f81ce5bd
GA
34static std::string strRPCUserColonPass;
35
ff6a7af1 36static bool fRPCRunning = false;
21eb5ada
GA
37// These are created by StartRPCThreads, destroyed in StopRPCThreads
38static asio::io_service* rpc_io_service = NULL;
92f2c1fe 39static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers;
21eb5ada
GA
40static ssl::context* rpc_ssl_context = NULL;
41static boost::thread_group* rpc_worker_group = NULL;
a8db31c8 42static boost::asio::io_service::work *rpc_dummy_work = NULL;
ee219125 43static std::vector<CSubNet> rpc_allow_subnets; //!< List of subnets to allow RPC connections from
cef44941 44static std::vector< boost::shared_ptr<ip::tcp::acceptor> > rpc_acceptors;
e9205293 45
899d373b 46void 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
67void 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
86static inline int64_t roundint64(double d)
87{
88 return (int64_t)(d > 0 ? d + 0.5 : d - 0.5);
89}
90
51ed9ec9 91int64_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 102Value ValueFromAmount(int64_t amount)
69d605f4
WL
103{
104 return (double)amount / (double)COIN;
105}
106
463c9710
PT
107uint256 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}
118uint256 ParseHashO(const Object& o, string strKey)
119{
120 return ParseHashV(find_value(o, strKey), strKey);
121}
122vector<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}
131vector<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 141string 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
201Value 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
221Value 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 240static 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 344CRPCTable::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
356const 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
365bool 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
375void 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 386CNetAddr 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
408bool 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
417template <typename Protocol>
418class AcceptedConnectionImpl : public AcceptedConnection
419{
420public:
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
449private:
450 SSLIOStreamDevice<Protocol> _d;
451 iostreams::stream< SSLIOStreamDevice<Protocol> > _stream;
e9205293
DJS
452};
453
21eb5ada 454void ServiceConnection(AcceptedConnection *conn);
69d605f4 455
914dc012 456// Forward declaration required for RPCListen
a0780ba0
GS
457template <typename Protocol, typename SocketAcceptorService>
458static 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
467template <typename Protocol, typename SocketAcceptorService>
468static 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
490template <typename Protocol, typename SocketAcceptorService>
491static 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
524static 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 532void 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
695void 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
709void 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
744bool IsRPCRunning()
745{
746 return fRPCRunning;
747}
748
92f2c1fe
GA
749void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func)
750{
751 if (!err)
752 func();
753}
754
51ed9ec9 755void 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
768class JSONRequest
769{
770public:
771 Value id;
772 string strMethod;
773 Array params;
774
775 JSONRequest() { id = Value::null; }
776 void parse(const Value& valRequest);
777};
778
779void 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
810static 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
834static 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
843static 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 907void 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
937json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array &params) 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
984std::string HelpExampleCli(string methodname, string args){
985 return "> bitcoin-cli " + methodname + " " + args + "\n";
986}
987
988std::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 993const CRPCTable tableRPC;
This page took 0.365725 seconds and 4 git commands to generate.