diff --git a/src/cpp/ripple/Amount.cpp b/src/cpp/ripple/Amount.cpp index 6ec1c62105..3308864339 100644 --- a/src/cpp/ripple/Amount.cpp +++ b/src/cpp/ripple/Amount.cpp @@ -1056,7 +1056,6 @@ STAmount STAmount::setRate(uint64 rate) // <-- saTakerGot: Actual // <-- saTakerIssuerFee: Actual // <-- saOfferIssuerFee: Actual -// <-- bRemove: remove offer it is either fullfilled or unfunded bool STAmount::applyOffer( const uint32 uTakerPaysRate, const uint32 uOfferPaysRate, const STAmount& saOfferRate, diff --git a/src/cpp/ripple/AutoSocket.h b/src/cpp/ripple/AutoSocket.h index 1a3b6db7b6..73883f41f7 100644 --- a/src/cpp/ripple/AutoSocket.h +++ b/src/cpp/ripple/AutoSocket.h @@ -67,6 +67,18 @@ public: std::swap(mSecure, s.mSecure); } + boost::system::error_code verify(const std::string& strDomain) + { + boost::system::error_code ec; + + mSocket->set_verify_mode(boost::asio::ssl::verify_peer); + + // XXX Verify semantics of RFC 2818 are what we want. + mSocket->set_verify_callback(boost::asio::ssl::rfc2818_verification(strDomain), ec); + + return ec; + } + void async_handshake(handshake_type type, callback cbFunc) { if ((type == ssl_socket::client) || (mSecure)) diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index 6db317cca5..a49ff29f3d 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include #include @@ -573,6 +575,29 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) return (this->*(commandsA[i].pfpFunc))(jvParams); } +void callRPCAsync( + boost::asio::io_service& isService, +#if 0 + const std::string& strIp, const int iPort, const std::string& strUsername, const std::string& strPassword, const std::string& strPath, const std::string& strMethod, const Json::Value& jvParams, +#endif + boost::function callbackFuncP) +{ + Json::Value jvResult(Json::objectValue); + Json::Value jvSub(Json::objectValue); + + jvSub["foo"] = "bar"; + + jvResult["result"] = jvSub; + + (callbackFuncP)(jvResult); +} + +// Place the async result somewhere useful. +void callRPCHandler(Json::Value* jvOutput, const Json::Value& jvInput) +{ + (*jvOutput) = jvInput; +} + int commandLineRPC(const std::vector& vCmd) { Json::Value jvOutput; @@ -615,7 +640,11 @@ int commandLineRPC(const std::vector& vCmd) if (!theConfig.RPC_ADMIN_PASSWORD.empty()) jvRequest["admin_password"] = theConfig.RPC_ADMIN_PASSWORD; - jvOutput = callRPC( + boost::asio::io_service isService; + + callRPCAsync( + isService, +#if 0 theConfig.RPC_IP, theConfig.RPC_PORT, theConfig.RPC_USER, @@ -624,7 +653,12 @@ int commandLineRPC(const std::vector& vCmd) jvRequest.isMember("method") // Allow parser to rewrite method. ? jvRequest["method"].asString() : vCmd[0], - jvParams); // Parsed, execute. + jvParams, // Parsed, execute. + boost::bind(callRPCHandler, jvOutput, _1)); +#endif + boost::bind(callRPCHandler, &jvOutput, _1)); + + isService.run(); // This blocks until there is no more outstanding async calls. if (jvOutput.isMember("result")) { @@ -681,7 +715,7 @@ int commandLineRPC(const std::vector& vCmd) return nRet; } -Json::Value callRPC(const std::string& strIp, const int iPort, const std::string& strUsername, const std::string& strPassword, const std::string& strPath, const std::string& strMethod, const Json::Value& jvParams) +Json::Value callRPC(const std::string& strIp, const int iPort, const std::string& strUsername, const std::string& strPassword, const std::string& strPath, const std::string& strMethod, const Json::Value& jvParams, const bool bSSL) { // Connect to localhost if (!theConfig.QUIET) @@ -694,10 +728,23 @@ Json::Value callRPC(const std::string& strIp, const int iPort, const std::string boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(strIp), iPort); - boost::asio::ip::tcp::iostream stream; - stream.connect(endpoint); - if (stream.fail()) - throw std::runtime_error("couldn't connect to server"); + boost::asio::ip::tcp::iostream stream; + boost::asio::io_service isService; + boost::asio::ssl::context cContext(boost::asio::ssl::context::sslv23); + boost::asio::ssl::stream stream_ssl(isService, cContext); + + if (bSSL) + { +// stream_ssl.connect(endpoint); +// if (stream_ssl.fail()) +// throw std::runtime_error("couldn't connect to server"); + } + else + { + stream.connect(endpoint); + if (stream.fail()) + throw std::runtime_error("couldn't connect to server"); + } // cLog(lsDEBUG) << "connected" << std::endl; @@ -713,14 +760,32 @@ Json::Value callRPC(const std::string& strIp, const int iPort, const std::string // cLog(lsDEBUG) << "send request " << strMethod << " : " << strRequest << std::endl; std::string strPost = createHTTPPost(strPath, strRequest, mapRequestHeaders); - stream << strPost << std::flush; + if (bSSL) + { +// stream_ssl << strPost << std::flush; + } + else + { + stream << strPost << std::flush; + } // std::cerr << "post " << strPost << std::endl; // Receive reply std::map mapHeaders; std::string strReply; - int nStatus = ReadHTTP(stream, mapHeaders, strReply); + + int nStatus; + + if (bSSL) + { +// nStatus = ReadHTTP(stream_ssl, mapHeaders, strReply); + } + else + { + nStatus = ReadHTTP(stream, mapHeaders, strReply); + } + if (nStatus == 401) throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); else if ((nStatus >= 400) && (nStatus != 400) && (nStatus != 404) && (nStatus != 500)) // ? diff --git a/src/cpp/ripple/Config.cpp b/src/cpp/ripple/Config.cpp index 7313d87cf9..7af7c08242 100644 --- a/src/cpp/ripple/Config.cpp +++ b/src/cpp/ripple/Config.cpp @@ -134,26 +134,31 @@ void Config::setup(const std::string& strConf, bool bTestNet, bool bQuiet) if (strXdgConfigHome.empty()) { // $XDG_CONFIG_HOME was not set, use default based on $HOME. - strXdgConfigHome = str(boost::format("%s/.config") % strHome); + strXdgConfigHome = boost::str(boost::format("%s/.config") % strHome); } if (strXdgDataHome.empty()) { // $XDG_DATA_HOME was not set, use default based on $HOME. - strXdgDataHome = str(boost::format("%s/.local/share") % strHome); + strXdgDataHome = boost::str(boost::format("%s/.local/share") % strHome); } - CONFIG_DIR = str(boost::format("%s/" SYSTEM_NAME) % strXdgConfigHome); + CONFIG_DIR = boost::str(boost::format("%s/" SYSTEM_NAME) % strXdgConfigHome); CONFIG_FILE = CONFIG_DIR / strConfFile; - DATA_DIR = str(boost::format("%s/" SYSTEM_NAME) % strXdgDataHome); + DATA_DIR = boost::str(boost::format("%s/" SYSTEM_NAME) % strXdgDataHome); boost::filesystem::create_directories(CONFIG_DIR, ec); if (ec) - throw std::runtime_error(str(boost::format("Can not create %s") % CONFIG_DIR)); + throw std::runtime_error(boost::str(boost::format("Can not create %s") % CONFIG_DIR)); } } + SSL_CONTEXT.set_default_verify_paths(ec); + + if (ec) + throw std::runtime_error(boost::str(boost::format("Failed to set_default_verify_paths: %s") % ec.message())); + // Update default values load(); @@ -164,10 +169,11 @@ void Config::setup(const std::string& strConf, bool bTestNet, bool bQuiet) boost::filesystem::create_directories(DATA_DIR, ec); if (ec) - throw std::runtime_error(str(boost::format("Can not create %s") % DATA_DIR)); + throw std::runtime_error(boost::str(boost::format("Can not create %s") % DATA_DIR)); } Config::Config() + : SSL_CONTEXT(boost::asio::ssl::context::sslv23) { // // Defaults diff --git a/src/cpp/ripple/Config.h b/src/cpp/ripple/Config.h index 904f12ca90..11a6b8a1b9 100644 --- a/src/cpp/ripple/Config.h +++ b/src/cpp/ripple/Config.h @@ -2,6 +2,8 @@ #define __CONFIG__ #include +#include +#include #include #include "types.h" @@ -167,6 +169,8 @@ public: uint32 SIGN_VALIDATION; uint32 SIGN_PROPOSAL; + boost::asio::ssl::context SSL_CONTEXT; // Generic SSL context. + Config(); int getSize(SizedItemName); diff --git a/src/cpp/ripple/HttpsClient.cpp b/src/cpp/ripple/HttpsClient.cpp index 517e2d02f9..61a105f33e 100644 --- a/src/cpp/ripple/HttpsClient.cpp +++ b/src/cpp/ripple/HttpsClient.cpp @@ -12,6 +12,8 @@ #include #include +#include "Config.h" + using namespace boost::system; using namespace boost::asio; @@ -21,9 +23,8 @@ HttpsClient::HttpsClient( const std::string& strPath, std::size_t responseMax ) : - mCtx(boost::asio::ssl::context::sslv23), + mSocket(io_service, theConfig.SSL_CONTEXT), mResolver(io_service), - mSocketSsl(io_service, mCtx), mResponse(responseMax), mStrPath(strPath), mPort(port), @@ -32,10 +33,12 @@ HttpsClient::HttpsClient( } void HttpsClient::httpsGet( + bool bSSL, std::deque deqSites, boost::posix_time::time_duration timeout, boost::function complete) { + mSSL = bSSL; mDeqSites = deqSites; mComplete = complete; mTimeout = timeout; @@ -47,21 +50,12 @@ void HttpsClient::httpsNext() { // std::cerr << "Fetch: " << mDeqSites[0] << std::endl; boost::shared_ptr query(new boost::asio::ip::tcp::resolver::query(mDeqSites[0], boost::lexical_cast(mPort), - ip::resolver_query_base::numeric_service|ip::resolver_query_base::numeric_service)); + ip::resolver_query_base::numeric_service)); mQuery = query; - mCtx.set_default_verify_paths(mShutdown); - if (mShutdown) - { - std::cerr << "set_default_verify_paths: " << mShutdown.message() << std::endl; - } + mDeadline.expires_from_now(mTimeout, mShutdown); - if (!mShutdown) - { - mDeadline.expires_from_now(mTimeout, mShutdown); - - // std::cerr << "expires_from_now: " << mShutdown.message() << std::endl; - } + // std::cerr << "expires_from_now: " << mShutdown.message() << std::endl; if (!mShutdown) { @@ -106,8 +100,6 @@ void HttpsClient::handleDeadline(const boost::system::error_code& ecResult) } else { - boost::system::error_code ec_shutdown; - std::cerr << "Deadline arrived." << std::endl; // Mark us as shutting down. @@ -118,15 +110,24 @@ void HttpsClient::handleDeadline(const boost::system::error_code& ecResult) mResolver.cancel(); // Stop the transaction. - mSocketSsl.shutdown(ec_shutdown); + mSocket.async_shutdown(boost::bind( + &HttpsClient::handleShutdown, + shared_from_this(), + boost::asio::placeholders::error)); - if (ec_shutdown) - { - std::cerr << "Shutdown error: " << mDeqSites[0] << ": " << ec_shutdown.message() << std::endl; - } } } +void HttpsClient::handleShutdown( + const boost::system::error_code& ecResult + ) +{ + if (ecResult) + { + std::cerr << "Shutdown error: " << mDeqSites[0] << ": " << ecResult.message() << std::endl; + } +} + void HttpsClient::handleResolve( const boost::system::error_code& ecResult, boost::asio::ip::tcp::resolver::iterator itrEndpoint @@ -141,12 +142,12 @@ void HttpsClient::handleResolve( invokeComplete(mShutdown); } - else + else { // std::cerr << "Resolve complete." << std::endl; boost::asio::async_connect( - mSocketSsl.lowest_layer(), + mSocket.lowest_layer(), itrEndpoint, boost::bind( &HttpsClient::ShandleConnect, @@ -168,11 +169,7 @@ void HttpsClient::handleConnect(const boost::system::error_code& ecResult) if (!mShutdown) { // std::cerr << "Connected." << std::endl; - - mSocketSsl.set_verify_mode(boost::asio::ssl::verify_peer); - - // XXX Verify semantics of RFC 2818 are what we want. - mSocketSsl.set_verify_callback(boost::asio::ssl::rfc2818_verification(mDeqSites[0]), mShutdown); + mShutdown = mSocket.verify(mDeqSites[0]); if (mShutdown) { @@ -181,16 +178,22 @@ void HttpsClient::handleConnect(const boost::system::error_code& ecResult) } if (!mShutdown) - { - mSocketSsl.async_handshake(boost::asio::ssl::stream::client, - boost::bind(&HttpsClient::ShandleRequest, - shared_from_this(), - boost::asio::placeholders::error)); - } - else { invokeComplete(mShutdown); } + else if (mSSL) + { + mSocket.async_handshake( + AutoSocket::ssl_socket::client, + boost::bind( + &HttpsClient::ShandleRequest, + shared_from_this(), + boost::asio::placeholders::error)); + } + else + { + handleRequest(ecResult); + } } void HttpsClient::handleRequest(const boost::system::error_code& ecResult) @@ -217,7 +220,7 @@ void HttpsClient::handleRequest(const boost::system::error_code& ecResult) "Connection: close\r\n\r\n"; boost::asio::async_write( - mSocketSsl, + mSocket, mRequest, boost::bind(&HttpsClient::ShandleWrite, shared_from_this(), @@ -241,7 +244,7 @@ void HttpsClient::handleWrite(const boost::system::error_code& ecResult) // std::cerr << "Wrote." << std::endl; boost::asio::async_read( - mSocketSsl, + mSocket, mResponse, boost::asio::transfer_all(), boost::bind(&HttpsClient::ShandleData, @@ -333,6 +336,7 @@ void HttpsClient::parseData() } void HttpsClient::httpsGet( + bool bSSL, boost::asio::io_service& io_service, std::deque deqSites, const unsigned short port, @@ -343,10 +347,11 @@ void HttpsClient::httpsGet( boost::shared_ptr client(new HttpsClient(io_service, port, strPath, responseMax)); - client->httpsGet(deqSites, timeout, complete); + client->httpsGet(bSSL, deqSites, timeout, complete); } void HttpsClient::httpsGet( + bool bSSL, boost::asio::io_service& io_service, std::string strSite, const unsigned short port, @@ -359,7 +364,7 @@ void HttpsClient::httpsGet( boost::shared_ptr client(new HttpsClient(io_service, port, strPath, responseMax)); - client->httpsGet(deqSites, timeout, complete); + client->httpsGet(bSSL, deqSites, timeout, complete); } // vim:ts=4 diff --git a/src/cpp/ripple/HttpsClient.h b/src/cpp/ripple/HttpsClient.h index a9d6c93846..d9f920fff0 100644 --- a/src/cpp/ripple/HttpsClient.h +++ b/src/cpp/ripple/HttpsClient.h @@ -11,6 +11,7 @@ #include #include +#include "AutoSocket.h" // // Async https client. @@ -21,10 +22,10 @@ class HttpsClient : public boost::enable_shared_from_this private: typedef boost::shared_ptr pointer; - boost::asio::ssl::context mCtx; + bool mSSL; + AutoSocket mSocket; boost::asio::ip::tcp::resolver mResolver; boost::shared_ptr mQuery; - boost::asio::ssl::stream mSocketSsl; boost::asio::streambuf mRequest; boost::asio::streambuf mResponse; const std::string mStrPath; @@ -63,6 +64,8 @@ private: static void ShandleData(pointer This, const boost::system::error_code& ecResult) { This->handleData(ecResult); } + void handleShutdown(const boost::system::error_code& ecResult); + void parseData(); void httpsNext(); @@ -78,11 +81,13 @@ public: ); void httpsGet( + bool bSSL, std::deque deqSites, boost::posix_time::time_duration timeout, boost::function complete); static void httpsGet( + bool bSSL, boost::asio::io_service& io_service, std::deque deqSites, const unsigned short port, @@ -92,6 +97,7 @@ public: boost::function complete); static void httpsGet( + bool bSSL, boost::asio::io_service& io_service, std::string strSite, const unsigned short port, diff --git a/src/cpp/ripple/OfferCreateTransactor.cpp b/src/cpp/ripple/OfferCreateTransactor.cpp index 03743e1517..bbe3522ec3 100644 --- a/src/cpp/ripple/OfferCreateTransactor.cpp +++ b/src/cpp/ripple/OfferCreateTransactor.cpp @@ -152,7 +152,7 @@ TER OfferCreateTransactor::takeOffers( STAmount saTakerFunds = mEngine->getNodes().accountFunds(uTakerAccountID, saTakerPays); SLE::pointer sleOfferAccount; // Owner of offer. - if (!saOfferFunds.isPositive()) + if (!saOfferFunds.isPositive()) // Includes zero. { // Offer is unfunded, possibly due to previous balance action. cLog(lsINFO) << "takeOffers: offer unfunded: delete"; diff --git a/src/cpp/ripple/RPCSub.cpp b/src/cpp/ripple/RPCSub.cpp index e85d8ffe29..e9369d7b23 100644 --- a/src/cpp/ripple/RPCSub.cpp +++ b/src/cpp/ripple/RPCSub.cpp @@ -1,5 +1,6 @@ #include +#include "Application.h" #include "RPCSub.h" #include "CallRPC.h" @@ -63,7 +64,7 @@ void RPCSub::sendThread() cLog(lsDEBUG) << boost::str(boost::format("callRPC calling: %s") % mIp); // Drop result. - (void) callRPC(mIp, mPort, mUsername, mPassword, mPath, "event", jvEvent); +// (void) callRPC(theApp->getIOService(), mIp, mPort, mUsername, mPassword, mPath, "event", jvEvent); } catch (const std::exception& e) { diff --git a/src/cpp/ripple/UniqueNodeList.cpp b/src/cpp/ripple/UniqueNodeList.cpp index c0f4d8016f..3b457b7a0a 100644 --- a/src/cpp/ripple/UniqueNodeList.cpp +++ b/src/cpp/ripple/UniqueNodeList.cpp @@ -824,6 +824,7 @@ void UniqueNodeList::getIpsUrl(const RippleAddress& naNodePublic, section secSit && strScheme == "https") { HttpsClient::httpsGet( + true, theApp->getIOService(), strDomain, 443, @@ -867,6 +868,7 @@ void UniqueNodeList::getValidatorsUrl(const RippleAddress& naNodePublic, section && strScheme == "https") { HttpsClient::httpsGet( + true, theApp->getIOService(), strDomain, 443, @@ -1037,6 +1039,7 @@ void UniqueNodeList::fetchProcess(std::string strDomain) deqSites.push_back(strDomain); HttpsClient::httpsGet( + true, theApp->getIOService(), deqSites, 443, @@ -1572,6 +1575,7 @@ void UniqueNodeList::nodeNetwork() if (!theConfig.VALIDATORS_SITE.empty()) { HttpsClient::httpsGet( + true, theApp->getIOService(), theConfig.VALIDATORS_SITE, 443,