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