diff --git a/init.cpp b/init.cpp index 45d79d71b..63ebfb0ad 100644 --- a/init.cpp +++ b/init.cpp @@ -165,17 +165,31 @@ bool AppInit2(int argc, char* argv[]) " bitcoin [options] help \t\t " + _("List commands\n") + " bitcoin [options] help \t\t " + _("Get help for a command\n") + _("Options:\n") + - " -conf= \t " + _("Specify configuration file (default: bitcoin.conf)\n") + - " -gen \t " + _("Generate coins\n") + - " -gen=0 \t " + _("Don't generate coins\n") + - " -min \t " + _("Start minimized\n") + - " -datadir= \t " + _("Specify data directory\n") + - " -proxy=\t " + _("Connect through socks4 proxy\n") + - " -addnode= \t " + _("Add a node to connect to\n") + - " -connect= \t " + _("Connect only to the specified node\n") + - " -server \t " + _("Accept command line and JSON-RPC commands\n") + - " -daemon \t " + _("Run in the background as a daemon and accept commands\n") + - " -? \t " + _("This help message\n"); + " -conf= \t " + _("Specify configuration file (default: bitcoin.conf)\n") + + " -gen \t " + _("Generate coins\n") + + " -gen=0 \t " + _("Don't generate coins\n") + + " -min \t " + _("Start minimized\n") + + " -datadir= \t " + _("Specify data directory\n") + + " -proxy= \t " + _("Connect through socks4 proxy\n") + + " -addnode= \t " + _("Add a node to connect to\n") + + " -connect= \t " + _("Connect only to the specified node\n") + + " -server \t " + _("Accept command line and JSON-RPC commands\n") + + " -daemon \t " + _("Run in the background as a daemon and accept commands\n") + + " -rpcuser= \t " + _("Username for JSON-RPC connections\n") + + " -rpcpassword=\t " + _("Password for JSON-RPC connections\n") + + " -rpcport= \t " + _("Listen for JSON-RPC connections on \n") + + " -rpcallowip= \t " + _("Allow JSON-RPC connections from specified IP address\n") + + " -rpcconnect= \t " + _("Send commands to node running on \n") + + " -? \t " + _("This help message\n"); + +#ifdef USE_SSL + strUsage += string() + + _("\nSSL options: (see the Bitcoin Wiki for SSL setup instructions)\n") + + " -rpcssl=1 \t " + _("Use OpenSSL (https) for JSON-RPC connections\n") + + " -rpcsslcertificatchainfile=\t " + _("Server certificate file (default: server.cert)\n") + + " -rpcsslprivatekeyfile= \t " + _("Server private key (default: server.pem)\n") + + " -rpcsslciphers= \t " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)\n"); +#endif #if defined(__WXMSW__) && defined(GUI) // Tabs make the columns line up in the message box diff --git a/main.cpp b/main.cpp index 7b04a1f63..569ea5372 100644 --- a/main.cpp +++ b/main.cpp @@ -2433,15 +2433,20 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { // Nodes rebroadcast an addr every 24 hours pfrom->vAddrToSend.clear(); - int64 nSince = GetAdjustedTime() - 6 * 60 * 60; // in the last 6 hours + int64 nSince = GetAdjustedTime() - 3 * 60 * 60; // in the last 3 hours CRITICAL_BLOCK(cs_mapAddresses) { + unsigned int nCount = 0; foreach(const PAIRTYPE(vector, CAddress)& item, mapAddresses) { - if (fShutdown) - return true; const CAddress& addr = item.second; if (addr.nTime > nSince) + nCount++; + } + foreach(const PAIRTYPE(vector, CAddress)& item, mapAddresses) + { + const CAddress& addr = item.second; + if (addr.nTime > nSince && GetRand(nCount) < 2000) pfrom->PushAddress(addr); } } @@ -2780,7 +2785,7 @@ void ThreadBitcoinMiner(void* parg) vnThreadsRunning[3]--; PrintException(NULL, "ThreadBitcoinMiner()"); } - UIThreadCall(bind(CalledSetStatusBar, "", 0)); + UIThreadCall(boost::bind(CalledSetStatusBar, "", 0)); nHPSTimerStart = 0; if (vnThreadsRunning[3] == 0) dHashesPerSec = 0; @@ -3143,7 +3148,7 @@ void BitcoinMiner() nHPSTimerStart = GetTimeMillis(); nHashCounter = 0; string strStatus = strprintf(" %.0f khash/s", dHashesPerSec/1000.0); - UIThreadCall(bind(CalledSetStatusBar, strStatus, 0)); + UIThreadCall(boost::bind(CalledSetStatusBar, strStatus, 0)); static int64 nLogTime; if (GetTime() - nLogTime > 30 * 60) { diff --git a/makefile.osx b/makefile.osx index 7894d055d..892417220 100644 --- a/makefile.osx +++ b/makefile.osx @@ -22,9 +22,10 @@ LIBS= -dead_strip \ $(DEPSDIR)/lib/libboost_filesystem.a \ $(DEPSDIR)/lib/libboost_program_options.a \ $(DEPSDIR)/lib/libboost_thread.a \ + $(DEPSDIR)/lib/libssl.a \ $(DEPSDIR)/lib/libcrypto.a -DEFS=$(shell $(DEPSDIR)/bin/wx-config --cxxflags) -D__WXMAC_OSX__ -DNOPCH -DMSG_NOSIGNAL=0 +DEFS=$(shell $(DEPSDIR)/bin/wx-config --cxxflags) -D__WXMAC_OSX__ -DNOPCH -DMSG_NOSIGNAL=0 -DUSE_SSL DEBUGFLAGS=-g -DwxDEBUG_LEVEL=0 # ppc doesn't work because we don't support big-endian diff --git a/makefile.unix b/makefile.unix index 21994154a..aa85ef9a0 100644 --- a/makefile.unix +++ b/makefile.unix @@ -23,11 +23,12 @@ LIBS= \ -l boost_program_options \ -l boost_thread \ -l db_cxx \ + -l ssl \ -l crypto \ -Wl,-Bdynamic \ -l gthread-2.0 -DEFS=-D__WXGTK__ -DNOPCH -DFOURWAYSSE2 +DEFS=-D__WXGTK__ -DNOPCH -DFOURWAYSSE2 -DUSE_SSL DEBUGFLAGS=-g -D__WXDEBUG__ CFLAGS=-O2 -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) HEADERS=headers.h strlcpy.h serialize.h uint256.h util.h key.h bignum.h base58.h \ diff --git a/rpc.cpp b/rpc.cpp index 19ec95004..88e44cc47 100644 --- a/rpc.cpp +++ b/rpc.cpp @@ -5,6 +5,12 @@ #include "headers.h" #undef printf #include +#include +#include +#ifdef USE_SSL +#include +typedef boost::asio::ssl::stream SSLStream; +#endif #include "json/json_spirit_reader_template.h" #include "json/json_spirit_writer_template.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 // file, we could use the compiled json_spirit option. -using boost::asio::ip::tcp; +using namespace boost::asio; using namespace json_spirit; void ThreadRPCServer2(void* parg); @@ -777,7 +783,7 @@ string HTTPReply(int nStatus, const string& strMsg) strMsg.c_str()); } -int ReadHTTPStatus(tcp::iostream& stream) +int ReadHTTPStatus(std::basic_istream& stream) { string str; getline(stream, str); @@ -788,7 +794,7 @@ int ReadHTTPStatus(tcp::iostream& stream) return atoi(vWords[1].c_str()); } -int ReadHTTPHeader(tcp::iostream& stream, map& mapHeadersRet) +int ReadHTTPHeader(std::basic_istream& stream, map& mapHeadersRet) { int nLen = 0; loop @@ -812,7 +818,7 @@ int ReadHTTPHeader(tcp::iostream& stream, map& mapHeadersRet) return nLen; } -int ReadHTTP(tcp::iostream& stream, map& mapHeadersRet, string& strMessageRet) +int ReadHTTP(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet) { mapHeadersRet.clear(); strMessageRet = ""; @@ -930,8 +936,59 @@ bool ClientAllowed(const string& strAddress) return false; } +#ifdef USE_SSL +// +// IOStream device that speaks SSL but can also speak non-SSL +// +class SSLIOStreamDevice : public iostreams::device { +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) { @@ -972,18 +1029,54 @@ void ThreadRPCServer2(void* parg) return; } - // Bind to loopback 127.0.0.1 so the socket can only be accessed locally - boost::asio::io_service io_service; - tcp::endpoint endpoint(mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(), 8332); - tcp::acceptor acceptor(io_service, endpoint); + bool fUseSSL = (mapArgs.count("-rpcssl") > 0); + asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); + + 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 { // Accept connection - tcp::iostream stream; - tcp::endpoint peer; +#ifdef USE_SSL + SSLStream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream stream(d); +#else + ip::tcp::iostream stream; +#endif + + ip::tcp::endpoint peer; vnThreadsRunning[4]--; +#ifdef USE_SSL + acceptor.accept(sslStream.lowest_layer(), peer); +#else acceptor.accept(*stream.rdbuf(), peer); +#endif vnThreadsRunning[4]++; if (fShutdown) return; @@ -1102,9 +1195,25 @@ Object CallRPC(const string& strMethod, const Array& params) GetConfigFile().c_str())); // 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 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()) throw runtime_error("couldn't connect to server"); +#endif + // HTTP basic authentication string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); diff --git a/serialize.h b/serialize.h index fde83e7a8..d6e715d46 100644 --- a/serialize.h +++ b/serialize.h @@ -22,7 +22,7 @@ class CDataStream; class CAutoFile; static const unsigned int MAX_SIZE = 0x02000000; -static const int VERSION = 31303; +static const int VERSION = 31304; static const char* pszSubVer = ""; diff --git a/util.cpp b/util.cpp index b63b795b8..5d81262e8 100644 --- a/util.cpp +++ b/util.cpp @@ -572,7 +572,7 @@ void PrintExceptionContinue(std::exception* pex, const char* pszThread) strMiscWarning = pszMessage; #ifdef GUI if (wxTheApp && !fDaemon) - boost::thread(bind(ThreadOneMessageBox, string(pszMessage))); + boost::thread(boost::bind(ThreadOneMessageBox, string(pszMessage))); #endif } @@ -807,7 +807,7 @@ void AddTimeData(unsigned int ip, int64 nTime) string strMessage = _("Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly."); strMiscWarning = strMessage; printf("*** %s\n", strMessage.c_str()); - boost::thread(bind(ThreadSafeMessageBox, strMessage+" ", string("Bitcoin"), wxOK | wxICON_EXCLAMATION, (wxWindow*)NULL, -1, -1)); + boost::thread(boost::bind(ThreadSafeMessageBox, strMessage+" ", string("Bitcoin"), wxOK | wxICON_EXCLAMATION, (wxWindow*)NULL, -1, -1)); } } foreach(int64 n, vTimeOffsets)