diff options
Diffstat (limited to 'src/bitcoinrpc.cpp')
-rw-r--r-- | src/bitcoinrpc.cpp | 413 |
1 files changed, 224 insertions, 189 deletions
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index d82a382c42..285a88c8d8 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -11,35 +11,32 @@ #include "bitcoinrpc.h" #include "db.h" -#undef printf +#include <boost/algorithm/string.hpp> #include <boost/asio.hpp> #include <boost/asio/ip/v6_only.hpp> +#include <boost/asio/ssl.hpp> #include <boost/bind.hpp> #include <boost/filesystem.hpp> +#include <boost/filesystem/fstream.hpp> #include <boost/foreach.hpp> #include <boost/iostreams/concepts.hpp> #include <boost/iostreams/stream.hpp> -#include <boost/algorithm/string.hpp> #include <boost/lexical_cast.hpp> -#include <boost/asio/ssl.hpp> -#include <boost/filesystem/fstream.hpp> #include <boost/shared_ptr.hpp> #include <list> -#define printf OutputDebugStringF - using namespace std; using namespace boost; using namespace boost::asio; using namespace json_spirit; -void ThreadRPCServer2(void* parg); - static std::string strRPCUserColonPass; -const Object emptyobj; - -void ThreadRPCServer3(void* parg); +// These are created by StartRPCThreads, destroyed in StopRPCThreads +static asio::io_service* rpc_io_service = NULL; +static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers; +static ssl::context* rpc_ssl_context = NULL; +static boost::thread_group* rpc_worker_group = NULL; static inline unsigned short GetDefaultRPCPort() { @@ -179,14 +176,12 @@ Value help(const Array& params, bool fHelp) Value stop(const Array& params, bool fHelp) { + // Accept the deprecated and ignored 'detach' boolean argument if (fHelp || params.size() > 1) throw runtime_error( - "stop <detach>\n" - "<detach> is true or false to detach the database or not for this stop only\n" - "Stop Bitcoin server (and possibly override the detachdb config value)."); + "stop\n" + "Stop Bitcoin server."); // Shutdown will take long enough that the response should get back - if (params.size() > 0) - bitdb.SetDetach(params[0].get_bool()); StartShutdown(); return "Bitcoin server stopping"; } @@ -199,63 +194,70 @@ Value stop(const Array& params, bool fHelp) static const CRPCCommand vRPCCommands[] = -{ // name function safemd unlocked - // ------------------------ ----------------------- ------ -------- - { "help", &help, true, true }, - { "stop", &stop, true, true }, - { "getblockcount", &getblockcount, true, false }, - { "getconnectioncount", &getconnectioncount, true, false }, - { "getpeerinfo", &getpeerinfo, true, false }, - { "getdifficulty", &getdifficulty, true, false }, - { "getgenerate", &getgenerate, true, false }, - { "setgenerate", &setgenerate, true, false }, - { "gethashespersec", &gethashespersec, true, false }, - { "getinfo", &getinfo, true, false }, - { "getmininginfo", &getmininginfo, true, false }, - { "getnewaddress", &getnewaddress, true, false }, - { "getaccountaddress", &getaccountaddress, true, false }, - { "setaccount", &setaccount, true, false }, - { "getaccount", &getaccount, false, false }, - { "getaddressesbyaccount", &getaddressesbyaccount, true, false }, - { "sendtoaddress", &sendtoaddress, false, false }, - { "getreceivedbyaddress", &getreceivedbyaddress, false, false }, - { "getreceivedbyaccount", &getreceivedbyaccount, false, false }, - { "listreceivedbyaddress", &listreceivedbyaddress, false, false }, - { "listreceivedbyaccount", &listreceivedbyaccount, false, false }, - { "backupwallet", &backupwallet, true, false }, - { "keypoolrefill", &keypoolrefill, true, false }, - { "walletpassphrase", &walletpassphrase, true, false }, - { "walletpassphrasechange", &walletpassphrasechange, false, false }, - { "walletlock", &walletlock, true, false }, - { "encryptwallet", &encryptwallet, false, false }, - { "validateaddress", &validateaddress, true, false }, - { "getbalance", &getbalance, false, false }, - { "move", &movecmd, false, false }, - { "sendfrom", &sendfrom, false, false }, - { "sendmany", &sendmany, false, false }, - { "addmultisigaddress", &addmultisigaddress, false, false }, - { "getrawmempool", &getrawmempool, true, false }, - { "getblock", &getblock, false, false }, - { "getblockhash", &getblockhash, false, false }, - { "gettransaction", &gettransaction, false, false }, - { "listtransactions", &listtransactions, false, false }, - { "listaddressgroupings", &listaddressgroupings, false, false }, - { "signmessage", &signmessage, false, false }, - { "verifymessage", &verifymessage, false, false }, - { "getwork", &getwork, true, false }, - { "listaccounts", &listaccounts, false, false }, - { "settxfee", &settxfee, false, false }, - { "getblocktemplate", &getblocktemplate, true, false }, - { "submitblock", &submitblock, false, false }, - { "listsinceblock", &listsinceblock, false, false }, - { "dumpprivkey", &dumpprivkey, false, false }, - { "importprivkey", &importprivkey, false, false }, - { "listunspent", &listunspent, false, false }, - { "getrawtransaction", &getrawtransaction, false, false }, - { "createrawtransaction", &createrawtransaction, false, false }, - { "decoderawtransaction", &decoderawtransaction, false, false }, - { "signrawtransaction", &signrawtransaction, false, false }, - { "sendrawtransaction", &sendrawtransaction, false, false }, +{ // name actor (function) okSafeMode threadSafe + // ------------------------ ----------------------- ---------- ---------- + { "help", &help, true, true }, + { "stop", &stop, true, true }, + { "getblockcount", &getblockcount, true, false }, + { "getconnectioncount", &getconnectioncount, true, false }, + { "getpeerinfo", &getpeerinfo, true, false }, + { "addnode", &addnode, true, true }, + { "getaddednodeinfo", &getaddednodeinfo, true, true }, + { "getdifficulty", &getdifficulty, true, false }, + { "getgenerate", &getgenerate, true, false }, + { "setgenerate", &setgenerate, true, false }, + { "gethashespersec", &gethashespersec, true, false }, + { "getinfo", &getinfo, true, false }, + { "getmininginfo", &getmininginfo, true, false }, + { "getnewaddress", &getnewaddress, true, false }, + { "getaccountaddress", &getaccountaddress, true, false }, + { "setaccount", &setaccount, true, false }, + { "getaccount", &getaccount, false, false }, + { "getaddressesbyaccount", &getaddressesbyaccount, true, false }, + { "sendtoaddress", &sendtoaddress, false, false }, + { "getreceivedbyaddress", &getreceivedbyaddress, false, false }, + { "getreceivedbyaccount", &getreceivedbyaccount, false, false }, + { "listreceivedbyaddress", &listreceivedbyaddress, false, false }, + { "listreceivedbyaccount", &listreceivedbyaccount, false, false }, + { "backupwallet", &backupwallet, true, false }, + { "keypoolrefill", &keypoolrefill, true, false }, + { "walletpassphrase", &walletpassphrase, true, false }, + { "walletpassphrasechange", &walletpassphrasechange, false, false }, + { "walletlock", &walletlock, true, false }, + { "encryptwallet", &encryptwallet, false, false }, + { "validateaddress", &validateaddress, true, false }, + { "getbalance", &getbalance, false, false }, + { "move", &movecmd, false, false }, + { "sendfrom", &sendfrom, false, false }, + { "sendmany", &sendmany, false, false }, + { "addmultisigaddress", &addmultisigaddress, false, false }, + { "createmultisig", &createmultisig, true, true }, + { "getrawmempool", &getrawmempool, true, false }, + { "getblock", &getblock, false, false }, + { "getblockhash", &getblockhash, false, false }, + { "gettransaction", &gettransaction, false, false }, + { "listtransactions", &listtransactions, false, false }, + { "listaddressgroupings", &listaddressgroupings, false, false }, + { "signmessage", &signmessage, false, false }, + { "verifymessage", &verifymessage, false, false }, + { "getwork", &getwork, true, false }, + { "listaccounts", &listaccounts, false, false }, + { "settxfee", &settxfee, false, false }, + { "getblocktemplate", &getblocktemplate, true, false }, + { "submitblock", &submitblock, false, false }, + { "listsinceblock", &listsinceblock, false, false }, + { "dumpprivkey", &dumpprivkey, true, false }, + { "importprivkey", &importprivkey, false, false }, + { "listunspent", &listunspent, false, false }, + { "getrawtransaction", &getrawtransaction, false, false }, + { "createrawtransaction", &createrawtransaction, false, false }, + { "decoderawtransaction", &decoderawtransaction, false, false }, + { "signrawtransaction", &signrawtransaction, false, false }, + { "sendrawtransaction", &sendrawtransaction, false, false }, + { "gettxoutsetinfo", &gettxoutsetinfo, true, false }, + { "gettxout", &gettxout, true, false }, + { "lockunspent", &lockunspent, false, false }, + { "listlockunspent", &listlockunspent, false, false }, }; CRPCTable::CRPCTable() @@ -359,6 +361,41 @@ static string HTTPReply(int nStatus, const string& strMsg, bool keepalive) strMsg.c_str()); } +bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto, + string& http_method, string& http_uri) +{ + string str; + getline(stream, str); + + // HTTP request line is space-delimited + vector<string> vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return false; + + // HTTP methods permitted: GET, POST + http_method = vWords[0]; + if (http_method != "GET" && http_method != "POST") + return false; + + // HTTP URI must be an absolute path, relative to current host + http_uri = vWords[1]; + if (http_uri.size() == 0 || http_uri[0] != '/') + return false; + + // parse proto, if present + string strProto = ""; + if (vWords.size() > 2) + strProto = vWords[2]; + + proto = 0; + const char *ver = strstr(strProto.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver+7); + + return true; +} + int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto) { string str; @@ -374,7 +411,7 @@ int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto) return atoi(vWords[1].c_str()); } -int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet) +int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet) { int nLen = 0; loop @@ -399,17 +436,15 @@ int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHea return nLen; } -int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet) +int ReadHTTPMessage(std::basic_istream<char>& stream, map<string, + string>& mapHeadersRet, string& strMessageRet, + int nProto) { mapHeadersRet.clear(); strMessageRet = ""; - // Read status - int nProto = 0; - int nStatus = ReadHTTPStatus(stream, nProto); - // Read header - int nLen = ReadHTTPHeader(stream, mapHeadersRet); + int nLen = ReadHTTPHeaders(stream, mapHeadersRet); if (nLen < 0 || nLen > (int)MAX_SIZE) return HTTP_INTERNAL_SERVER_ERROR; @@ -431,7 +466,7 @@ int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRe mapHeadersRet["connection"] = "close"; } - return nStatus; + return HTTP_OK; } bool HTTPAuthorized(map<string, string>& mapHeaders) @@ -615,26 +650,7 @@ private: iostreams::stream< SSLIOStreamDevice<Protocol> > _stream; }; -void ThreadRPCServer(void* parg) -{ - // Make this thread recognisable as the RPC listener - RenameThread("bitcoin-rpclist"); - - try - { - vnThreadsRunning[THREAD_RPCLISTENER]++; - ThreadRPCServer2(parg); - vnThreadsRunning[THREAD_RPCLISTENER]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_RPCLISTENER]--; - PrintException(&e, "ThreadRPCServer()"); - } catch (...) { - vnThreadsRunning[THREAD_RPCLISTENER]--; - PrintException(NULL, "ThreadRPCServer()"); - } - printf("ThreadRPCServer exited\n"); -} +void ServiceConnection(AcceptedConnection *conn); // Forward declaration required for RPCListen template <typename Protocol, typename SocketAcceptorService> @@ -676,11 +692,8 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, AcceptedConnection* conn, const boost::system::error_code& error) { - vnThreadsRunning[THREAD_RPCLISTENER]++; - // Immediately start accepting new connections, except when we're cancelled or our socket is closed. - if (error != asio::error::operation_aborted - && acceptor->is_open()) + if (error != asio::error::operation_aborted && acceptor->is_open()) RPCListen(acceptor, context, fUseSSL); AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn); @@ -694,30 +707,25 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, // Restrict callers by IP. It is important to // do this before starting client thread, to filter out // certain DoS and misbehaving clients. - else if (tcp_conn - && !ClientAllowed(tcp_conn->peer.address())) + else if (tcp_conn && !ClientAllowed(tcp_conn->peer.address())) { // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. if (!fUseSSL) conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush; delete conn; } - - // start HTTP client thread - else if (!NewThread(ThreadRPCServer3, conn)) { - printf("Failed to create RPC server client thread\n"); + else { + ServiceConnection(conn); + conn->close(); delete conn; } - - vnThreadsRunning[THREAD_RPCLISTENER]--; } -void ThreadRPCServer2(void* parg) +void StartRPCThreads() { - printf("ThreadRPCServer started\n"); - strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; - if (mapArgs["-rpcpassword"] == "") + if ((mapArgs["-rpcpassword"] == "") || + (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) { unsigned char rand_pwd[32]; RAND_bytes(rand_pwd, 32); @@ -727,41 +735,46 @@ void ThreadRPCServer2(void* parg) else if (mapArgs.count("-daemon")) strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); uiInterface.ThreadSafeMessageBox(strprintf( - _("%s, you must set a rpcpassword in the configuration file:\n %s\n" + _("%s, you must set a rpcpassword in the configuration file:\n" + "%s\n" "It is recommended you use the following random password:\n" "rpcuser=bitcoinrpc\n" "rpcpassword=%s\n" "(you do not need to remember this password)\n" - "If the file does not exist, create it with owner-readable-only file permissions.\n"), + "The username and password MUST NOT be the same.\n" + "If the file does not exist, create it with owner-readable-only file permissions.\n" + "It is also recommended to set alertnotify so you are notified of problems;\n" + "for example: alertnotify=echo %%s | mail -s \"Bitcoin Alert\" admin@foo.com\n"), strWhatAmI.c_str(), GetConfigFile().string().c_str(), EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()), - _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL); + "", CClientUIInterface::MSG_ERROR); StartShutdown(); return; } - const bool fUseSSL = GetBoolArg("-rpcssl"); + assert(rpc_io_service == NULL); + rpc_io_service = new asio::io_service(); + rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23); - asio::io_service io_service; + const bool fUseSSL = GetBoolArg("-rpcssl", false); - ssl::context context(io_service, ssl::context::sslv23); if (fUseSSL) { - context.set_options(ssl::context::no_sslv2); + rpc_ssl_context->set_options(ssl::context::no_sslv2); filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; - if (filesystem::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string()); + if (filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str()); filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; - if (filesystem::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string(), ssl::context::pem); + if (filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str()); string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); - SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str()); + SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str()); } // Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets @@ -769,9 +782,7 @@ void ThreadRPCServer2(void* parg) asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any(); ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort())); boost::system::error_code v6_only_error; - boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(io_service)); - - boost::signals2::signal<void ()> StopRequests; + boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service)); bool fListening = false; std::string strerr; @@ -786,17 +797,13 @@ void ThreadRPCServer2(void* parg) acceptor->bind(endpoint); acceptor->listen(socket_base::max_connections); - RPCListen(acceptor, context, fUseSSL); - // Cancel outstanding listen-requests for this acceptor when shutting down - StopRequests.connect(signals2::slot<void ()>( - static_cast<void (ip::tcp::acceptor::*)()>(&ip::tcp::acceptor::close), acceptor.get()) - .track(acceptor)); + RPCListen(acceptor, *rpc_ssl_context, fUseSSL); fListening = true; } catch(boost::system::system_error &e) { - strerr = strprintf(_("An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what()); + strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what()); } try { @@ -806,17 +813,13 @@ void ThreadRPCServer2(void* parg) bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any(); endpoint.address(bindAddress); - acceptor.reset(new ip::tcp::acceptor(io_service)); + acceptor.reset(new ip::tcp::acceptor(*rpc_io_service)); acceptor->open(endpoint.protocol()); acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); acceptor->bind(endpoint); acceptor->listen(socket_base::max_connections); - RPCListen(acceptor, context, fUseSSL); - // Cancel outstanding listen-requests for this acceptor when shutting down - StopRequests.connect(signals2::slot<void ()>( - static_cast<void (ip::tcp::acceptor::*)()>(&ip::tcp::acceptor::close), acceptor.get()) - .track(acceptor)); + RPCListen(acceptor, *rpc_ssl_context, fUseSSL); fListening = true; } @@ -827,18 +830,48 @@ void ThreadRPCServer2(void* parg) } if (!fListening) { - uiInterface.ThreadSafeMessageBox(strerr, _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL); + uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return; } - vnThreadsRunning[THREAD_RPCLISTENER]--; - while (!fShutdown) - io_service.run_one(); - vnThreadsRunning[THREAD_RPCLISTENER]++; - StopRequests(); + rpc_worker_group = new boost::thread_group(); + for (int i = 0; i < GetArg("-rpcthreads", 4); i++) + rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); +} + +void StopRPCThreads() +{ + if (rpc_io_service == NULL) return; + + deadlineTimers.clear(); + rpc_io_service->stop(); + rpc_worker_group->join_all(); + delete rpc_worker_group; rpc_worker_group = NULL; + delete rpc_ssl_context; rpc_ssl_context = NULL; + delete rpc_io_service; rpc_io_service = NULL; +} + +void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func) +{ + if (!err) + func(); } +void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64 nSeconds) +{ + assert(rpc_io_service != NULL); + + if (deadlineTimers.count(name) == 0) + { + deadlineTimers.insert(make_pair(name, + boost::shared_ptr<deadline_timer>(new deadline_timer(*rpc_io_service)))); + } + deadlineTimers[name]->expires_from_now(posix_time::seconds(nSeconds)); + deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func)); +} + + class JSONRequest { public: @@ -913,35 +946,26 @@ static string JSONRPCExecBatch(const Array& vReq) return write_string(Value(ret), false) + "\n"; } -static CCriticalSection cs_THREAD_RPCHANDLER; - -void ThreadRPCServer3(void* parg) +void ServiceConnection(AcceptedConnection *conn) { - // Make this thread recognisable as the RPC handler - RenameThread("bitcoin-rpchand"); - - { - LOCK(cs_THREAD_RPCHANDLER); - vnThreadsRunning[THREAD_RPCHANDLER]++; - } - AcceptedConnection *conn = (AcceptedConnection *) parg; - bool fRun = true; - loop { - if (fShutdown || !fRun) - { - conn->close(); - delete conn; - { - LOCK(cs_THREAD_RPCHANDLER); - --vnThreadsRunning[THREAD_RPCHANDLER]; - } - return; - } + while (fRun) + { + int nProto = 0; map<string, string> mapHeaders; - string strRequest; + string strRequest, strMethod, strURI; + + // Read HTTP request line + if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI)) + break; - ReadHTTP(conn->stream(), mapHeaders, strRequest); + // Read HTTP message headers and body + ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto); + + if (strURI != "/") { + conn->stream() << HTTPReply(HTTP_NOT_FOUND, "", false) << std::flush; + break; + } // Check authorization if (mapHeaders.count("authorization") == 0) @@ -956,7 +980,7 @@ void ThreadRPCServer3(void* parg) If this results in a DOS the user really shouldn't have their RPC port exposed.*/ if (mapArgs["-rpcpassword"].size() < 20) - Sleep(250); + MilliSleep(250); conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; break; @@ -1002,12 +1026,6 @@ void ThreadRPCServer3(void* parg) break; } } - - delete conn; - { - LOCK(cs_THREAD_RPCHANDLER); - vnThreadsRunning[THREAD_RPCHANDLER]--; - } } json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const @@ -1019,7 +1037,7 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s // Observe safe mode string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !GetBoolArg("-disablesafemode") && + if (strWarning != "" && !GetBoolArg("-disablesafemode", false) && !pcmd->okSafeMode) throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); @@ -1028,7 +1046,7 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s // Execute Value result; { - if (pcmd->unlocked) + if (pcmd->threadSafe) result = pcmd->actor(params, false); else { LOCK2(cs_main, pwalletMain->cs_wallet); @@ -1053,7 +1071,7 @@ Object CallRPC(const string& strMethod, const Array& params) GetConfigFile().string().c_str())); // Connect to localhost - bool fUseSSL = GetBoolArg("-rpcssl"); + bool fUseSSL = GetBoolArg("-rpcssl", false); asio::io_service io_service; ssl::context context(io_service, ssl::context::sslv23); context.set_options(ssl::context::no_sslv2); @@ -1073,10 +1091,15 @@ Object CallRPC(const string& strMethod, const Array& params) string strPost = HTTPPost(strRequest, mapRequestHeaders); stream << strPost << std::flush; - // Receive reply + // Receive HTTP reply status + int nProto = 0; + int nStatus = ReadHTTPStatus(stream, nProto); + + // Receive HTTP reply message headers and body map<string, string> mapHeaders; string strReply; - int nStatus = ReadHTTP(stream, mapHeaders, strReply); + ReadHTTPMessage(stream, mapHeaders, strReply, nProto); + if (nStatus == HTTP_UNAUTHORIZED) throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) @@ -1132,6 +1155,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri // Special case non-string parameter types // if (strMethod == "stop" && n > 0) ConvertTo<bool>(params[0]); + if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo<bool>(params[0]); if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]); if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]); if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]); @@ -1158,6 +1182,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]); if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]); if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]); + if (strMethod == "createmultisig" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "createmultisig" && n > 1) ConvertTo<Array>(params[1]); if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]); if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]); if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]); @@ -1167,6 +1193,11 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]); if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true); if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true); + if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]); + if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]); + if (strMethod == "lockunspent" && n > 1) ConvertTo<Array>(params[1]); + if (strMethod == "importprivkey" && n > 2) ConvertTo<bool>(params[2]); return params; } @@ -1218,13 +1249,14 @@ int CommandLineRPC(int argc, char *argv[]) strPrint = write_string(result, true); } } - catch (std::exception& e) - { + catch (boost::thread_interrupted) { + throw; + } + catch (std::exception& e) { strPrint = string("error: ") + e.what(); nRet = 87; } - catch (...) - { + catch (...) { PrintException(NULL, "CommandLineRPC()"); } @@ -1262,6 +1294,9 @@ int main(int argc, char *argv[]) return CommandLineRPC(argc, argv); } } + catch (boost::thread_interrupted) { + throw; + } catch (std::exception& e) { PrintException(&e, "main()"); } catch (...) { |