|
|
@ -26,7 +26,6 @@ |
|
|
|
#include <boost/thread.hpp> |
|
|
|
#include <boost/thread.hpp> |
|
|
|
#include "json/json_spirit_writer_template.h" |
|
|
|
#include "json/json_spirit_writer_template.h" |
|
|
|
|
|
|
|
|
|
|
|
using namespace boost; |
|
|
|
|
|
|
|
using namespace boost::asio; |
|
|
|
using namespace boost::asio; |
|
|
|
using namespace json_spirit; |
|
|
|
using namespace json_spirit; |
|
|
|
using namespace std; |
|
|
|
using namespace std; |
|
|
@ -39,7 +38,7 @@ static std::string rpcWarmupStatus("RPC server started"); |
|
|
|
static CCriticalSection cs_rpcWarmup; |
|
|
|
static CCriticalSection cs_rpcWarmup; |
|
|
|
|
|
|
|
|
|
|
|
//! These are created by StartRPCThreads, destroyed in StopRPCThreads
|
|
|
|
//! These are created by StartRPCThreads, destroyed in StopRPCThreads
|
|
|
|
static asio::io_service* rpc_io_service = NULL; |
|
|
|
static boost::asio::io_service* rpc_io_service = NULL; |
|
|
|
static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers; |
|
|
|
static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers; |
|
|
|
static ssl::context* rpc_ssl_context = NULL; |
|
|
|
static ssl::context* rpc_ssl_context = NULL; |
|
|
|
static boost::thread_group* rpc_worker_group = NULL; |
|
|
|
static boost::thread_group* rpc_worker_group = NULL; |
|
|
@ -428,7 +427,7 @@ class AcceptedConnectionImpl : public AcceptedConnection |
|
|
|
{ |
|
|
|
{ |
|
|
|
public: |
|
|
|
public: |
|
|
|
AcceptedConnectionImpl( |
|
|
|
AcceptedConnectionImpl( |
|
|
|
asio::io_service& io_service, |
|
|
|
boost::asio::io_service& io_service, |
|
|
|
ssl::context &context, |
|
|
|
ssl::context &context, |
|
|
|
bool fUseSSL) : |
|
|
|
bool fUseSSL) : |
|
|
|
sslStream(io_service, context), |
|
|
|
sslStream(io_service, context), |
|
|
@ -453,11 +452,11 @@ public: |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
typename Protocol::endpoint peer; |
|
|
|
typename Protocol::endpoint peer; |
|
|
|
asio::ssl::stream<typename Protocol::socket> sslStream; |
|
|
|
boost::asio::ssl::stream<typename Protocol::socket> sslStream; |
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
private: |
|
|
|
SSLIOStreamDevice<Protocol> _d; |
|
|
|
SSLIOStreamDevice<Protocol> _d; |
|
|
|
iostreams::stream< SSLIOStreamDevice<Protocol> > _stream; |
|
|
|
boost::iostreams::stream< SSLIOStreamDevice<Protocol> > _stream; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
void ServiceConnection(AcceptedConnection *conn); |
|
|
|
void ServiceConnection(AcceptedConnection *conn); |
|
|
@ -504,7 +503,7 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, |
|
|
|
const boost::system::error_code& error) |
|
|
|
const boost::system::error_code& error) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Immediately start accepting new connections, except when we're cancelled or our socket is closed.
|
|
|
|
// 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 != boost::asio::error::operation_aborted && acceptor->is_open()) |
|
|
|
RPCListen(acceptor, context, fUseSSL); |
|
|
|
RPCListen(acceptor, context, fUseSSL); |
|
|
|
|
|
|
|
|
|
|
|
AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn.get()); |
|
|
|
AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn.get()); |
|
|
@ -535,7 +534,7 @@ static ip::tcp::endpoint ParseEndpoint(const std::string &strEndpoint, int defau |
|
|
|
std::string addr; |
|
|
|
std::string addr; |
|
|
|
int port = defaultPort; |
|
|
|
int port = defaultPort; |
|
|
|
SplitHostPort(strEndpoint, port, addr); |
|
|
|
SplitHostPort(strEndpoint, port, addr); |
|
|
|
return ip::tcp::endpoint(asio::ip::address::from_string(addr), port); |
|
|
|
return ip::tcp::endpoint(boost::asio::ip::address::from_string(addr), port); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void StartRPCThreads() |
|
|
|
void StartRPCThreads() |
|
|
@ -590,7 +589,7 @@ void StartRPCThreads() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
assert(rpc_io_service == NULL); |
|
|
|
assert(rpc_io_service == NULL); |
|
|
|
rpc_io_service = new asio::io_service(); |
|
|
|
rpc_io_service = new boost::asio::io_service(); |
|
|
|
rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23); |
|
|
|
rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23); |
|
|
|
|
|
|
|
|
|
|
|
const bool fUseSSL = GetBoolArg("-rpcssl", false); |
|
|
|
const bool fUseSSL = GetBoolArg("-rpcssl", false); |
|
|
@ -599,14 +598,14 @@ void StartRPCThreads() |
|
|
|
{ |
|
|
|
{ |
|
|
|
rpc_ssl_context->set_options(ssl::context::no_sslv2 | ssl::context::no_sslv3); |
|
|
|
rpc_ssl_context->set_options(ssl::context::no_sslv2 | ssl::context::no_sslv3); |
|
|
|
|
|
|
|
|
|
|
|
filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); |
|
|
|
boost::filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); |
|
|
|
if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; |
|
|
|
if (!pathCertFile.is_complete()) pathCertFile = boost::filesystem::path(GetDataDir()) / pathCertFile; |
|
|
|
if (filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); |
|
|
|
if (boost::filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); |
|
|
|
else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string()); |
|
|
|
else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string()); |
|
|
|
|
|
|
|
|
|
|
|
filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); |
|
|
|
boost::filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); |
|
|
|
if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; |
|
|
|
if (!pathPKFile.is_complete()) pathPKFile = boost::filesystem::path(GetDataDir()) / pathPKFile; |
|
|
|
if (filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); |
|
|
|
if (boost::filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); |
|
|
|
else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string()); |
|
|
|
else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string()); |
|
|
|
|
|
|
|
|
|
|
|
string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); |
|
|
|
string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); |
|
|
@ -618,8 +617,8 @@ void StartRPCThreads() |
|
|
|
int defaultPort = GetArg("-rpcport", BaseParams().RPCPort()); |
|
|
|
int defaultPort = GetArg("-rpcport", BaseParams().RPCPort()); |
|
|
|
if (!mapArgs.count("-rpcallowip")) // Default to loopback if not allowing external IPs
|
|
|
|
if (!mapArgs.count("-rpcallowip")) // Default to loopback if not allowing external IPs
|
|
|
|
{ |
|
|
|
{ |
|
|
|
vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v6::loopback(), defaultPort)); |
|
|
|
vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v6::loopback(), defaultPort)); |
|
|
|
vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v4::loopback(), defaultPort)); |
|
|
|
vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v4::loopback(), defaultPort)); |
|
|
|
if (mapArgs.count("-rpcbind")) |
|
|
|
if (mapArgs.count("-rpcbind")) |
|
|
|
{ |
|
|
|
{ |
|
|
|
LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); |
|
|
|
LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); |
|
|
@ -641,8 +640,8 @@ void StartRPCThreads() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else { // No specific bind address specified, bind to any
|
|
|
|
} else { // No specific bind address specified, bind to any
|
|
|
|
vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v6::any(), defaultPort)); |
|
|
|
vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v6::any(), defaultPort)); |
|
|
|
vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v4::any(), defaultPort)); |
|
|
|
vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v4::any(), defaultPort)); |
|
|
|
// Prefer making the socket dual IPv6/IPv4 instead of binding
|
|
|
|
// Prefer making the socket dual IPv6/IPv4 instead of binding
|
|
|
|
// to both addresses seperately.
|
|
|
|
// to both addresses seperately.
|
|
|
|
bBindAny = true; |
|
|
|
bBindAny = true; |
|
|
@ -654,7 +653,7 @@ void StartRPCThreads() |
|
|
|
BOOST_FOREACH(const ip::tcp::endpoint &endpoint, vEndpoints) |
|
|
|
BOOST_FOREACH(const ip::tcp::endpoint &endpoint, vEndpoints) |
|
|
|
{ |
|
|
|
{ |
|
|
|
try { |
|
|
|
try { |
|
|
|
asio::ip::address bindAddress = endpoint.address(); |
|
|
|
boost::asio::ip::address bindAddress = endpoint.address(); |
|
|
|
straddress = bindAddress.to_string(); |
|
|
|
straddress = bindAddress.to_string(); |
|
|
|
LogPrintf("Binding RPC on address %s port %i (IPv4+IPv6 bind any: %i)\n", straddress, endpoint.port(), bBindAny); |
|
|
|
LogPrintf("Binding RPC on address %s port %i (IPv4+IPv6 bind any: %i)\n", straddress, endpoint.port(), bBindAny); |
|
|
|
boost::system::error_code v6_only_error; |
|
|
|
boost::system::error_code v6_only_error; |
|
|
@ -665,7 +664,7 @@ void StartRPCThreads() |
|
|
|
|
|
|
|
|
|
|
|
// Try making the socket dual IPv6/IPv4 when listening on the IPv6 "any" address
|
|
|
|
// Try making the socket dual IPv6/IPv4 when listening on the IPv6 "any" address
|
|
|
|
acceptor->set_option(boost::asio::ip::v6_only( |
|
|
|
acceptor->set_option(boost::asio::ip::v6_only( |
|
|
|
!bBindAny || bindAddress != asio::ip::address_v6::any()), v6_only_error); |
|
|
|
!bBindAny || bindAddress != boost::asio::ip::address_v6::any()), v6_only_error); |
|
|
|
|
|
|
|
|
|
|
|
acceptor->bind(endpoint); |
|
|
|
acceptor->bind(endpoint); |
|
|
|
acceptor->listen(socket_base::max_connections); |
|
|
|
acceptor->listen(socket_base::max_connections); |
|
|
@ -675,7 +674,7 @@ void StartRPCThreads() |
|
|
|
fListening = true; |
|
|
|
fListening = true; |
|
|
|
rpc_acceptors.push_back(acceptor); |
|
|
|
rpc_acceptors.push_back(acceptor); |
|
|
|
// If dual IPv6/IPv4 bind successful, skip binding to IPv4 separately
|
|
|
|
// If dual IPv6/IPv4 bind successful, skip binding to IPv4 separately
|
|
|
|
if(bBindAny && bindAddress == asio::ip::address_v6::any() && !v6_only_error) |
|
|
|
if(bBindAny && bindAddress == boost::asio::ip::address_v6::any() && !v6_only_error) |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
catch (const boost::system::system_error& e) |
|
|
|
catch (const boost::system::system_error& e) |
|
|
@ -693,7 +692,7 @@ void StartRPCThreads() |
|
|
|
|
|
|
|
|
|
|
|
rpc_worker_group = new boost::thread_group(); |
|
|
|
rpc_worker_group = new boost::thread_group(); |
|
|
|
for (int i = 0; i < GetArg("-rpcthreads", 4); i++) |
|
|
|
for (int i = 0; i < GetArg("-rpcthreads", 4); i++) |
|
|
|
rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); |
|
|
|
rpc_worker_group->create_thread(boost::bind(&boost::asio::io_service::run, rpc_io_service)); |
|
|
|
fRPCRunning = true; |
|
|
|
fRPCRunning = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -701,12 +700,12 @@ void StartDummyRPCThread() |
|
|
|
{ |
|
|
|
{ |
|
|
|
if(rpc_io_service == NULL) |
|
|
|
if(rpc_io_service == NULL) |
|
|
|
{ |
|
|
|
{ |
|
|
|
rpc_io_service = new asio::io_service(); |
|
|
|
rpc_io_service = new boost::asio::io_service(); |
|
|
|
/* Create dummy "work" to keep the thread from exiting when no timeouts active,
|
|
|
|
/* Create dummy "work" to keep the thread from exiting when no timeouts active,
|
|
|
|
* 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 */
|
|
|
|
* 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 */
|
|
|
|
rpc_dummy_work = new asio::io_service::work(*rpc_io_service); |
|
|
|
rpc_dummy_work = new boost::asio::io_service::work(*rpc_io_service); |
|
|
|
rpc_worker_group = new boost::thread_group(); |
|
|
|
rpc_worker_group = new boost::thread_group(); |
|
|
|
rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); |
|
|
|
rpc_worker_group->create_thread(boost::bind(&boost::asio::io_service::run, rpc_io_service)); |
|
|
|
fRPCRunning = true; |
|
|
|
fRPCRunning = true; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -719,7 +718,7 @@ void StopRPCThreads() |
|
|
|
|
|
|
|
|
|
|
|
// First, cancel all timers and acceptors
|
|
|
|
// First, cancel all timers and acceptors
|
|
|
|
// This is not done automatically by ->stop(), and in some cases the destructor of
|
|
|
|
// This is not done automatically by ->stop(), and in some cases the destructor of
|
|
|
|
// asio::io_service can hang if this is skipped.
|
|
|
|
// boost::asio::io_service can hang if this is skipped.
|
|
|
|
boost::system::error_code ec; |
|
|
|
boost::system::error_code ec; |
|
|
|
BOOST_FOREACH(const boost::shared_ptr<ip::tcp::acceptor> &acceptor, rpc_acceptors) |
|
|
|
BOOST_FOREACH(const boost::shared_ptr<ip::tcp::acceptor> &acceptor, rpc_acceptors) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -787,7 +786,7 @@ void RPCRunLater(const std::string& name, boost::function<void(void)> func, int6 |
|
|
|
deadlineTimers.insert(make_pair(name, |
|
|
|
deadlineTimers.insert(make_pair(name, |
|
|
|
boost::shared_ptr<deadline_timer>(new deadline_timer(*rpc_io_service)))); |
|
|
|
boost::shared_ptr<deadline_timer>(new deadline_timer(*rpc_io_service)))); |
|
|
|
} |
|
|
|
} |
|
|
|
deadlineTimers[name]->expires_from_now(posix_time::seconds(nSeconds)); |
|
|
|
deadlineTimers[name]->expires_from_now(boost::posix_time::seconds(nSeconds)); |
|
|
|
deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func)); |
|
|
|
deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|