|
|
@ -5,6 +5,12 @@ |
|
|
|
#include "headers.h" |
|
|
|
#include "headers.h" |
|
|
|
#undef printf |
|
|
|
#undef printf |
|
|
|
#include <boost/asio.hpp> |
|
|
|
#include <boost/asio.hpp> |
|
|
|
|
|
|
|
#include <boost/iostreams/concepts.hpp> |
|
|
|
|
|
|
|
#include <boost/iostreams/stream.hpp> |
|
|
|
|
|
|
|
#ifdef USE_SSL |
|
|
|
|
|
|
|
#include <boost/asio/ssl.hpp> |
|
|
|
|
|
|
|
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLStream; |
|
|
|
|
|
|
|
#endif |
|
|
|
#include "json/json_spirit_reader_template.h" |
|
|
|
#include "json/json_spirit_reader_template.h" |
|
|
|
#include "json/json_spirit_writer_template.h" |
|
|
|
#include "json/json_spirit_writer_template.h" |
|
|
|
#include "json/json_spirit_utils.h" |
|
|
|
#include "json/json_spirit_utils.h" |
|
|
@ -14,7 +20,7 @@ |
|
|
|
// a certain size around 145MB. If we need access to json_spirit outside this
|
|
|
|
// a certain size around 145MB. If we need access to json_spirit outside this
|
|
|
|
// file, we could use the compiled json_spirit option.
|
|
|
|
// file, we could use the compiled json_spirit option.
|
|
|
|
|
|
|
|
|
|
|
|
using boost::asio::ip::tcp; |
|
|
|
using namespace boost::asio; |
|
|
|
using namespace json_spirit; |
|
|
|
using namespace json_spirit; |
|
|
|
|
|
|
|
|
|
|
|
void ThreadRPCServer2(void* parg); |
|
|
|
void ThreadRPCServer2(void* parg); |
|
|
@ -777,7 +783,7 @@ string HTTPReply(int nStatus, const string& strMsg) |
|
|
|
strMsg.c_str()); |
|
|
|
strMsg.c_str()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int ReadHTTPStatus(tcp::iostream& stream) |
|
|
|
int ReadHTTPStatus(std::basic_istream<char>& stream) |
|
|
|
{ |
|
|
|
{ |
|
|
|
string str; |
|
|
|
string str; |
|
|
|
getline(stream, str); |
|
|
|
getline(stream, str); |
|
|
@ -788,7 +794,7 @@ int ReadHTTPStatus(tcp::iostream& stream) |
|
|
|
return atoi(vWords[1].c_str()); |
|
|
|
return atoi(vWords[1].c_str()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int ReadHTTPHeader(tcp::iostream& stream, map<string, string>& mapHeadersRet) |
|
|
|
int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int nLen = 0; |
|
|
|
int nLen = 0; |
|
|
|
loop |
|
|
|
loop |
|
|
@ -812,7 +818,7 @@ int ReadHTTPHeader(tcp::iostream& stream, map<string, string>& mapHeadersRet) |
|
|
|
return nLen; |
|
|
|
return nLen; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int ReadHTTP(tcp::iostream& stream, map<string, string>& mapHeadersRet, string& strMessageRet) |
|
|
|
int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet) |
|
|
|
{ |
|
|
|
{ |
|
|
|
mapHeadersRet.clear(); |
|
|
|
mapHeadersRet.clear(); |
|
|
|
strMessageRet = ""; |
|
|
|
strMessageRet = ""; |
|
|
@ -930,8 +936,59 @@ bool ClientAllowed(const string& strAddress) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef USE_SSL |
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// IOStream device that speaks SSL but can also speak non-SSL
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> { |
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
fUseSSL = fUseSSLIn; |
|
|
|
|
|
|
|
fNeedHandshake = fUseSSLIn; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void handshake(ssl::stream_base::handshake_type role) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!fNeedHandshake) return; |
|
|
|
|
|
|
|
fNeedHandshake = false; |
|
|
|
|
|
|
|
stream.handshake(role); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
std::streamsize read(char* s, std::streamsize n) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
handshake(ssl::stream_base::server); // HTTPS servers read first
|
|
|
|
|
|
|
|
if (fUseSSL) return stream.read_some(asio::buffer(s, n)); |
|
|
|
|
|
|
|
return stream.next_layer().read_some(asio::buffer(s, n)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
std::streamsize write(const char* s, std::streamsize n) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
handshake(ssl::stream_base::client); // HTTPS clients write first
|
|
|
|
|
|
|
|
if (fUseSSL) return asio::write(stream, asio::buffer(s, n)); |
|
|
|
|
|
|
|
return asio::write(stream.next_layer(), asio::buffer(s, n)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
bool connect(const std::string& server, const std::string& port) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
ip::tcp::resolver resolver(stream.get_io_service()); |
|
|
|
|
|
|
|
ip::tcp::resolver::query query(server.c_str(), port.c_str()); |
|
|
|
|
|
|
|
ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); |
|
|
|
|
|
|
|
ip::tcp::resolver::iterator end; |
|
|
|
|
|
|
|
boost::system::error_code error = asio::error::host_not_found; |
|
|
|
|
|
|
|
while (error && endpoint_iterator != end) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
stream.lowest_layer().close(); |
|
|
|
|
|
|
|
stream.lowest_layer().connect(*endpoint_iterator++, error); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (error) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
|
|
bool fNeedHandshake; |
|
|
|
|
|
|
|
bool fUseSSL; |
|
|
|
|
|
|
|
SSLStream& stream; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
void ThreadRPCServer(void* parg) |
|
|
|
void ThreadRPCServer(void* parg) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -972,18 +1029,54 @@ void ThreadRPCServer2(void* parg) |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Bind to loopback 127.0.0.1 so the socket can only be accessed locally
|
|
|
|
bool fUseSSL = (mapArgs.count("-rpcssl") > 0); |
|
|
|
boost::asio::io_service io_service; |
|
|
|
asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); |
|
|
|
tcp::endpoint endpoint(mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(), 8332); |
|
|
|
|
|
|
|
tcp::acceptor acceptor(io_service, endpoint); |
|
|
|
asio::io_service io_service; |
|
|
|
|
|
|
|
ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332)); |
|
|
|
|
|
|
|
ip::tcp::acceptor acceptor(io_service, endpoint); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef USE_SSL |
|
|
|
|
|
|
|
ssl::context context(io_service, ssl::context::sslv23); |
|
|
|
|
|
|
|
if (fUseSSL) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
context.set_options(ssl::context::no_sslv2); |
|
|
|
|
|
|
|
filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert"); |
|
|
|
|
|
|
|
if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile; |
|
|
|
|
|
|
|
if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str()); |
|
|
|
|
|
|
|
else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str()); |
|
|
|
|
|
|
|
filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem"); |
|
|
|
|
|
|
|
if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile; |
|
|
|
|
|
|
|
if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem); |
|
|
|
|
|
|
|
else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string ciphers = GetArg("-rpcsslciphers", |
|
|
|
|
|
|
|
"TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); |
|
|
|
|
|
|
|
SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
if (fUseSSL) |
|
|
|
|
|
|
|
throw runtime_error("-rpcssl=true, but bitcoin compiled without full openssl libraries."); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
loop |
|
|
|
loop |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Accept connection
|
|
|
|
// Accept connection
|
|
|
|
tcp::iostream stream; |
|
|
|
#ifdef USE_SSL |
|
|
|
tcp::endpoint peer; |
|
|
|
SSLStream sslStream(io_service, context); |
|
|
|
|
|
|
|
SSLIOStreamDevice d(sslStream, fUseSSL); |
|
|
|
|
|
|
|
iostreams::stream<SSLIOStreamDevice> stream(d); |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
ip::tcp::iostream stream; |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ip::tcp::endpoint peer; |
|
|
|
vnThreadsRunning[4]--; |
|
|
|
vnThreadsRunning[4]--; |
|
|
|
|
|
|
|
#ifdef USE_SSL |
|
|
|
|
|
|
|
acceptor.accept(sslStream.lowest_layer(), peer); |
|
|
|
|
|
|
|
#else |
|
|
|
acceptor.accept(*stream.rdbuf(), peer); |
|
|
|
acceptor.accept(*stream.rdbuf(), peer); |
|
|
|
|
|
|
|
#endif |
|
|
|
vnThreadsRunning[4]++; |
|
|
|
vnThreadsRunning[4]++; |
|
|
|
if (fShutdown) |
|
|
|
if (fShutdown) |
|
|
|
return; |
|
|
|
return; |
|
|
@ -1102,9 +1195,25 @@ Object CallRPC(const string& strMethod, const Array& params) |
|
|
|
GetConfigFile().c_str())); |
|
|
|
GetConfigFile().c_str())); |
|
|
|
|
|
|
|
|
|
|
|
// Connect to localhost
|
|
|
|
// Connect to localhost
|
|
|
|
tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), "8332"); |
|
|
|
bool fUseSSL = (mapArgs.count("-rpcssl") > 0); |
|
|
|
|
|
|
|
#ifdef USE_SSL |
|
|
|
|
|
|
|
asio::io_service io_service; |
|
|
|
|
|
|
|
ssl::context context(io_service, ssl::context::sslv23); |
|
|
|
|
|
|
|
context.set_options(ssl::context::no_sslv2); |
|
|
|
|
|
|
|
SSLStream sslStream(io_service, context); |
|
|
|
|
|
|
|
SSLIOStreamDevice d(sslStream, fUseSSL); |
|
|
|
|
|
|
|
iostreams::stream<SSLIOStreamDevice> stream(d); |
|
|
|
|
|
|
|
if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"))) |
|
|
|
|
|
|
|
throw runtime_error("couldn't connect to server"); |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
if (fUseSSL) |
|
|
|
|
|
|
|
throw runtime_error("-rpcssl=true, but bitcoin compiled without full openssl libraries."); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")); |
|
|
|
if (stream.fail()) |
|
|
|
if (stream.fail()) |
|
|
|
throw runtime_error("couldn't connect to server"); |
|
|
|
throw runtime_error("couldn't connect to server"); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// HTTP basic authentication
|
|
|
|
// HTTP basic authentication
|
|
|
|
string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); |
|
|
|
string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); |
|
|
|