From fa5e328561d08df879c288372b305520ac887d0e Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 6 Dec 2012 10:55:23 -0800 Subject: [PATCH 01/13] New 'getRand' function. --- src/cpp/ripple/utils.cpp | 11 +++++++++++ src/cpp/ripple/utils.h | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/src/cpp/ripple/utils.cpp b/src/cpp/ripple/utils.cpp index 9734e59743..8e94f5a10f 100644 --- a/src/cpp/ripple/utils.cpp +++ b/src/cpp/ripple/utils.cpp @@ -5,6 +5,17 @@ #include #include +#include + +void getRand(unsigned char *buf, int num) +{ + if (RAND_bytes(buf, num) != 1) + { + assert(false); + throw std::runtime_error("Entropy pool not seeded"); + } +} + // // Time support // We have our own epoch. diff --git a/src/cpp/ripple/utils.h b/src/cpp/ripple/utils.h index 10eae9673b..92161ffae3 100644 --- a/src/cpp/ripple/utils.h +++ b/src/cpp/ripple/utils.h @@ -189,6 +189,10 @@ bool parseQuality(const std::string& strSource, uint32& uQuality); DH* DH_der_load(const std::string& strDer); std::string DH_der_gen(int iKeyLength); +void getRand(unsigned char *buf, int num); +inline static void getRand(char *buf, int num) { return getRand(reinterpret_cast(buf), num); } +inline static void getRand(void *buf, int num) { return getRand(reinterpret_cast(buf), num); } + inline std::string strGetEnv(const std::string& strKey) { return getenv(strKey.c_str()) ? getenv(strKey.c_str()) : ""; From 1d2bb868b672461aca3bd807e544399250d5d110 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 6 Dec 2012 10:55:56 -0800 Subject: [PATCH 02/13] Remove RAND_bytes calls. --- src/cpp/ripple/Application.cpp | 4 ++-- src/cpp/ripple/ECIES.cpp | 6 ++---- src/cpp/ripple/NetworkOPs.cpp | 15 ++------------- src/cpp/ripple/ProofOfWork.cpp | 6 +++--- src/cpp/ripple/RPCHandler.cpp | 26 +++++++++----------------- src/cpp/ripple/RippleAddress.cpp | 2 +- src/cpp/ripple/SNTPClient.cpp | 2 +- 7 files changed, 20 insertions(+), 41 deletions(-) diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index 91dacfb385..d8b7b8fa31 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -45,8 +45,8 @@ Application::Application() : mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL), mSweepTimer(mAuxService) { - RAND_bytes(mNonce256.begin(), mNonce256.size()); - RAND_bytes(reinterpret_cast(&mNonceST), sizeof(mNonceST)); + getRand(mNonce256.begin(), mNonce256.size()); + getRand(reinterpret_cast(&mNonceST), sizeof(mNonceST)); mJobQueue.setThreadCount(); mSweepTimer.expires_from_now(boost::posix_time::seconds(60)); mSweepTimer.async_wait(boost::bind(&Application::sweep, this)); diff --git a/src/cpp/ripple/ECIES.cpp b/src/cpp/ripple/ECIES.cpp index 447794f93a..ac9c371550 100644 --- a/src/cpp/ripple/ECIES.cpp +++ b/src/cpp/ripple/ECIES.cpp @@ -116,8 +116,7 @@ std::vector CKey::encryptECIES(CKey& otherKey, const std::vector< { ECIES_ENC_IV_TYPE iv; - if (RAND_bytes(static_cast(iv.begin()), ECIES_ENC_BLK_SIZE) != 1) - throw std::runtime_error("insufficient entropy"); + getRand(static_cast(iv.begin()), ECIES_ENC_BLK_SIZE); ECIES_ENC_KEY_TYPE secret; ECIES_HMAC_KEY_TYPE hmacKey; @@ -280,8 +279,7 @@ bool checkECIES(void) std::vector message(4096); int msglen = i%3000; - if (RAND_bytes(static_cast(&message.front()), msglen) != 1) - throw std::runtime_error("insufficient entropy"); + getRand(static_cast(&message.front()), msglen); message.resize(msglen); // encrypt message with sender's private key and recipient's public key diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 9ec450ddfc..07ac2dfc1e 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1311,19 +1311,8 @@ bool NetworkOPs::subServer(InfoSub* ispListener, Json::Value& jvResult) jvResult["stand_alone"] = theConfig.RUN_STANDALONE; - switch (RAND_bytes(uRandom.begin(), uRandom.size())) - { - case 0: - case 1: - jvResult["random"] = uRandom.ToString(); - break; - - case -1: - default: - // XXX Should probably stop running. - cLog(lsFATAL) << "Internal error: unable to generate secure random."; - break; - } + getRand(uRandom.begin(), uRandom.size()); + jvResult["random"] = uRandom.ToString(); return mSubServer.insert(ispListener).second; } diff --git a/src/cpp/ripple/ProofOfWork.cpp b/src/cpp/ripple/ProofOfWork.cpp index 1e4ab5d9b1..9f68929649 100644 --- a/src/cpp/ripple/ProofOfWork.cpp +++ b/src/cpp/ripple/ProofOfWork.cpp @@ -61,7 +61,7 @@ uint256 ProofOfWork::solve(int maxIterations) const throw std::runtime_error("invalid proof of work target/iteration"); uint256 nonce; - RAND_bytes(nonce.begin(), nonce.size()); + getRand(nonce.begin(), nonce.size()); std::vector buf2; buf2.resize(mIterations); @@ -112,7 +112,7 @@ bool ProofOfWork::checkSolution(const uint256& solution) const ProofOfWorkGenerator::ProofOfWorkGenerator() : mValidTime(180) { setDifficulty(1); - RAND_bytes(mSecret.begin(), mSecret.size()); + getRand(mSecret.begin(), mSecret.size()); } ProofOfWork ProofOfWorkGenerator::getProof() @@ -123,7 +123,7 @@ ProofOfWork ProofOfWorkGenerator::getProof() int now = static_cast(time(NULL) / 4); uint256 challenge; - RAND_bytes(challenge.begin(), challenge.size()); + getRand(challenge.begin(), challenge.size()); boost::mutex::scoped_lock sl(mLock); diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index a471baa680..c2c1c437c9 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -664,24 +664,16 @@ Json::Value RPCHandler::doRandom(Json::Value jvRequest) { uint256 uRandom; - switch (RAND_bytes(uRandom.begin(), uRandom.size())) + try { - case 0: - case 1: - { - Json::Value jvResult; - - jvResult["random"] = uRandom.ToString(); - - return jvResult; - } - break; - - case -1: - return rpcError(rpcNOT_SUPPORTED); - - default: - return rpcError(rpcINTERNAL); + getRand(uRandom.begin(), uRandom.size()); + Json::Value jvResult; + jvResult["random"] = uRandom.ToString(); + return jvResult; + } + catch (...) + { + return rpcError(rpcINTERNAL); } } diff --git a/src/cpp/ripple/RippleAddress.cpp b/src/cpp/ripple/RippleAddress.cpp index ecb152857e..7841f322e5 100644 --- a/src/cpp/ripple/RippleAddress.cpp +++ b/src/cpp/ripple/RippleAddress.cpp @@ -792,7 +792,7 @@ void RippleAddress::setSeedRandom() // XXX Maybe we should call MakeNewKey uint128 key; - RAND_bytes(key.begin(), key.size()); + getRand(key.begin(), key.size()); RippleAddress::setSeed(key); } diff --git a/src/cpp/ripple/SNTPClient.cpp b/src/cpp/ripple/SNTPClient.cpp index d9663d134b..d99cee4e1f 100644 --- a/src/cpp/ripple/SNTPClient.cpp +++ b/src/cpp/ripple/SNTPClient.cpp @@ -78,7 +78,7 @@ void SNTPClient::resolveComplete(const boost::system::error_code& error, boost:: } query.mReceivedReply = false; query.mLocalTimeSent = now; - RAND_bytes(reinterpret_cast(&query.mQueryNonce), sizeof(query.mQueryNonce)); + getRand(reinterpret_cast(&query.mQueryNonce), sizeof(query.mQueryNonce)); reinterpret_cast(SNTPQueryData)[NTP_OFF_XMITTS_INT] = time(NULL) + NTP_UNIX_OFFSET; reinterpret_cast(SNTPQueryData)[NTP_OFF_XMITTS_FRAC] = query.mQueryNonce; mSocket.async_send_to(boost::asio::buffer(SNTPQueryData, 48), *sel, From d3acc2cf753e72dc15f2a126a3d5df43f5e8561a Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Wed, 5 Dec 2012 13:59:40 -0800 Subject: [PATCH 03/13] Fix fractional part in to_human(). --- src/js/amount.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/js/amount.js b/src/js/amount.js index dd96653c44..62b6a51e56 100644 --- a/src/js/amount.js +++ b/src/js/amount.js @@ -499,12 +499,16 @@ Amount.prototype.to_human = function (opts) if ("undefined" === typeof opts.group_sep) opts.group_sep = true; opts.group_width = opts.group_width || 3; - var denominator = this._is_native ? - consts.bi_xns_unit : - consts.bi_10.clone().pow(-this._offset); + var order = this._is_native ? consts.xns_precision : -this._offset; + var denominator = consts.bi_10.clone().pow(order); var int_part = this._value.divide(denominator).toString(10); var fraction_part = this._value.mod(denominator).toString(10); + // Add leading zeros to fraction + while (fraction_part.length < order) { + fraction_part = "0" + fraction_part; + } + int_part = int_part.replace(/^0*/, ''); fraction_part = fraction_part.replace(/0*$/, ''); From cbb1e50222514f01b0bd0930ec7836ff3855b681 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Thu, 6 Dec 2012 12:20:16 -0800 Subject: [PATCH 04/13] Add event allowing client to track server's network connectivity. --- src/cpp/ripple/NetworkOPs.cpp | 21 +++++++++++++++++++++ src/cpp/ripple/RPCHandler.cpp | 1 + 2 files changed, 22 insertions(+) diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 07ac2dfc1e..54566429fc 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -867,8 +867,10 @@ void NetworkOPs::consensusViewChange() void NetworkOPs::setMode(OperatingMode om) { if (mMode == om) return; + if ((om >= omCONNECTED) && (mMode == omDISCONNECTED)) mConnectTime = boost::posix_time::second_clock::universal_time(); + Log lg((om < mMode) ? lsWARNING : lsINFO); if (om == omDISCONNECTED) lg << "STATE->Disconnected"; @@ -878,6 +880,25 @@ void NetworkOPs::setMode(OperatingMode om) lg << "STATE->Tracking"; else lg << "STATE->Full"; + + if ((om == omDISCONNECTED) || (mMode == omDISCONNECTED)) + { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); + + if (!mSubServer.empty()) + { + Json::Value jvObj(Json::objectValue); + + jvObj["type"] = "serverStatus"; + jvObj["server_status"] = om >= omCONNECTED ? "ok" : "noNetwork"; + + BOOST_FOREACH(InfoSub* ispListener, mSubServer) + { + ispListener->send(jvObj); + } + } + } + mMode = om; } diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 076706bdcb..9a6d6191d2 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2084,6 +2084,7 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest) if(streamName=="server") { mNetOps->subServer(mInfoSub, jvResult); + jvResult["server_status"] = mNetOps->available() ? "ok" : "noNetwork"; }else if(streamName=="ledger") { mNetOps->subLedger(mInfoSub, jvResult); From 73db82ac56c554bc9007d03ccade581ff94d68a5 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Thu, 6 Dec 2012 12:33:29 -0800 Subject: [PATCH 05/13] Only consider a server connection with server_status == 'ok' as being 'online'. --- src/js/remote.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/js/remote.js b/src/js/remote.js index f7e02742cd..793d07610c 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -412,8 +412,6 @@ Remote.prototype._connect_start = function () { }; if (self.online_target) { - self._set_state('online'); - // Note, we could get disconnected before this goes through. self._server_subscribe(); // Automatically subscribe. } @@ -493,6 +491,10 @@ Remote.prototype._connect_message = function (ws, json) { this.emit('ledger_closed', message); break; + case 'serverStatus': + this._set_state(message.server_status === 'ok' ? 'online' : 'offline'); + break; + // All other messages default: if (this.trace) utils.logObject("remote: "+message.type+": %s", message); @@ -852,6 +854,10 @@ Remote.prototype._server_subscribe = function () { self.emit('ledger_closed', message); } + if (message.server_status === "ok") { + self._set_state('online'); + } + self.emit('subscribed'); }) .request(); From 10e53b7562e25b17eb3bfc01d91a2027688e5f25 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Thu, 6 Dec 2012 14:10:26 -0800 Subject: [PATCH 06/13] Add WebSocket Secure (SSL) support to client library. --- src/js/remote.js | 4 +++- test/config-example.js | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/js/remote.js b/src/js/remote.js index 793d07610c..3f5be45110 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -191,6 +191,7 @@ var Remote = function (opts, trace) { this.trusted = opts.trusted; this.websocket_ip = opts.websocket_ip; this.websocket_port = opts.websocket_port; + this.websocket_ssl = opts.websocket_ssl; this.local_sequence = opts.local_sequence; // Locally track sequence numbers this.local_fee = opts.local_fee; // Locally set fees this.id = 0; @@ -384,7 +385,8 @@ Remote.prototype._connect_start = function () { // with self-signed certs as the user must have pre-approved the self-signed certs. var self = this; - var url = "ws://" + this.websocket_ip + ":" + this.websocket_port; + var url = (this.websocket_ssl ? "wss://" : "ws://") + + this.websocket_ip + ":" + this.websocket_port; if (this.trace) console.log("remote: connect: %s", url); diff --git a/test/config-example.js b/test/config-example.js index af94850fe1..c43f4d5d87 100644 --- a/test/config-example.js +++ b/test/config-example.js @@ -23,6 +23,7 @@ exports.servers = { 'rpc_port' : 5005, 'websocket_ip' : "127.0.0.1", 'websocket_port' : 5006, + 'websocket_ssl' : false, 'local_sequence' : true, 'local_fee' : true, // 'validation_seed' : "shhDFVsmS2GSu5vUyZSPXYfj1r79h", From 4ec4ccf4903d7b39af7cb4c1e3f491861a4e32bc Mon Sep 17 00:00:00 2001 From: jed Date: Thu, 6 Dec 2012 16:48:04 -0800 Subject: [PATCH 07/13] broken websocket code --- newcoin.vcxproj | 13 ++++++++++ newcoin.vcxproj.filters | 42 +++++++++++++++++++++++++++++++ src/cpp/ripple/Config.cpp | 9 +++++++ src/cpp/ripple/Config.h | 2 ++ src/cpp/ripple/WSConnection.cpp | 22 ++++++++--------- src/cpp/ripple/WSConnection.h | 23 +++++++++++++---- src/cpp/ripple/WSDoor.cpp | 44 ++++++++++++++++++++++++--------- src/cpp/ripple/WSDoor.h | 12 +++------ src/cpp/ripple/WSHandler.h | 34 +++++++++++++++++++++---- 9 files changed, 161 insertions(+), 40 deletions(-) diff --git a/newcoin.vcxproj b/newcoin.vcxproj index 5b3889f069..83c3483efb 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -143,6 +143,7 @@ + @@ -184,6 +185,7 @@ + @@ -295,6 +297,17 @@ + + + + + + + + + + + diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters index f8070562c7..f7dfc590ef 100644 --- a/newcoin.vcxproj.filters +++ b/newcoin.vcxproj.filters @@ -31,6 +31,9 @@ {92775c5f-dc9f-4a97-a9a6-6d4bd4e424b4} + + {cd4c41c0-3ee6-49f8-8322-d11422b892f9} + @@ -351,6 +354,12 @@ Source Files + + Source Files + + + Source Files + @@ -656,6 +665,39 @@ Header Files + + Header Files\websocket + + + Header Files\websocket + + + Header Files\websocket + + + Header Files\websocket + + + Header Files\websocket + + + Header Files\websocket + + + Header Files\websocket + + + Header Files\websocket + + + Header Files\websocket + + + Header Files\websocket + + + Header Files\websocket + diff --git a/src/cpp/ripple/Config.cpp b/src/cpp/ripple/Config.cpp index 03c5453321..8ada3d6070 100644 --- a/src/cpp/ripple/Config.cpp +++ b/src/cpp/ripple/Config.cpp @@ -39,6 +39,8 @@ #define SECTION_WEBSOCKET_PUBLIC_PORT "websocket_public_port" #define SECTION_WEBSOCKET_IP "websocket_ip" #define SECTION_WEBSOCKET_PORT "websocket_port" +#define SECTION_WEBSOCKET_SECURE "websocket_secure" +#define SECTION_WEBSOCKET_SSL_CERT "websocket_ssl_cert" #define SECTION_VALIDATORS "validators" #define SECTION_VALIDATORS_SITE "validators_site" @@ -134,6 +136,7 @@ void Config::setup(const std::string& strConf, bool bQuiet) RPC_PORT = 5001; WEBSOCKET_PORT = SYSTEM_WEBSOCKET_PORT; WEBSOCKET_PUBLIC_PORT = SYSTEM_WEBSOCKET_PUBLIC_PORT; + WEBSOCKET_SECURE = false; NUMBER_CONNECTIONS = 30; // a new ledger every minute @@ -256,6 +259,12 @@ void Config::load() if (sectionSingleB(secConfig, SECTION_WEBSOCKET_PUBLIC_PORT, strTemp)) WEBSOCKET_PUBLIC_PORT = boost::lexical_cast(strTemp); + if (sectionSingleB(secConfig, SECTION_WEBSOCKET_SECURE, strTemp)) + WEBSOCKET_SECURE = boost::lexical_cast(strTemp); + + sectionSingleB(secConfig, SECTION_WEBSOCKET_SSL_CERT, WEBSOCKET_SSL_CERT); + + if (sectionSingleB(secConfig, SECTION_VALIDATION_SEED, strTemp)) { VALIDATION_SEED.setSeedGeneric(strTemp); diff --git a/src/cpp/ripple/Config.h b/src/cpp/ripple/Config.h index b7e4898c03..b8094a1481 100644 --- a/src/cpp/ripple/Config.h +++ b/src/cpp/ripple/Config.h @@ -91,6 +91,8 @@ public: std::string WEBSOCKET_IP; int WEBSOCKET_PORT; + bool WEBSOCKET_SECURE; + std::string WEBSOCKET_SSL_CERT; // RPC parameters std::string RPC_IP; diff --git a/src/cpp/ripple/WSConnection.cpp b/src/cpp/ripple/WSConnection.cpp index f217f07dae..136e7304d5 100644 --- a/src/cpp/ripple/WSConnection.cpp +++ b/src/cpp/ripple/WSConnection.cpp @@ -13,25 +13,25 @@ SETUP_LOG(); #include "../json/reader.h" #include "../json/writer.h" -WSConnection::~WSConnection() -{ - mNetwork.unsubTransactions(this); - mNetwork.unsubRTTransactions(this); - mNetwork.unsubLedger(this); - mNetwork.unsubServer(this); - mNetwork.unsubAccount(this, mSubAccountInfo, true); - mNetwork.unsubAccount(this, mSubAccountInfo, false); -} -void WSConnection::send(const Json::Value& jvObj) +//template +//WSConnection::~WSConnection() + +//template WSConnection::~WSConnection(); +//template WSConnection::~WSConnection(); + +template +void WSConnection::send(const Json::Value& jvObj) { mHandler->send(mConnection, jvObj); } +template void WSConnection::send(const Json::Value& jvObj); +template void WSConnection::send(const Json::Value& jvObj); // // Utilities // - +template Json::Value WSConnection::invokeCommand(Json::Value& jvRequest) { if (!jvRequest.isMember("command")) diff --git a/src/cpp/ripple/WSConnection.h b/src/cpp/ripple/WSConnection.h index 2bc4914d4c..d90ddab4f9 100644 --- a/src/cpp/ripple/WSConnection.h +++ b/src/cpp/ripple/WSConnection.h @@ -12,16 +12,17 @@ class WSServerHandler; // Storage for connection specific info // - Subscriptions // +template class WSConnection : public InfoSub { public: - typedef websocketpp::WSDOOR_SERVER::handler::connection_ptr connection_ptr; - typedef websocketpp::WSDOOR_SERVER::handler::message_ptr message_ptr; + typedef typename endpoint_type::handler::connection_ptr connection_ptr; + typedef typename endpoint_type::handler::message_ptr message_ptr; protected: typedef void (WSConnection::*doFuncPtr)(Json::Value& jvResult, Json::Value &jvRequest); - WSServerHandler* mHandler; + WSServerHandler* mHandler; connection_ptr mConnection; NetworkOPs& mNetwork; @@ -30,17 +31,29 @@ public: // : mHandler((WSServerHandler*)(NULL)), // mConnection(connection_ptr()) { ; } - WSConnection(WSServerHandler* wshpHandler, connection_ptr cpConnection) + WSConnection(WSServerHandler* wshpHandler, connection_ptr cpConnection) : mHandler(wshpHandler), mConnection(cpConnection), mNetwork(theApp->getOPs()) { ; } - virtual ~WSConnection(); + + virtual ~WSConnection() + { + mNetwork.unsubTransactions(this); + mNetwork.unsubRTTransactions(this); + mNetwork.unsubLedger(this); + mNetwork.unsubServer(this); + mNetwork.unsubAccount(this, mSubAccountInfo, true); + mNetwork.unsubAccount(this, mSubAccountInfo, false); + } // Implement overridden functions from base class: + template void send(const Json::Value& jvObj); // Utilities + template Json::Value invokeCommand(Json::Value& jvRequest); }; + // vim:ts=4 diff --git a/src/cpp/ripple/WSDoor.cpp b/src/cpp/ripple/WSDoor.cpp index 55e46b58a1..9bd25a2703 100644 --- a/src/cpp/ripple/WSDoor.cpp +++ b/src/cpp/ripple/WSDoor.cpp @@ -9,6 +9,7 @@ SETUP_LOG(); #include "utils.h" #include "WSConnection.h" #include "WSHandler.h" +#include "Config.h" #include "WSDoor.h" @@ -53,21 +54,42 @@ void WSDoor::startListening() SSL_CTX_set_tmp_dh_callback(mCtx->native_handle(), handleTmpDh); - // Construct a single handler for all requests. - websocketpp::WSDOOR_SERVER::handler::ptr handler(new WSServerHandler(mCtx, mPublic)); + if(theConfig.WEBSOCKET_SECURE) + { + // Construct a single handler for all requests. + websocketpp::server_tls::handler::ptr handler(new WSServerHandler(mCtx, mPublic)); - // Construct a websocket server. - mEndpoint = new websocketpp::WSDOOR_SERVER(handler); + // Construct a websocket server. + mSEndpoint = new websocketpp::server_tls(handler); - // mEndpoint->alog().unset_level(websocketpp::log::alevel::ALL); - // mEndpoint->elog().unset_level(websocketpp::log::elevel::ALL); + // mEndpoint->alog().unset_level(websocketpp::log::alevel::ALL); + // mEndpoint->elog().unset_level(websocketpp::log::elevel::ALL); - // Call the main-event-loop of the websocket server. - mEndpoint->listen( - boost::asio::ip::tcp::endpoint( - boost::asio::ip::address().from_string(mIp), mPort)); + // Call the main-event-loop of the websocket server. + mSEndpoint->listen( + boost::asio::ip::tcp::endpoint( + boost::asio::ip::address().from_string(mIp), mPort)); - delete mEndpoint; + delete mSEndpoint; + }else + { + // Construct a single handler for all requests. + websocketpp::server::handler::ptr handler(new WSServerHandler(mCtx, mPublic)); + + // Construct a websocket server. + mEndpoint = new websocketpp::server(handler); + + // mEndpoint->alog().unset_level(websocketpp::log::alevel::ALL); + // mEndpoint->elog().unset_level(websocketpp::log::elevel::ALL); + + // Call the main-event-loop of the websocket server. + mEndpoint->listen( + boost::asio::ip::tcp::endpoint( + boost::asio::ip::address().from_string(mIp), mPort)); + + delete mEndpoint; + } + } WSDoor* WSDoor::createWSDoor(const std::string& strIp, const int iPort, bool bPublic) diff --git a/src/cpp/ripple/WSDoor.h b/src/cpp/ripple/WSDoor.h index fe41d1bc5a..40c37815d1 100644 --- a/src/cpp/ripple/WSDoor.h +++ b/src/cpp/ripple/WSDoor.h @@ -10,16 +10,12 @@ #include #include -#if 1 -#define WSDOOR_SERVER server -#else -#define WSDOOR_SERVER server_tls -#endif - class WSDoor { private: - websocketpp::WSDOOR_SERVER* mEndpoint; + websocketpp::server* mEndpoint; + websocketpp::server_tls* mSEndpoint; + boost::thread* mThread; bool mPublic; std::string mIp; @@ -29,7 +25,7 @@ private: public: - WSDoor(const std::string& strIp, int iPort, bool bPublic) : mEndpoint(0), mThread(0), mPublic(bPublic), mIp(strIp), mPort(iPort) { ; } + WSDoor(const std::string& strIp, int iPort, bool bPublic) : mEndpoint(0), mSEndpoint(0), mThread(0), mPublic(bPublic), mIp(strIp), mPort(iPort) { ; } void stop(); diff --git a/src/cpp/ripple/WSHandler.h b/src/cpp/ripple/WSHandler.h index 9f4b23d5e6..6e2b40cf2c 100644 --- a/src/cpp/ripple/WSHandler.h +++ b/src/cpp/ripple/WSHandler.h @@ -2,7 +2,9 @@ #define __WSHANDLER__ #include "Application.h" +#include "Config.h" +template class WSConnection; // A single instance of this object is made. @@ -25,7 +27,7 @@ private: protected: boost::mutex mMapLock; // For each connection maintain an associated object to track subscriptions. - boost::unordered_map > mMap; + boost::unordered_map > > mMap; bool mPublic; public: @@ -33,10 +35,7 @@ public: bool getPublic() { return mPublic; }; - boost::shared_ptr on_tls_init() - { - return mCtx; - } + void send(connection_ptr cpClient, message_ptr mpMessage) { @@ -127,6 +126,31 @@ public: } } + boost::shared_ptr on_tls_init() + { + if(theConfig.WEBSOCKET_SECURE) + { + // create a tls context, init, and return. + boost::shared_ptr context(new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1)); + try { + context->set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::single_dh_use); + context->set_password_callback(boost::bind(&type::get_password, this)); + context->use_certificate_chain_file(theConfig.WEBSOCKET_SSL_CERT); + context->use_private_key_file(theConfig.WEBSOCKET_SSL_CERT, boost::asio::ssl::context::pem); + //context->use_tmp_dh_file("../../src/ssl/dh512.pem"); + } catch (std::exception& e) { + std::cout << e.what() << std::endl; + } + return context; + }else + { + return mCtx; + } + + } + // Respond to http requests. void http(connection_ptr cpClient) { From 176efd4a8ed834322b0ac8fc366b30f55999606a Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 6 Dec 2012 18:58:49 -0800 Subject: [PATCH 08/13] Add support for SSL certificate chain. --- src/cpp/ripple/Config.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cpp/ripple/Config.h b/src/cpp/ripple/Config.h index b8094a1481..50862a0c34 100644 --- a/src/cpp/ripple/Config.h +++ b/src/cpp/ripple/Config.h @@ -93,6 +93,7 @@ public: int WEBSOCKET_PORT; bool WEBSOCKET_SECURE; std::string WEBSOCKET_SSL_CERT; + std::string WEBSOCKET_SSL_CHAIN; // RPC parameters std::string RPC_IP; From 2e425d60fd2d8521622b810c7bd3a9d662c21864 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 6 Dec 2012 18:59:10 -0800 Subject: [PATCH 09/13] Don't return rpcSUCCESS. --- src/cpp/ripple/RPCErr.cpp | 1 - src/cpp/ripple/RPCErr.h | 3 +-- src/cpp/ripple/RPCHandler.cpp | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/cpp/ripple/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp index 4e1eecc127..cbc699a4a3 100644 --- a/src/cpp/ripple/RPCErr.cpp +++ b/src/cpp/ripple/RPCErr.cpp @@ -58,7 +58,6 @@ Json::Value rpcError(int iError, Json::Value jvResult) { rpcSRC_ACT_MISSING, "srcActMissing", "Source account does not exist." }, { rpcSRC_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed." }, { rpcSRC_UNCLAIMED, "srcUnclaimed", "Source account is not claimed." }, - { rpcSUCCESS, "success", "Success." }, { rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found." }, { rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown command." }, { rpcWRONG_SEED, "wrongSeed", "The regular key does not point as the master key." }, diff --git a/src/cpp/ripple/RPCErr.h b/src/cpp/ripple/RPCErr.h index d77cc24355..0059b35a45 100644 --- a/src/cpp/ripple/RPCErr.h +++ b/src/cpp/ripple/RPCErr.h @@ -4,8 +4,7 @@ #include "../json/value.h" enum { - rpcSUCCESS, - + rpcSUCCESS = 0, rpcBAD_SYNTAX, // Must be 1 to print usage to command line. rpcJSON_RPC, diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 9a6d6191d2..dce825aaca 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1564,7 +1564,7 @@ Json::Value RPCHandler::doLogLevel(Json::Value jvRequest) if (!jvRequest.isMember("partition")) { // set base log severity Log::setMinSeverity(sv, true); - return rpcError(rpcSUCCESS); + return Json::objectValue; } // log_level partition severity base? @@ -1576,7 +1576,7 @@ Json::Value RPCHandler::doLogLevel(Json::Value jvRequest) else if (!LogPartition::setSeverity(partition, sv)) return rpcError(rpcINVALID_PARAMS); - return rpcError(rpcSUCCESS); + return Json::objectValue; } return rpcError(rpcINVALID_PARAMS); From a17babcfaf5c621ea92b36f22d518adcf55808ff Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 6 Dec 2012 18:59:34 -0800 Subject: [PATCH 10/13] Fix small buglets. --- src/cpp/ripple/WSConnection.cpp | 102 -------------------------------- src/cpp/ripple/WSHandler.h | 12 ++-- 2 files changed, 7 insertions(+), 107 deletions(-) delete mode 100644 src/cpp/ripple/WSConnection.cpp diff --git a/src/cpp/ripple/WSConnection.cpp b/src/cpp/ripple/WSConnection.cpp deleted file mode 100644 index 136e7304d5..0000000000 --- a/src/cpp/ripple/WSConnection.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// -// WSConnection -// - -#include "Log.h" - -SETUP_LOG(); - -#include "CallRPC.h" // XXX Remove this, don't provide support for RPC syntax. -#include "WSConnection.h" -#include "WSHandler.h" - -#include "../json/reader.h" -#include "../json/writer.h" - - -//template -//WSConnection::~WSConnection() - -//template WSConnection::~WSConnection(); -//template WSConnection::~WSConnection(); - -template -void WSConnection::send(const Json::Value& jvObj) -{ - mHandler->send(mConnection, jvObj); -} -template void WSConnection::send(const Json::Value& jvObj); -template void WSConnection::send(const Json::Value& jvObj); - -// -// Utilities -// -template -Json::Value WSConnection::invokeCommand(Json::Value& jvRequest) -{ - if (!jvRequest.isMember("command")) - { - Json::Value jvResult(Json::objectValue); - - jvResult["type"] = "response"; - jvResult["result"] = "error"; - jvResult["error"] = "missingCommand"; - jvResult["command"] = jvRequest; - - return jvResult; - } - - RPCHandler mRPCHandler(&mNetwork, this); - Json::Value jvResult(Json::objectValue); - - // XXX Temporarily support RPC style commands over websocket. Remove this. - if (jvRequest.isMember("params")) - { - RPCParser rpParser; - - Json::Value jvRpcRequest = rpParser.parseCommand(jvRequest["command"].asString(), jvRequest["params"]); - - if (jvRpcRequest.isMember("error")) - { - jvResult = jvRpcRequest; - } - else - { - jvResult["result"] = mRPCHandler.doCommand( - jvRpcRequest, - mHandler->getPublic() ? RPCHandler::GUEST : RPCHandler::ADMIN); - } - } - else - { - jvResult["result"] = mRPCHandler.doCommand( - jvRequest, - mHandler->getPublic() ? RPCHandler::GUEST : RPCHandler::ADMIN); - } - - // Currently we will simply unwrap errors returned by the RPC - // API, in the future maybe we can make the responses - // consistent. - // - // Regularize result. This is duplicate code. - if (jvResult["result"].isMember("error")) - { - jvResult = jvResult["result"]; - jvResult["status"] = "error"; - jvResult["request"] = jvRequest; - - } else { - jvResult["status"] = "success"; - } - - if (jvRequest.isMember("id")) - { - jvResult["id"] = jvRequest["id"]; - } - - jvResult["type"] = "response"; - - return jvResult; -} - -// vim:ts=4 diff --git a/src/cpp/ripple/WSHandler.h b/src/cpp/ripple/WSHandler.h index 6e2b40cf2c..3a90c6d456 100644 --- a/src/cpp/ripple/WSHandler.h +++ b/src/cpp/ripple/WSHandler.h @@ -76,7 +76,7 @@ public: { boost::mutex::scoped_lock sl(mMapLock); - mMap[cpClient] = boost::make_shared(this, cpClient); + mMap[cpClient] = boost::make_shared< WSConnection >(this, cpClient); } void on_close(connection_ptr cpClient) @@ -115,7 +115,7 @@ public: } else { - boost::shared_ptr conn; + boost::shared_ptr< WSConnection > conn; { boost::mutex::scoped_lock sl(mMapLock); conn = mMap[cpClient]; @@ -136,9 +136,11 @@ public: context->set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use); - context->set_password_callback(boost::bind(&type::get_password, this)); - context->use_certificate_chain_file(theConfig.WEBSOCKET_SSL_CERT); - context->use_private_key_file(theConfig.WEBSOCKET_SSL_CERT, boost::asio::ssl::context::pem); +// context->set_password_callback(boost::bind(&type::get_password, this)); + if (!theConfig.WEBSOCKET_SSL_CERT.empty()) + context->use_private_key_file(theConfig.WEBSOCKET_SSL_CERT, boost::asio::ssl::context::pem); + if (!theConfig.WEBSOCKET_SSL_CHAIN.empty()) + context->use_certificate_chain_file(theConfig.WEBSOCKET_SSL_CHAIN); //context->use_tmp_dh_file("../../src/ssl/dh512.pem"); } catch (std::exception& e) { std::cout << e.what() << std::endl; From 2c342d984f2ed9efdb9729b5e655f33663e22e23 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 6 Dec 2012 18:59:45 -0800 Subject: [PATCH 11/13] Fixes. Template code must go in header files. --- src/cpp/ripple/WSConnection.h | 82 ++++++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/src/cpp/ripple/WSConnection.h b/src/cpp/ripple/WSConnection.h index d90ddab4f9..cd5b17a631 100644 --- a/src/cpp/ripple/WSConnection.h +++ b/src/cpp/ripple/WSConnection.h @@ -1,10 +1,14 @@ + #include "../websocketpp/src/sockets/tls.hpp" #include "../websocketpp/src/websocketpp.hpp" + +#include "../json/value.h" + #include "WSDoor.h" #include "Application.h" - #include "Log.h" #include "NetworkOPs.h" +#include "CallRPC.h" template class WSServerHandler; @@ -22,7 +26,7 @@ public: protected: typedef void (WSConnection::*doFuncPtr)(Json::Value& jvResult, Json::Value &jvRequest); - WSServerHandler* mHandler; + WSServerHandler* mHandler; connection_ptr mConnection; NetworkOPs& mNetwork; @@ -46,12 +50,78 @@ public: } // Implement overridden functions from base class: - template - void send(const Json::Value& jvObj); + void send(const Json::Value& jvObj) + { + mHandler->send(mConnection, jvObj); + } // Utilities - template - Json::Value invokeCommand(Json::Value& jvRequest); + Json::Value invokeCommand(Json::Value& jvRequest) + { + if (!jvRequest.isMember("command")) + { + Json::Value jvResult(Json::objectValue); + + jvResult["type"] = "response"; + jvResult["result"] = "error"; + jvResult["error"] = "missingCommand"; + jvResult["command"] = jvRequest; + + return jvResult; + } + + RPCHandler mRPCHandler(&mNetwork, this); + Json::Value jvResult(Json::objectValue); + + // XXX Temporarily support RPC style commands over websocket. Remove this. + if (jvRequest.isMember("params")) + { + RPCParser rpParser; + + Json::Value jvRpcRequest = rpParser.parseCommand(jvRequest["command"].asString(), jvRequest["params"]); + + if (jvRpcRequest.isMember("error")) + { + jvResult = jvRpcRequest; + } + else + { + jvResult["result"] = mRPCHandler.doCommand( + jvRpcRequest, + mHandler->getPublic() ? RPCHandler::GUEST : RPCHandler::ADMIN); + } + } + else + { + jvResult["result"] = mRPCHandler.doCommand( + jvRequest, + mHandler->getPublic() ? RPCHandler::GUEST : RPCHandler::ADMIN); + } + + // Currently we will simply unwrap errors returned by the RPC + // API, in the future maybe we can make the responses + // consistent. + // + // Regularize result. This is duplicate code. + if (jvResult["result"].isMember("error")) + { + jvResult = jvResult["result"]; + jvResult["status"] = "error"; + jvResult["request"] = jvRequest; + + } else { + jvResult["status"] = "success"; + } + + if (jvRequest.isMember("id")) + { + jvResult["id"] = jvRequest["id"]; + } + + jvResult["type"] = "response"; + + return jvResult; + } }; From dbb70e2bf908fd70d4177fa83d3163e321b93673 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 6 Dec 2012 19:14:58 -0800 Subject: [PATCH 12/13] Stop whichever endpoint is running. --- src/cpp/ripple/WSDoor.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/WSDoor.cpp b/src/cpp/ripple/WSDoor.cpp index 9bd25a2703..f5327a486d 100644 --- a/src/cpp/ripple/WSDoor.cpp +++ b/src/cpp/ripple/WSDoor.cpp @@ -111,7 +111,11 @@ void WSDoor::stop() { if (mThread) { - mEndpoint->stop(); + if (mEndpoint) + mEndpoint->stop(); + if (mSEndpoint) + mSEndpoint->stop(); + mThread->join(); } From 670763d3213dd5926a971286599ed73bfccb5a6f Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 6 Dec 2012 19:19:53 -0800 Subject: [PATCH 13/13] Need working ways to specify the key, cert, and chain. --- src/cpp/ripple/Config.cpp | 4 ++++ src/cpp/ripple/Config.h | 1 + src/cpp/ripple/WSHandler.h | 4 +++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/Config.cpp b/src/cpp/ripple/Config.cpp index 8ada3d6070..cede9d2486 100644 --- a/src/cpp/ripple/Config.cpp +++ b/src/cpp/ripple/Config.cpp @@ -41,6 +41,8 @@ #define SECTION_WEBSOCKET_PORT "websocket_port" #define SECTION_WEBSOCKET_SECURE "websocket_secure" #define SECTION_WEBSOCKET_SSL_CERT "websocket_ssl_cert" +#define SECTION_WEBSOCKET_SSL_CHAIN "websocket_ssl_chain" +#define SECTION_WEBSOCKET_SSL_KEY "websocket_ssl_key" #define SECTION_VALIDATORS "validators" #define SECTION_VALIDATORS_SITE "validators_site" @@ -263,6 +265,8 @@ void Config::load() WEBSOCKET_SECURE = boost::lexical_cast(strTemp); sectionSingleB(secConfig, SECTION_WEBSOCKET_SSL_CERT, WEBSOCKET_SSL_CERT); + sectionSingleB(secConfig, SECTION_WEBSOCKET_SSL_CHAIN, WEBSOCKET_SSL_CHAIN); + sectionSingleB(secConfig, SECTION_WEBSOCKET_SSL_KEY, WEBSOCKET_SSL_KEY); if (sectionSingleB(secConfig, SECTION_VALIDATION_SEED, strTemp)) diff --git a/src/cpp/ripple/Config.h b/src/cpp/ripple/Config.h index 50862a0c34..bd3c9c56e5 100644 --- a/src/cpp/ripple/Config.h +++ b/src/cpp/ripple/Config.h @@ -94,6 +94,7 @@ public: bool WEBSOCKET_SECURE; std::string WEBSOCKET_SSL_CERT; std::string WEBSOCKET_SSL_CHAIN; + std::string WEBSOCKET_SSL_KEY; // RPC parameters std::string RPC_IP; diff --git a/src/cpp/ripple/WSHandler.h b/src/cpp/ripple/WSHandler.h index 3a90c6d456..9c13fc4f0e 100644 --- a/src/cpp/ripple/WSHandler.h +++ b/src/cpp/ripple/WSHandler.h @@ -138,7 +138,9 @@ public: boost::asio::ssl::context::single_dh_use); // context->set_password_callback(boost::bind(&type::get_password, this)); if (!theConfig.WEBSOCKET_SSL_CERT.empty()) - context->use_private_key_file(theConfig.WEBSOCKET_SSL_CERT, boost::asio::ssl::context::pem); + context->use_certificate_file(theConfig.WEBSOCKET_SSL_CERT, boost::asio::ssl::context::pem); + if (!theConfig.WEBSOCKET_SSL_KEY.empty()) + context->use_private_key_file(theConfig.WEBSOCKET_SSL_KEY, boost::asio::ssl::context::pem); if (!theConfig.WEBSOCKET_SSL_CHAIN.empty()) context->use_certificate_chain_file(theConfig.WEBSOCKET_SSL_CHAIN); //context->use_tmp_dh_file("../../src/ssl/dh512.pem");