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.
12 #include "ui_interface.h"
14 #include "utilstrencodings.h"
16 #include "wallet/wallet.h"
19 #include <boost/algorithm/string.hpp>
20 #include <boost/asio.hpp>
21 #include <boost/asio/ssl.hpp>
22 #include <boost/bind.hpp>
23 #include <boost/filesystem.hpp>
24 #include <boost/foreach.hpp>
25 #include <boost/iostreams/concepts.hpp>
26 #include <boost/iostreams/stream.hpp>
27 #include <boost/shared_ptr.hpp>
28 #include <boost/signals2/signal.hpp>
29 #include <boost/thread.hpp>
30 #include "json/json_spirit_writer_template.h"
32 using namespace boost::asio
;
33 using namespace json_spirit
;
34 using namespace RPCServer
;
37 static std::string strRPCUserColonPass
;
39 static bool fRPCRunning
= false;
40 static bool fRPCInWarmup
= true;
41 static std::string
rpcWarmupStatus("RPC server started");
42 static CCriticalSection cs_rpcWarmup
;
44 //! These are created by StartRPCThreads, destroyed in StopRPCThreads
45 static boost::asio::io_service
* rpc_io_service
= NULL
;
46 static map
<string
, boost::shared_ptr
<deadline_timer
> > deadlineTimers
;
47 static ssl::context
* rpc_ssl_context
= NULL
;
48 static boost::thread_group
* rpc_worker_group
= NULL
;
49 static boost::asio::io_service::work
*rpc_dummy_work
= NULL
;
50 static std::vector
<CSubNet
> rpc_allow_subnets
; //!< List of subnets to allow RPC connections from
51 static std::vector
< boost::shared_ptr
<ip::tcp::acceptor
> > rpc_acceptors
;
53 static struct CRPCSignals
55 boost::signals2::signal
<void ()> Started
;
56 boost::signals2::signal
<void ()> Stopped
;
57 boost::signals2::signal
<void (const CRPCCommand
&)> PreCommand
;
58 boost::signals2::signal
<void (const CRPCCommand
&)> PostCommand
;
61 void RPCServer::OnStarted(boost::function
<void ()> slot
)
63 g_rpcSignals
.Started
.connect(slot
);
66 void RPCServer::OnStopped(boost::function
<void ()> slot
)
68 g_rpcSignals
.Stopped
.connect(slot
);
71 void RPCServer::OnPreCommand(boost::function
<void (const CRPCCommand
&)> slot
)
73 g_rpcSignals
.PreCommand
.connect(boost::bind(slot
, _1
));
76 void RPCServer::OnPostCommand(boost::function
<void (const CRPCCommand
&)> slot
)
78 g_rpcSignals
.PostCommand
.connect(boost::bind(slot
, _1
));
81 void RPCTypeCheck(const Array
& params
,
82 const list
<Value_type
>& typesExpected
,
86 BOOST_FOREACH(Value_type t
, typesExpected
)
88 if (params
.size() <= i
)
91 const Value
& v
= params
[i
];
92 if (!((v
.type() == t
) || (fAllowNull
&& (v
.type() == null_type
))))
94 string err
= strprintf("Expected type %s, got %s",
95 Value_type_name
[t
], Value_type_name
[v
.type()]);
96 throw JSONRPCError(RPC_TYPE_ERROR
, err
);
102 void RPCTypeCheck(const Object
& o
,
103 const map
<string
, Value_type
>& typesExpected
,
106 BOOST_FOREACH(const PAIRTYPE(string
, Value_type
)& t
, typesExpected
)
108 const Value
& v
= find_value(o
, t
.first
);
109 if (!fAllowNull
&& v
.type() == null_type
)
110 throw JSONRPCError(RPC_TYPE_ERROR
, strprintf("Missing %s", t
.first
));
112 if (!((v
.type() == t
.second
) || (fAllowNull
&& (v
.type() == null_type
))))
114 string err
= strprintf("Expected type %s for %s, got %s",
115 Value_type_name
[t
.second
], t
.first
, Value_type_name
[v
.type()]);
116 throw JSONRPCError(RPC_TYPE_ERROR
, err
);
121 static inline int64_t roundint64(double d
)
123 return (int64_t)(d
> 0 ? d
+ 0.5 : d
- 0.5);
126 CAmount
AmountFromValue(const Value
& value
)
128 double dAmount
= value
.get_real();
129 if (dAmount
<= 0.0 || dAmount
> 21000000.0)
130 throw JSONRPCError(RPC_TYPE_ERROR
, "Invalid amount");
131 CAmount nAmount
= roundint64(dAmount
* COIN
);
132 if (!MoneyRange(nAmount
))
133 throw JSONRPCError(RPC_TYPE_ERROR
, "Invalid amount");
137 Value
ValueFromAmount(const CAmount
& amount
)
139 return (double)amount
/ (double)COIN
;
142 uint256
ParseHashV(const Value
& v
, string strName
)
145 if (v
.type() == str_type
)
146 strHex
= v
.get_str();
147 if (!IsHex(strHex
)) // Note: IsHex("") is false
148 throw JSONRPCError(RPC_INVALID_PARAMETER
, strName
+" must be hexadecimal string (not '"+strHex
+"')");
150 result
.SetHex(strHex
);
153 uint256
ParseHashO(const Object
& o
, string strKey
)
155 return ParseHashV(find_value(o
, strKey
), strKey
);
157 vector
<unsigned char> ParseHexV(const Value
& v
, string strName
)
160 if (v
.type() == str_type
)
161 strHex
= v
.get_str();
163 throw JSONRPCError(RPC_INVALID_PARAMETER
, strName
+" must be hexadecimal string (not '"+strHex
+"')");
164 return ParseHex(strHex
);
166 vector
<unsigned char> ParseHexO(const Object
& o
, string strKey
)
168 return ParseHexV(find_value(o
, strKey
), strKey
);
173 * Note: This interface may still be subject to change.
176 string
CRPCTable::help(string strCommand
) const
180 set
<rpcfn_type
> setDone
;
181 vector
<pair
<string
, const CRPCCommand
*> > vCommands
;
183 for (map
<string
, const CRPCCommand
*>::const_iterator mi
= mapCommands
.begin(); mi
!= mapCommands
.end(); ++mi
)
184 vCommands
.push_back(make_pair(mi
->second
->category
+ mi
->first
, mi
->second
));
185 sort(vCommands
.begin(), vCommands
.end());
187 BOOST_FOREACH(const PAIRTYPE(string
, const CRPCCommand
*)& command
, vCommands
)
189 const CRPCCommand
*pcmd
= command
.second
;
190 string strMethod
= pcmd
->name
;
191 // We already filter duplicates, but these deprecated screw up the sort order
192 if (strMethod
.find("label") != string::npos
)
194 if ((strCommand
!= "" || pcmd
->category
== "hidden") && strMethod
!= strCommand
)
199 rpcfn_type pfn
= pcmd
->actor
;
200 if (setDone
.insert(pfn
).second
)
201 (*pfn
)(params
, true);
203 catch (const std::exception
& e
)
205 // Help text is returned in an exception
206 string strHelp
= string(e
.what());
207 if (strCommand
== "")
209 if (strHelp
.find('\n') != string::npos
)
210 strHelp
= strHelp
.substr(0, strHelp
.find('\n'));
212 if (category
!= pcmd
->category
)
214 if (!category
.empty())
216 category
= pcmd
->category
;
217 string firstLetter
= category
.substr(0,1);
218 boost::to_upper(firstLetter
);
219 strRet
+= "== " + firstLetter
+ category
.substr(1) + " ==\n";
222 strRet
+= strHelp
+ "\n";
226 strRet
= strprintf("help: unknown command: %s\n", strCommand
);
227 strRet
= strRet
.substr(0,strRet
.size()-1);
231 Value
help(const Array
& params
, bool fHelp
)
233 if (fHelp
|| params
.size() > 1)
235 "help ( \"command\" )\n"
236 "\nList all commands, or get help for a specified command.\n"
238 "1. \"command\" (string, optional) The command to get help on\n"
240 "\"text\" (string) The help text\n"
244 if (params
.size() > 0)
245 strCommand
= params
[0].get_str();
247 return tableRPC
.help(strCommand
);
251 Value
stop(const Array
& params
, bool fHelp
)
253 // Accept the deprecated and ignored 'detach' boolean argument
254 if (fHelp
|| params
.size() > 1)
257 "\nStop Bitcoin server.");
258 // Shutdown will take long enough that the response should get back
260 return "Bitcoin server stopping";
268 static const CRPCCommand vRPCCommands
[] =
269 { // category name actor (function) okSafeMode
270 // --------------------- ------------------------ ----------------------- ----------
271 /* Overall control/query calls */
272 { "control", "getinfo", &getinfo
, true }, /* uses wallet if enabled */
273 { "control", "help", &help
, true },
274 { "control", "stop", &stop
, true },
277 { "network", "getnetworkinfo", &getnetworkinfo
, true },
278 { "network", "addnode", &addnode
, true },
279 { "network", "getaddednodeinfo", &getaddednodeinfo
, true },
280 { "network", "getconnectioncount", &getconnectioncount
, true },
281 { "network", "getnettotals", &getnettotals
, true },
282 { "network", "getpeerinfo", &getpeerinfo
, true },
283 { "network", "ping", &ping
, true },
285 /* Block chain and UTXO */
286 { "blockchain", "getblockchaininfo", &getblockchaininfo
, true },
287 { "blockchain", "getbestblockhash", &getbestblockhash
, true },
288 { "blockchain", "getblockcount", &getblockcount
, true },
289 { "blockchain", "getblock", &getblock
, true },
290 { "blockchain", "getblockhash", &getblockhash
, true },
291 { "blockchain", "getchaintips", &getchaintips
, true },
292 { "blockchain", "getdifficulty", &getdifficulty
, true },
293 { "blockchain", "getmempoolinfo", &getmempoolinfo
, true },
294 { "blockchain", "getrawmempool", &getrawmempool
, true },
295 { "blockchain", "gettxout", &gettxout
, true },
296 { "blockchain", "gettxoutproof", &gettxoutproof
, true },
297 { "blockchain", "verifytxoutproof", &verifytxoutproof
, true },
298 { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo
, true },
299 { "blockchain", "verifychain", &verifychain
, true },
302 { "mining", "getblocktemplate", &getblocktemplate
, true },
303 { "mining", "getmininginfo", &getmininginfo
, true },
304 { "mining", "getnetworkhashps", &getnetworkhashps
, true },
305 { "mining", "prioritisetransaction", &prioritisetransaction
, true },
306 { "mining", "submitblock", &submitblock
, true },
309 /* Coin generation */
310 { "generating", "getgenerate", &getgenerate
, true },
311 { "generating", "setgenerate", &setgenerate
, true },
312 { "generating", "generate", &generate
, true },
315 /* Raw transactions */
316 { "rawtransactions", "createrawtransaction", &createrawtransaction
, true },
317 { "rawtransactions", "decoderawtransaction", &decoderawtransaction
, true },
318 { "rawtransactions", "decodescript", &decodescript
, true },
319 { "rawtransactions", "getrawtransaction", &getrawtransaction
, true },
320 { "rawtransactions", "sendrawtransaction", &sendrawtransaction
, false },
321 { "rawtransactions", "signrawtransaction", &signrawtransaction
, false }, /* uses wallet if enabled */
323 /* Utility functions */
324 { "util", "createmultisig", &createmultisig
, true },
325 { "util", "validateaddress", &validateaddress
, true }, /* uses wallet if enabled */
326 { "util", "verifymessage", &verifymessage
, true },
327 { "util", "estimatefee", &estimatefee
, true },
328 { "util", "estimatepriority", &estimatepriority
, true },
330 /* Not shown in help */
331 { "hidden", "invalidateblock", &invalidateblock
, true },
332 { "hidden", "reconsiderblock", &reconsiderblock
, true },
333 { "hidden", "setmocktime", &setmocktime
, true },
335 { "hidden", "resendwallettransactions", &resendwallettransactions
, true},
340 { "wallet", "addmultisigaddress", &addmultisigaddress
, true },
341 { "wallet", "backupwallet", &backupwallet
, true },
342 { "wallet", "dumpprivkey", &dumpprivkey
, true },
343 { "wallet", "dumpwallet", &dumpwallet
, true },
344 { "wallet", "encryptwallet", &encryptwallet
, true },
345 { "wallet", "getaccountaddress", &getaccountaddress
, true },
346 { "wallet", "getaccount", &getaccount
, true },
347 { "wallet", "getaddressesbyaccount", &getaddressesbyaccount
, true },
348 { "wallet", "getbalance", &getbalance
, false },
349 { "wallet", "getnewaddress", &getnewaddress
, true },
350 { "wallet", "getrawchangeaddress", &getrawchangeaddress
, true },
351 { "wallet", "getreceivedbyaccount", &getreceivedbyaccount
, false },
352 { "wallet", "getreceivedbyaddress", &getreceivedbyaddress
, false },
353 { "wallet", "gettransaction", &gettransaction
, false },
354 { "wallet", "getunconfirmedbalance", &getunconfirmedbalance
, false },
355 { "wallet", "getwalletinfo", &getwalletinfo
, false },
356 { "wallet", "importprivkey", &importprivkey
, true },
357 { "wallet", "importwallet", &importwallet
, true },
358 { "wallet", "importaddress", &importaddress
, true },
359 { "wallet", "keypoolrefill", &keypoolrefill
, true },
360 { "wallet", "listaccounts", &listaccounts
, false },
361 { "wallet", "listaddressgroupings", &listaddressgroupings
, false },
362 { "wallet", "listlockunspent", &listlockunspent
, false },
363 { "wallet", "listreceivedbyaccount", &listreceivedbyaccount
, false },
364 { "wallet", "listreceivedbyaddress", &listreceivedbyaddress
, false },
365 { "wallet", "listsinceblock", &listsinceblock
, false },
366 { "wallet", "listtransactions", &listtransactions
, false },
367 { "wallet", "listunspent", &listunspent
, false },
368 { "wallet", "lockunspent", &lockunspent
, true },
369 { "wallet", "move", &movecmd
, false },
370 { "wallet", "sendfrom", &sendfrom
, false },
371 { "wallet", "sendmany", &sendmany
, false },
372 { "wallet", "sendtoaddress", &sendtoaddress
, false },
373 { "wallet", "setaccount", &setaccount
, true },
374 { "wallet", "settxfee", &settxfee
, true },
375 { "wallet", "signmessage", &signmessage
, true },
376 { "wallet", "walletlock", &walletlock
, true },
377 { "wallet", "walletpassphrasechange", &walletpassphrasechange
, true },
378 { "wallet", "walletpassphrase", &walletpassphrase
, true },
379 #endif // ENABLE_WALLET
382 CRPCTable::CRPCTable()
385 for (vcidx
= 0; vcidx
< (sizeof(vRPCCommands
) / sizeof(vRPCCommands
[0])); vcidx
++)
387 const CRPCCommand
*pcmd
;
389 pcmd
= &vRPCCommands
[vcidx
];
390 mapCommands
[pcmd
->name
] = pcmd
;
394 const CRPCCommand
*CRPCTable::operator[](string name
) const
396 map
<string
, const CRPCCommand
*>::const_iterator it
= mapCommands
.find(name
);
397 if (it
== mapCommands
.end())
403 bool HTTPAuthorized(map
<string
, string
>& mapHeaders
)
405 string strAuth
= mapHeaders
["authorization"];
406 if (strAuth
.substr(0,6) != "Basic ")
408 string strUserPass64
= strAuth
.substr(6); boost::trim(strUserPass64
);
409 string strUserPass
= DecodeBase64(strUserPass64
);
410 return TimingResistantEqual(strUserPass
, strRPCUserColonPass
);
413 void ErrorReply(std::ostream
& stream
, const Object
& objError
, const Value
& id
)
415 // Send error reply from json-rpc error object
416 int nStatus
= HTTP_INTERNAL_SERVER_ERROR
;
417 int code
= find_value(objError
, "code").get_int();
418 if (code
== RPC_INVALID_REQUEST
) nStatus
= HTTP_BAD_REQUEST
;
419 else if (code
== RPC_METHOD_NOT_FOUND
) nStatus
= HTTP_NOT_FOUND
;
420 string strReply
= JSONRPCReply(Value::null
, objError
, id
);
421 stream
<< HTTPReply(nStatus
, strReply
, false) << std::flush
;
424 CNetAddr
BoostAsioToCNetAddr(boost::asio::ip::address address
)
427 // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses
429 && (address
.to_v6().is_v4_compatible()
430 || address
.to_v6().is_v4_mapped()))
431 address
= address
.to_v6().to_v4();
435 boost::asio::ip::address_v4::bytes_type bytes
= address
.to_v4().to_bytes();
436 netaddr
.SetRaw(NET_IPV4
, &bytes
[0]);
440 boost::asio::ip::address_v6::bytes_type bytes
= address
.to_v6().to_bytes();
441 netaddr
.SetRaw(NET_IPV6
, &bytes
[0]);
446 bool ClientAllowed(const boost::asio::ip::address
& address
)
448 CNetAddr netaddr
= BoostAsioToCNetAddr(address
);
449 BOOST_FOREACH(const CSubNet
&subnet
, rpc_allow_subnets
)
450 if (subnet
.Match(netaddr
))
455 template <typename Protocol
>
456 class AcceptedConnectionImpl
: public AcceptedConnection
459 AcceptedConnectionImpl(
460 boost::asio::io_service
& io_service
,
461 ssl::context
&context
,
463 sslStream(io_service
, context
),
464 _d(sslStream
, fUseSSL
),
469 virtual std::iostream
& stream()
474 virtual std::string
peer_address_to_string() const
476 return peer
.address().to_string();
484 typename
Protocol::endpoint peer
;
485 boost::asio::ssl::stream
<typename
Protocol::socket
> sslStream
;
488 SSLIOStreamDevice
<Protocol
> _d
;
489 boost::iostreams::stream
< SSLIOStreamDevice
<Protocol
> > _stream
;
492 void ServiceConnection(AcceptedConnection
*conn
);
494 //! Forward declaration required for RPCListen
495 template <typename Protocol
, typename SocketAcceptorService
>
496 static void RPCAcceptHandler(boost::shared_ptr
< basic_socket_acceptor
<Protocol
, SocketAcceptorService
> > acceptor
,
497 ssl::context
& context
,
499 boost::shared_ptr
< AcceptedConnection
> conn
,
500 const boost::system::error_code
& error
);
503 * Sets up I/O resources to accept and handle a new connection.
505 template <typename Protocol
, typename SocketAcceptorService
>
506 static void RPCListen(boost::shared_ptr
< basic_socket_acceptor
<Protocol
, SocketAcceptorService
> > acceptor
,
507 ssl::context
& context
,
511 boost::shared_ptr
< AcceptedConnectionImpl
<Protocol
> > conn(new AcceptedConnectionImpl
<Protocol
>(acceptor
->get_io_service(), context
, fUseSSL
));
513 acceptor
->async_accept(
514 conn
->sslStream
.lowest_layer(),
516 boost::bind(&RPCAcceptHandler
<Protocol
, SocketAcceptorService
>,
526 * Accept and handle incoming connection.
528 template <typename Protocol
, typename SocketAcceptorService
>
529 static void RPCAcceptHandler(boost::shared_ptr
< basic_socket_acceptor
<Protocol
, SocketAcceptorService
> > acceptor
,
530 ssl::context
& context
,
532 boost::shared_ptr
< AcceptedConnection
> conn
,
533 const boost::system::error_code
& error
)
535 // Immediately start accepting new connections, except when we're cancelled or our socket is closed.
536 if (error
!= boost::asio::error::operation_aborted
&& acceptor
->is_open())
537 RPCListen(acceptor
, context
, fUseSSL
);
539 AcceptedConnectionImpl
<ip::tcp
>* tcp_conn
= dynamic_cast< AcceptedConnectionImpl
<ip::tcp
>* >(conn
.get());
543 // TODO: Actually handle errors
544 LogPrintf("%s: Error: %s\n", __func__
, error
.message());
546 // Restrict callers by IP. It is important to
547 // do this before starting client thread, to filter out
548 // certain DoS and misbehaving clients.
549 else if (tcp_conn
&& !ClientAllowed(tcp_conn
->peer
.address()))
551 // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
553 conn
->stream() << HTTPError(HTTP_FORBIDDEN
, false) << std::flush
;
557 ServiceConnection(conn
.get());
562 static ip::tcp::endpoint
ParseEndpoint(const std::string
&strEndpoint
, int defaultPort
)
565 int port
= defaultPort
;
566 SplitHostPort(strEndpoint
, port
, addr
);
567 return ip::tcp::endpoint(boost::asio::ip::address::from_string(addr
), port
);
570 void StartRPCThreads()
572 rpc_allow_subnets
.clear();
573 rpc_allow_subnets
.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet
574 rpc_allow_subnets
.push_back(CSubNet("::1")); // always allow IPv6 localhost
575 if (mapMultiArgs
.count("-rpcallowip"))
577 const vector
<string
>& vAllow
= mapMultiArgs
["-rpcallowip"];
578 BOOST_FOREACH(string strAllow
, vAllow
)
580 CSubNet
subnet(strAllow
);
581 if(!subnet
.IsValid())
583 uiInterface
.ThreadSafeMessageBox(
584 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
),
585 "", CClientUIInterface::MSG_ERROR
);
589 rpc_allow_subnets
.push_back(subnet
);
592 std::string strAllowed
;
593 BOOST_FOREACH(const CSubNet
&subnet
, rpc_allow_subnets
)
594 strAllowed
+= subnet
.ToString() + " ";
595 LogPrint("rpc", "Allowing RPC connections from: %s\n", strAllowed
);
597 strRPCUserColonPass
= mapArgs
["-rpcuser"] + ":" + mapArgs
["-rpcpassword"];
598 if (((mapArgs
["-rpcpassword"] == "") ||
599 (mapArgs
["-rpcuser"] == mapArgs
["-rpcpassword"])) && Params().RequireRPCPassword())
601 unsigned char rand_pwd
[32];
602 GetRandBytes(rand_pwd
, 32);
603 uiInterface
.ThreadSafeMessageBox(strprintf(
604 _("To use bitcoind, or the -server option to bitcoin-qt, you must set an rpcpassword in the configuration file:\n"
606 "It is recommended you use the following random password:\n"
607 "rpcuser=bitcoinrpc\n"
609 "(you do not need to remember this password)\n"
610 "The username and password MUST NOT be the same.\n"
611 "If the file does not exist, create it with owner-readable-only file permissions.\n"
612 "It is also recommended to set alertnotify so you are notified of problems;\n"
613 "for example: alertnotify=echo %%s | mail -s \"Bitcoin Alert\" admin@foo.com\n"),
614 GetConfigFile().string(),
615 EncodeBase58(&rand_pwd
[0],&rand_pwd
[0]+32)),
616 "", CClientUIInterface::MSG_ERROR
| CClientUIInterface::SECURE
);
621 assert(rpc_io_service
== NULL
);
622 rpc_io_service
= new boost::asio::io_service();
623 rpc_ssl_context
= new ssl::context(*rpc_io_service
, ssl::context::sslv23
);
625 const bool fUseSSL
= GetBoolArg("-rpcssl", false);
629 rpc_ssl_context
->set_options(ssl::context::no_sslv2
| ssl::context::no_sslv3
);
631 boost::filesystem::path
pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert"));
632 if (!pathCertFile
.is_complete()) pathCertFile
= boost::filesystem::path(GetDataDir()) / pathCertFile
;
633 if (boost::filesystem::exists(pathCertFile
)) rpc_ssl_context
->use_certificate_chain_file(pathCertFile
.string());
634 else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile
.string());
636 boost::filesystem::path
pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem"));
637 if (!pathPKFile
.is_complete()) pathPKFile
= boost::filesystem::path(GetDataDir()) / pathPKFile
;
638 if (boost::filesystem::exists(pathPKFile
)) rpc_ssl_context
->use_private_key_file(pathPKFile
.string(), ssl::context::pem
);
639 else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile
.string());
641 string strCiphers
= GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH");
642 SSL_CTX_set_cipher_list(rpc_ssl_context
->impl(), strCiphers
.c_str());
645 std::vector
<ip::tcp::endpoint
> vEndpoints
;
646 bool bBindAny
= false;
647 int defaultPort
= GetArg("-rpcport", BaseParams().RPCPort());
648 if (!mapArgs
.count("-rpcallowip")) // Default to loopback if not allowing external IPs
650 vEndpoints
.push_back(ip::tcp::endpoint(boost::asio::ip::address_v6::loopback(), defaultPort
));
651 vEndpoints
.push_back(ip::tcp::endpoint(boost::asio::ip::address_v4::loopback(), defaultPort
));
652 if (mapArgs
.count("-rpcbind"))
654 LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n");
656 } else if (mapArgs
.count("-rpcbind")) // Specific bind address
658 BOOST_FOREACH(const std::string
&addr
, mapMultiArgs
["-rpcbind"])
661 vEndpoints
.push_back(ParseEndpoint(addr
, defaultPort
));
663 catch (const boost::system::system_error
&)
665 uiInterface
.ThreadSafeMessageBox(
666 strprintf(_("Could not parse -rpcbind value %s as network address"), addr
),
667 "", CClientUIInterface::MSG_ERROR
);
672 } else { // No specific bind address specified, bind to any
673 vEndpoints
.push_back(ip::tcp::endpoint(boost::asio::ip::address_v6::any(), defaultPort
));
674 vEndpoints
.push_back(ip::tcp::endpoint(boost::asio::ip::address_v4::any(), defaultPort
));
675 // Prefer making the socket dual IPv6/IPv4 instead of binding
676 // to both addresses seperately.
680 bool fListening
= false;
682 std::string straddress
;
683 BOOST_FOREACH(const ip::tcp::endpoint
&endpoint
, vEndpoints
)
686 boost::asio::ip::address bindAddress
= endpoint
.address();
687 straddress
= bindAddress
.to_string();
688 LogPrintf("Binding RPC on address %s port %i (IPv4+IPv6 bind any: %i)\n", straddress
, endpoint
.port(), bBindAny
);
689 boost::system::error_code v6_only_error
;
690 boost::shared_ptr
<ip::tcp::acceptor
> acceptor(new ip::tcp::acceptor(*rpc_io_service
));
692 acceptor
->open(endpoint
.protocol());
693 acceptor
->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
695 // Try making the socket dual IPv6/IPv4 when listening on the IPv6 "any" address
696 acceptor
->set_option(boost::asio::ip::v6_only(
697 !bBindAny
|| bindAddress
!= boost::asio::ip::address_v6::any()), v6_only_error
);
699 acceptor
->bind(endpoint
);
700 acceptor
->listen(socket_base::max_connections
);
702 RPCListen(acceptor
, *rpc_ssl_context
, fUseSSL
);
705 rpc_acceptors
.push_back(acceptor
);
706 // If dual IPv6/IPv4 bind successful, skip binding to IPv4 separately
707 if(bBindAny
&& bindAddress
== boost::asio::ip::address_v6::any() && !v6_only_error
)
710 catch (const boost::system::system_error
& e
)
712 LogPrintf("ERROR: Binding RPC on address %s port %i failed: %s\n", straddress
, endpoint
.port(), e
.what());
713 strerr
= strprintf(_("An error occurred while setting up the RPC address %s port %u for listening: %s"), straddress
, endpoint
.port(), e
.what());
718 uiInterface
.ThreadSafeMessageBox(strerr
, "", CClientUIInterface::MSG_ERROR
);
723 rpc_worker_group
= new boost::thread_group();
724 for (int i
= 0; i
< GetArg("-rpcthreads", 4); i
++)
725 rpc_worker_group
->create_thread(boost::bind(&boost::asio::io_service::run
, rpc_io_service
));
727 g_rpcSignals
.Started();
730 void StartDummyRPCThread()
732 if(rpc_io_service
== NULL
)
734 rpc_io_service
= new boost::asio::io_service();
735 /* Create dummy "work" to keep the thread from exiting when no timeouts active,
736 * 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 */
737 rpc_dummy_work
= new boost::asio::io_service::work(*rpc_io_service
);
738 rpc_worker_group
= new boost::thread_group();
739 rpc_worker_group
->create_thread(boost::bind(&boost::asio::io_service::run
, rpc_io_service
));
744 void StopRPCThreads()
746 if (rpc_io_service
== NULL
) return;
747 // Set this to false first, so that longpolling loops will exit when woken up
750 // First, cancel all timers and acceptors
751 // This is not done automatically by ->stop(), and in some cases the destructor of
752 // boost::asio::io_service can hang if this is skipped.
753 boost::system::error_code ec
;
754 BOOST_FOREACH(const boost::shared_ptr
<ip::tcp::acceptor
> &acceptor
, rpc_acceptors
)
756 acceptor
->cancel(ec
);
758 LogPrintf("%s: Warning: %s when cancelling acceptor", __func__
, ec
.message());
760 rpc_acceptors
.clear();
761 BOOST_FOREACH(const PAIRTYPE(std::string
, boost::shared_ptr
<deadline_timer
>) &timer
, deadlineTimers
)
763 timer
.second
->cancel(ec
);
765 LogPrintf("%s: Warning: %s when cancelling timer", __func__
, ec
.message());
767 deadlineTimers
.clear();
769 rpc_io_service
->stop();
770 g_rpcSignals
.Stopped();
771 if (rpc_worker_group
!= NULL
)
772 rpc_worker_group
->join_all();
773 delete rpc_dummy_work
; rpc_dummy_work
= NULL
;
774 delete rpc_worker_group
; rpc_worker_group
= NULL
;
775 delete rpc_ssl_context
; rpc_ssl_context
= NULL
;
776 delete rpc_io_service
; rpc_io_service
= NULL
;
784 void SetRPCWarmupStatus(const std::string
& newStatus
)
787 rpcWarmupStatus
= newStatus
;
790 void SetRPCWarmupFinished()
793 assert(fRPCInWarmup
);
794 fRPCInWarmup
= false;
797 bool RPCIsInWarmup(std::string
*outStatus
)
801 *outStatus
= rpcWarmupStatus
;
805 void RPCRunHandler(const boost::system::error_code
& err
, boost::function
<void(void)> func
)
811 void RPCRunLater(const std::string
& name
, boost::function
<void(void)> func
, int64_t nSeconds
)
813 assert(rpc_io_service
!= NULL
);
815 if (deadlineTimers
.count(name
) == 0)
817 deadlineTimers
.insert(make_pair(name
,
818 boost::shared_ptr
<deadline_timer
>(new deadline_timer(*rpc_io_service
))));
820 deadlineTimers
[name
]->expires_from_now(boost::posix_time::seconds(nSeconds
));
821 deadlineTimers
[name
]->async_wait(boost::bind(RPCRunHandler
, _1
, func
));
831 JSONRequest() { id
= Value::null
; }
832 void parse(const Value
& valRequest
);
835 void JSONRequest::parse(const Value
& valRequest
)
838 if (valRequest
.type() != obj_type
)
839 throw JSONRPCError(RPC_INVALID_REQUEST
, "Invalid Request object");
840 const Object
& request
= valRequest
.get_obj();
842 // Parse id now so errors from here on will have the id
843 id
= find_value(request
, "id");
846 Value valMethod
= find_value(request
, "method");
847 if (valMethod
.type() == null_type
)
848 throw JSONRPCError(RPC_INVALID_REQUEST
, "Missing method");
849 if (valMethod
.type() != str_type
)
850 throw JSONRPCError(RPC_INVALID_REQUEST
, "Method must be a string");
851 strMethod
= valMethod
.get_str();
852 if (strMethod
!= "getblocktemplate")
853 LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod
));
856 Value valParams
= find_value(request
, "params");
857 if (valParams
.type() == array_type
)
858 params
= valParams
.get_array();
859 else if (valParams
.type() == null_type
)
862 throw JSONRPCError(RPC_INVALID_REQUEST
, "Params must be an array");
866 static Object
JSONRPCExecOne(const Value
& req
)
874 Value result
= tableRPC
.execute(jreq
.strMethod
, jreq
.params
);
875 rpc_result
= JSONRPCReplyObj(result
, Value::null
, jreq
.id
);
877 catch (const Object
& objError
)
879 rpc_result
= JSONRPCReplyObj(Value::null
, objError
, jreq
.id
);
881 catch (const std::exception
& e
)
883 rpc_result
= JSONRPCReplyObj(Value::null
,
884 JSONRPCError(RPC_PARSE_ERROR
, e
.what()), jreq
.id
);
890 static string
JSONRPCExecBatch(const Array
& vReq
)
893 for (unsigned int reqIdx
= 0; reqIdx
< vReq
.size(); reqIdx
++)
894 ret
.push_back(JSONRPCExecOne(vReq
[reqIdx
]));
896 return write_string(Value(ret
), false) + "\n";
899 static bool HTTPReq_JSONRPC(AcceptedConnection
*conn
,
901 map
<string
, string
>& mapHeaders
,
904 // Check authorization
905 if (mapHeaders
.count("authorization") == 0)
907 conn
->stream() << HTTPError(HTTP_UNAUTHORIZED
, false) << std::flush
;
911 if (!HTTPAuthorized(mapHeaders
))
913 LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn
->peer_address_to_string());
914 /* Deter brute-forcing
915 If this results in a DoS the user really
916 shouldn't have their RPC port exposed. */
919 conn
->stream() << HTTPError(HTTP_UNAUTHORIZED
, false) << std::flush
;
928 if (!read_string(strRequest
, valRequest
))
929 throw JSONRPCError(RPC_PARSE_ERROR
, "Parse error");
931 // Return immediately if in warmup
935 throw JSONRPCError(RPC_IN_WARMUP
, rpcWarmupStatus
);
941 if (valRequest
.type() == obj_type
) {
942 jreq
.parse(valRequest
);
944 Value result
= tableRPC
.execute(jreq
.strMethod
, jreq
.params
);
947 strReply
= JSONRPCReply(result
, Value::null
, jreq
.id
);
950 } else if (valRequest
.type() == array_type
)
951 strReply
= JSONRPCExecBatch(valRequest
.get_array());
953 throw JSONRPCError(RPC_PARSE_ERROR
, "Top-level object parse error");
955 conn
->stream() << HTTPReplyHeader(HTTP_OK
, fRun
, strReply
.size()) << strReply
<< std::flush
;
957 catch (const Object
& objError
)
959 ErrorReply(conn
->stream(), objError
, jreq
.id
);
962 catch (const std::exception
& e
)
964 ErrorReply(conn
->stream(), JSONRPCError(RPC_PARSE_ERROR
, e
.what()), jreq
.id
);
970 void ServiceConnection(AcceptedConnection
*conn
)
973 while (fRun
&& !ShutdownRequested())
976 map
<string
, string
> mapHeaders
;
977 string strRequest
, strMethod
, strURI
;
979 // Read HTTP request line
980 if (!ReadHTTPRequestLine(conn
->stream(), nProto
, strMethod
, strURI
))
983 // Read HTTP message headers and body
984 ReadHTTPMessage(conn
->stream(), mapHeaders
, strRequest
, nProto
, MAX_SIZE
);
986 // HTTP Keep-Alive is false; close connection immediately
987 if ((mapHeaders
["connection"] == "close") || (!GetBoolArg("-rpckeepalive", true)))
990 // Process via JSON-RPC API
992 if (!HTTPReq_JSONRPC(conn
, strRequest
, mapHeaders
, fRun
))
995 // Process via HTTP REST API
996 } else if (strURI
.substr(0, 6) == "/rest/" && GetBoolArg("-rest", false)) {
997 if (!HTTPReq_REST(conn
, strURI
, mapHeaders
, fRun
))
1001 conn
->stream() << HTTPError(HTTP_NOT_FOUND
, false) << std::flush
;
1007 json_spirit::Value
CRPCTable::execute(const std::string
&strMethod
, const json_spirit::Array
¶ms
) const
1010 const CRPCCommand
*pcmd
= tableRPC
[strMethod
];
1012 throw JSONRPCError(RPC_METHOD_NOT_FOUND
, "Method not found");
1014 g_rpcSignals
.PreCommand(*pcmd
);
1019 return pcmd
->actor(params
, false);
1021 catch (const std::exception
& e
)
1023 throw JSONRPCError(RPC_MISC_ERROR
, e
.what());
1026 g_rpcSignals
.PostCommand(*pcmd
);
1029 std::string
HelpExampleCli(string methodname
, string args
){
1030 return "> bitcoin-cli " + methodname
+ " " + args
+ "\n";
1033 std::string
HelpExampleRpc(string methodname
, string args
){
1034 return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
1035 "\"method\": \"" + methodname
+ "\", \"params\": [" + args
+ "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
1038 const CRPCTable tableRPC
;