Merge branch 'autoclient'

This commit is contained in:
Arthur Britto
2013-02-01 19:30:36 -08:00
16 changed files with 506 additions and 321 deletions

View File

@@ -1056,7 +1056,6 @@ STAmount STAmount::setRate(uint64 rate)
// <-- saTakerGot: Actual // <-- saTakerGot: Actual
// <-- saTakerIssuerFee: Actual // <-- saTakerIssuerFee: Actual
// <-- saOfferIssuerFee: Actual // <-- saOfferIssuerFee: Actual
// <-- bRemove: remove offer it is either fullfilled or unfunded
bool STAmount::applyOffer( bool STAmount::applyOffer(
const uint32 uTakerPaysRate, const uint32 uOfferPaysRate, const uint32 uTakerPaysRate, const uint32 uOfferPaysRate,
const STAmount& saOfferRate, const STAmount& saOfferRate,

View File

@@ -67,6 +67,18 @@ public:
std::swap(mSecure, s.mSecure); 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) void async_handshake(handshake_type type, callback cbFunc)
{ {
if ((type == ssl_socket::client) || (mSecure)) if ((type == ssl_socket::client) || (mSecure))
@@ -140,7 +152,6 @@ public:
boost::asio::async_write(PlainSocket(), buffers, handler); boost::asio::async_write(PlainSocket(), buffers, handler);
} }
template <typename Allocator, typename Handler> template <typename Allocator, typename Handler>
void async_write(boost::asio::basic_streambuf<Allocator>& buffers, Handler handler) void async_write(boost::asio::basic_streambuf<Allocator>& buffers, Handler handler)
{ {
@@ -150,7 +161,6 @@ public:
boost::asio::async_write(PlainSocket(), buffers, handler); boost::asio::async_write(PlainSocket(), buffers, handler);
} }
template <typename Buf, typename Condition, typename Handler> template <typename Buf, typename Condition, typename Handler>
void async_read(const Buf& buffers, Condition cond, Handler handler) void async_read(const Buf& buffers, Condition cond, Handler handler)
{ {

View File

@@ -3,6 +3,8 @@
#include <cstdlib> #include <cstdlib>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/bind.hpp>
#include <boost/iostreams/concepts.hpp> #include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/stream.hpp> #include <boost/iostreams/stream.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
@@ -573,6 +575,12 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
return (this->*(commandsA[i].pfpFunc))(jvParams); return (this->*(commandsA[i].pfpFunc))(jvParams);
} }
// Place the async result somewhere useful.
void callRPCHandler(Json::Value* jvOutput, const Json::Value& jvInput)
{
(*jvOutput) = jvInput;
}
int commandLineRPC(const std::vector<std::string>& vCmd) int commandLineRPC(const std::vector<std::string>& vCmd)
{ {
Json::Value jvOutput; Json::Value jvOutput;
@@ -615,16 +623,21 @@ int commandLineRPC(const std::vector<std::string>& vCmd)
if (!theConfig.RPC_ADMIN_PASSWORD.empty()) if (!theConfig.RPC_ADMIN_PASSWORD.empty())
jvRequest["admin_password"] = theConfig.RPC_ADMIN_PASSWORD; jvRequest["admin_password"] = theConfig.RPC_ADMIN_PASSWORD;
jvOutput = callRPC( boost::asio::io_service isService;
theConfig.RPC_IP,
theConfig.RPC_PORT, callRPC(
theConfig.RPC_USER, isService,
theConfig.RPC_PASSWORD, theConfig.RPC_IP, theConfig.RPC_PORT,
theConfig.RPC_USER, theConfig.RPC_PASSWORD,
"", "",
jvRequest.isMember("method") // Allow parser to rewrite method. jvRequest.isMember("method") // Allow parser to rewrite method.
? jvRequest["method"].asString() ? jvRequest["method"].asString()
: vCmd[0], : vCmd[0],
jvParams); // Parsed, execute. jvParams, // Parsed, execute.
false,
boost::bind(callRPCHandler, &jvOutput, _1));
isService.run(); // This blocks until there is no more outstanding async calls.
if (jvOutput.isMember("result")) if (jvOutput.isMember("result"))
{ {
@@ -681,7 +694,67 @@ int commandLineRPC(const std::vector<std::string>& vCmd)
return nRet; 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) #define RPC_NOTIFY_MAX_BYTES 8192
#define RPC_NOTIFY_SECONDS 10
bool responseRPC(
boost::function<void(const Json::Value& jvInput)> callbackFuncP,
const boost::system::error_code& ecResult, int iStatus, const std::string& strData)
{
if (callbackFuncP)
{
// Only care about the result, if we care to deliver it callbackFuncP.
// Receive reply
if (iStatus == 401)
throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
else if ((iStatus >= 400) && (iStatus != 400) && (iStatus != 404) && (iStatus != 500)) // ?
throw std::runtime_error(strprintf("server returned HTTP error %d", iStatus));
else if (strData.empty())
throw std::runtime_error("no response from server");
// Parse reply
cLog(lsDEBUG) << "RPC reply: " << strData << std::endl;
Json::Reader reader;
Json::Value jvReply;
if (!reader.parse(strData, jvReply))
throw std::runtime_error("couldn't parse reply from server");
if (jvReply.isNull())
throw std::runtime_error("expected reply to have result, error and id properties");
Json::Value jvResult(Json::objectValue);
jvResult["result"] = jvReply;
(callbackFuncP)(jvResult);
}
return false;
}
// Build the request.
void requestRPC(const std::string& strMethod, const Json::Value& jvParams, const std::map<std::string, std::string>& mHeaders, const std::string& strPath, boost::asio::streambuf& sb, const std::string& strHost)
{
std::ostream osRequest(&sb);
osRequest <<
createHTTPPost(
strHost,
strPath,
JSONRPCRequest(strMethod, jvParams, Json::Value(1)),
mHeaders);
}
void callRPC(
boost::asio::io_service& io_service,
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,
boost::function<void(const Json::Value& jvInput)> callbackFuncP)
{ {
// Connect to localhost // Connect to localhost
if (!theConfig.QUIET) if (!theConfig.QUIET)
@@ -692,55 +765,31 @@ Json::Value callRPC(const std::string& strIp, const int iPort, const std::string
// std::cerr << "Method: " << strMethod << std::endl; // std::cerr << "Method: " << strMethod << std::endl;
} }
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");
// cLog(lsDEBUG) << "connected" << std::endl;
// HTTP basic authentication // HTTP basic authentication
std::string strUserPass64 = EncodeBase64(strUsername + ":" + strPassword); std::string strUserPass64 = EncodeBase64(strUsername + ":" + strPassword);
std::map<std::string, std::string> mapRequestHeaders; std::map<std::string, std::string> mapRequestHeaders;
mapRequestHeaders["Authorization"] = std::string("Basic ") + strUserPass64; mapRequestHeaders["Authorization"] = std::string("Basic ") + strUserPass64;
// Log(lsDEBUG) << "requesting" << std::endl;
// Send request // Send request
std::string strRequest = JSONRPCRequest(strMethod, jvParams, Json::Value(1)); // Log(lsDEBUG) << "requesting" << std::endl;
// cLog(lsDEBUG) << "send request " << strMethod << " : " << strRequest << std::endl; // cLog(lsDEBUG) << "send request " << strMethod << " : " << strRequest << std::endl;
std::string strPost = createHTTPPost(strPath, strRequest, mapRequestHeaders); HttpsClient::httpsRequest(
stream << strPost << std::flush; bSSL,
io_service,
// std::cerr << "post " << strPost << std::endl; strIp,
iPort,
// Receive reply boost::bind(
std::map<std::string, std::string> mapHeaders; &requestRPC,
std::string strReply; strMethod,
int nStatus = ReadHTTP(stream, mapHeaders, strReply); jvParams,
if (nStatus == 401) mapRequestHeaders,
throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); "/", _1, _2),
else if ((nStatus >= 400) && (nStatus != 400) && (nStatus != 404) && (nStatus != 500)) // ? RPC_NOTIFY_MAX_BYTES,
throw std::runtime_error(strprintf("server returned HTTP error %d", nStatus)); boost::posix_time::seconds(RPC_NOTIFY_SECONDS),
else if (strReply.empty()) boost::bind(&responseRPC, callbackFuncP, _1, _2, _3));
throw std::runtime_error("no response from server");
// Parse reply
cLog(lsDEBUG) << "RPC reply: " << strReply << std::endl;
Json::Reader reader;
Json::Value valReply;
if (!reader.parse(strReply, valReply))
throw std::runtime_error("couldn't parse reply from server");
if (valReply.isNull())
throw std::runtime_error("expected reply to have result, error and id properties");
return valReply;
} }
// vim:ts=4 // vim:ts=4

View File

@@ -48,8 +48,14 @@ public:
}; };
extern int commandLineRPC(const std::vector<std::string>& vCmd); extern int commandLineRPC(const std::vector<std::string>& vCmd);
extern 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& params);
extern void callRPC(
boost::asio::io_service& io_service,
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,
boost::function<void(const Json::Value& jvInput)> callbackFuncP = 0);
#endif #endif
// vim:ts=4 // vim:ts=4

View File

@@ -134,26 +134,31 @@ void Config::setup(const std::string& strConf, bool bTestNet, bool bQuiet)
if (strXdgConfigHome.empty()) if (strXdgConfigHome.empty())
{ {
// $XDG_CONFIG_HOME was not set, use default based on $HOME. // $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()) if (strXdgDataHome.empty())
{ {
// $XDG_DATA_HOME was not set, use default based on $HOME. // $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; 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); boost::filesystem::create_directories(CONFIG_DIR, ec);
if (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 // Update default values
load(); load();
@@ -164,10 +169,11 @@ void Config::setup(const std::string& strConf, bool bTestNet, bool bQuiet)
boost::filesystem::create_directories(DATA_DIR, ec); boost::filesystem::create_directories(DATA_DIR, ec);
if (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() Config::Config()
: SSL_CONTEXT(boost::asio::ssl::context::sslv23)
{ {
// //
// Defaults // Defaults

View File

@@ -2,6 +2,8 @@
#define __CONFIG__ #define __CONFIG__
#include <string> #include <string>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include "types.h" #include "types.h"
@@ -29,7 +31,7 @@
#define VALIDATORS_FILE_NAME "validators.txt" #define VALIDATORS_FILE_NAME "validators.txt"
const int DOMAIN_BYTES_MAX = 256; const int DOMAIN_BYTES_MAX = 256;
const int PUBLIC_BYTES_MAX = 2048; // Maximum bytes for an account public key. const int PUBLIC_BYTES_MAX = 33; // Maximum bytes for an account public key.
const int SYSTEM_PEER_PORT = 6561; const int SYSTEM_PEER_PORT = 6561;
const int SYSTEM_WEBSOCKET_PORT = 6562; const int SYSTEM_WEBSOCKET_PORT = 6562;
@@ -167,6 +169,8 @@ public:
uint32 SIGN_VALIDATION; uint32 SIGN_VALIDATION;
uint32 SIGN_PROPOSAL; uint32 SIGN_PROPOSAL;
boost::asio::ssl::context SSL_CONTEXT; // Generic SSL context.
Config(); Config();
int getSize(SizedItemName); int getSize(SizedItemName);

View File

@@ -12,73 +12,100 @@
#include <boost/smart_ptr/shared_ptr.hpp> #include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
#include "Config.h"
#include "Log.h"
SETUP_LOG();
using namespace boost::system; using namespace boost::system;
using namespace boost::asio; using namespace boost::asio;
HttpsClient::HttpsClient( HttpsClient::HttpsClient(
boost::asio::io_service& io_service, boost::asio::io_service& io_service,
const unsigned short port, const unsigned short port,
const std::string& strPath,
std::size_t responseMax std::size_t responseMax
) : ) :
mCtx(boost::asio::ssl::context::sslv23), mSocket(io_service, theConfig.SSL_CONTEXT),
mResolver(io_service), mResolver(io_service),
mSocketSsl(io_service, mCtx),
mResponse(responseMax), mResponse(responseMax),
mStrPath(strPath),
mPort(port), mPort(port),
mDeadline(io_service) mDeadline(io_service)
{ {
} }
void HttpsClient::httpsGet( void HttpsClient::makeGet(const std::string& strPath, boost::asio::streambuf& sb, const std::string& strHost)
std::deque<std::string> deqSites, {
boost::posix_time::time_duration timeout, std::ostream osRequest(&sb);
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) {
osRequest <<
"GET " << strPath << " HTTP/1.0\r\n"
"Host: " << strHost << "\r\n"
"Accept: */*\r\n" // YYY Do we need this line?
"Connection: close\r\n\r\n";
}
void HttpsClient::httpsRequest(
bool bSSL,
std::deque<std::string> deqSites,
boost::function<void(boost::asio::streambuf& sb, const std::string& strHost)> build,
boost::posix_time::time_duration timeout,
boost::function<bool(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete)
{
mSSL = bSSL;
mDeqSites = deqSites; mDeqSites = deqSites;
mBuild = build;
mComplete = complete; mComplete = complete;
mTimeout = timeout; mTimeout = timeout;
httpsNext(); httpsNext();
} }
void HttpsClient::httpsGet(
bool bSSL,
std::deque<std::string> deqSites,
const std::string& strPath,
boost::posix_time::time_duration timeout,
boost::function<bool(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete) {
mComplete = complete;
mTimeout = timeout;
httpsRequest(
bSSL,
deqSites,
boost::bind(&HttpsClient::makeGet, shared_from_this(), strPath, _1, _2),
timeout,
complete);
}
void HttpsClient::httpsNext() void HttpsClient::httpsNext()
{ {
// std::cerr << "Fetch: " << mDeqSites[0] << std::endl; cLog(lsTRACE) << "Fetch: " << mDeqSites[0];
boost::shared_ptr<boost::asio::ip::tcp::resolver::query> query(new boost::asio::ip::tcp::resolver::query(mDeqSites[0], boost::lexical_cast<std::string>(mPort), boost::shared_ptr<boost::asio::ip::tcp::resolver::query> query(new boost::asio::ip::tcp::resolver::query(mDeqSites[0], boost::lexical_cast<std::string>(mPort),
ip::resolver_query_base::numeric_service|ip::resolver_query_base::numeric_service)); ip::resolver_query_base::numeric_service));
mQuery = query; mQuery = query;
mCtx.set_default_verify_paths(mShutdown); mDeadline.expires_from_now(mTimeout, mShutdown);
if (mShutdown)
{
std::cerr << "set_default_verify_paths: " << mShutdown.message() << std::endl;
}
if (!mShutdown) cLog(lsTRACE) << "expires_from_now: " << mShutdown.message();
{
mDeadline.expires_from_now(mTimeout, mShutdown);
// std::cerr << "expires_from_now: " << mShutdown.message() << std::endl;
}
if (!mShutdown) if (!mShutdown)
{ {
mDeadline.async_wait( mDeadline.async_wait(
boost::bind( boost::bind(
&HttpsClient::ShandleDeadline, &HttpsClient::handleDeadline,
shared_from_this(), shared_from_this(),
boost::asio::placeholders::error)); boost::asio::placeholders::error));
} }
if (!mShutdown) if (!mShutdown)
{ {
// std::cerr << "Resolving: " << mDeqSites[0] << std::endl; cLog(lsTRACE) << "Resolving: " << mDeqSites[0];
mResolver.async_resolve(*mQuery, mResolver.async_resolve(*mQuery,
boost::bind( boost::bind(
&HttpsClient::ShandleResolve, &HttpsClient::handleResolve,
shared_from_this(), shared_from_this(),
boost::asio::placeholders::error, boost::asio::placeholders::error,
boost::asio::placeholders::iterator)); boost::asio::placeholders::iterator));
@@ -93,22 +120,20 @@ void HttpsClient::handleDeadline(const boost::system::error_code& ecResult)
if (ecResult == boost::asio::error::operation_aborted) if (ecResult == boost::asio::error::operation_aborted)
{ {
// Timer canceled because deadline no longer needed. // Timer canceled because deadline no longer needed.
// std::cerr << "Deadline cancelled." << std::endl; cLog(lsTRACE) << "Deadline cancelled.";
nothing(); // Aborter is done. nothing(); // Aborter is done.
} }
else if (ecResult) else if (ecResult)
{ {
std::cerr << "Deadline error: " << mDeqSites[0] << ": " << ecResult.message() << std::endl; cLog(lsTRACE) << "Deadline error: " << mDeqSites[0] << ": " << ecResult.message();
// Can't do anything sound. // Can't do anything sound.
abort(); abort();
} }
else else
{ {
boost::system::error_code ec_shutdown; cLog(lsTRACE) << "Deadline arrived.";
std::cerr << "Deadline arrived." << std::endl;
// Mark us as shutting down. // Mark us as shutting down.
// XXX Use our own error code. // XXX Use our own error code.
@@ -118,15 +143,24 @@ void HttpsClient::handleDeadline(const boost::system::error_code& ecResult)
mResolver.cancel(); mResolver.cancel();
// Stop the transaction. // 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)
{
cLog(lsTRACE) << "Shutdown error: " << mDeqSites[0] << ": " << ecResult.message();
}
}
void HttpsClient::handleResolve( void HttpsClient::handleResolve(
const boost::system::error_code& ecResult, const boost::system::error_code& ecResult,
boost::asio::ip::tcp::resolver::iterator itrEndpoint boost::asio::ip::tcp::resolver::iterator itrEndpoint
@@ -137,19 +171,19 @@ void HttpsClient::handleResolve(
if (mShutdown) if (mShutdown)
{ {
// std::cerr << "Resolve error: " << mDeqSites[0] << ": " << mShutdown.message() << std::endl; cLog(lsTRACE) << "Resolve error: " << mDeqSites[0] << ": " << mShutdown.message();
invokeComplete(mShutdown); invokeComplete(mShutdown);
} }
else else
{ {
// std::cerr << "Resolve complete." << std::endl; cLog(lsTRACE) << "Resolve complete.";
boost::asio::async_connect( boost::asio::async_connect(
mSocketSsl.lowest_layer(), mSocket.lowest_layer(),
itrEndpoint, itrEndpoint,
boost::bind( boost::bind(
&HttpsClient::ShandleConnect, &HttpsClient::handleConnect,
shared_from_this(), shared_from_this(),
boost::asio::placeholders::error)); boost::asio::placeholders::error));
} }
@@ -162,35 +196,38 @@ void HttpsClient::handleConnect(const boost::system::error_code& ecResult)
if (mShutdown) if (mShutdown)
{ {
std::cerr << "Connect error: " << mShutdown.message() << std::endl; cLog(lsTRACE) << "Connect error: " << mShutdown.message();
} }
if (!mShutdown) if (!mShutdown)
{ {
// std::cerr << "Connected." << std::endl; cLog(lsTRACE) << "Connected.";
mSocketSsl.set_verify_mode(boost::asio::ssl::verify_peer); mShutdown = mSocket.verify(mDeqSites[0]);
// XXX Verify semantics of RFC 2818 are what we want.
mSocketSsl.set_verify_callback(boost::asio::ssl::rfc2818_verification(mDeqSites[0]), mShutdown);
if (mShutdown) if (mShutdown)
{ {
std::cerr << "set_verify_callback: " << mDeqSites[0] << ": " << mShutdown.message() << std::endl; cLog(lsTRACE) << "set_verify_callback: " << mDeqSites[0] << ": " << mShutdown.message();
} }
} }
if (!mShutdown) if (mShutdown)
{
mSocketSsl.async_handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>::client,
boost::bind(&HttpsClient::ShandleRequest,
shared_from_this(),
boost::asio::placeholders::error));
}
else
{ {
invokeComplete(mShutdown); invokeComplete(mShutdown);
} }
else if (mSSL)
{
mSocket.async_handshake(
AutoSocket::ssl_socket::client,
boost::bind(
&HttpsClient::handleRequest,
shared_from_this(),
boost::asio::placeholders::error));
}
else
{
handleRequest(ecResult);
}
} }
void HttpsClient::handleRequest(const boost::system::error_code& ecResult) void HttpsClient::handleRequest(const boost::system::error_code& ecResult)
@@ -200,64 +237,58 @@ void HttpsClient::handleRequest(const boost::system::error_code& ecResult)
if (mShutdown) if (mShutdown)
{ {
std::cerr << "Handshake error:" << mShutdown.message() << std::endl; cLog(lsTRACE) << "Handshake error:" << mShutdown.message();
invokeComplete(mShutdown); invokeComplete(mShutdown);
} }
else else
{ {
// std::cerr << "SSL session started." << std::endl; cLog(lsTRACE) << "Session started.";
std::ostream osRequest(&mRequest); mBuild(mRequest, mDeqSites[0]);
osRequest << mSocket.async_write(
"GET " << mStrPath << " HTTP/1.0\r\n"
"Host: " << mDeqSites[0] << "\r\n"
"Accept: */*\r\n" // YYY Do we need this line?
"Connection: close\r\n\r\n";
boost::asio::async_write(
mSocketSsl,
mRequest, mRequest,
boost::bind(&HttpsClient::ShandleWrite, boost::bind(&HttpsClient::handleWrite,
shared_from_this(), shared_from_this(),
boost::asio::placeholders::error)); boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
} }
} }
void HttpsClient::handleWrite(const boost::system::error_code& ecResult) void HttpsClient::handleWrite(const boost::system::error_code& ecResult, std::size_t bytes_transferred)
{ {
if (!mShutdown) if (!mShutdown)
mShutdown = ecResult; mShutdown = ecResult;
if (mShutdown) if (mShutdown)
{ {
std::cerr << "Write error: " << mShutdown.message() << std::endl; cLog(lsTRACE) << "Write error: " << mShutdown.message();
invokeComplete(mShutdown); invokeComplete(mShutdown);
} }
else else
{ {
// std::cerr << "Wrote." << std::endl; cLog(lsTRACE) << "Wrote.";
boost::asio::async_read( mSocket.async_read(
mSocketSsl,
mResponse, mResponse,
boost::asio::transfer_all(), boost::asio::transfer_all(),
boost::bind(&HttpsClient::ShandleData, boost::bind(&HttpsClient::handleData,
shared_from_this(), shared_from_this(),
boost::asio::placeholders::error)); boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
} }
} }
void HttpsClient::handleData(const boost::system::error_code& ecResult) void HttpsClient::handleData(const boost::system::error_code& ecResult, std::size_t bytes_transferred)
{ {
if (!mShutdown) if (!mShutdown)
mShutdown = ecResult; mShutdown = ecResult;
if (mShutdown && mShutdown != boost::asio::error::eof) if (mShutdown && mShutdown != boost::asio::error::eof)
{ {
std::cerr << "Read error: " << mShutdown.message() << std::endl; cLog(lsTRACE) << "Read error: " << mShutdown.message();
invokeComplete(mShutdown); invokeComplete(mShutdown);
} }
@@ -265,13 +296,14 @@ void HttpsClient::handleData(const boost::system::error_code& ecResult)
{ {
if (mShutdown) if (mShutdown)
{ {
// std::cerr << "Complete." << std::endl; cLog(lsTRACE) << "Complete.";
nothing(); nothing();
} }
else else
{ {
// XXX According to boost example code, this is what we should expect for success. // XXX According to boost example code, this is what we should expect for success.
std::cerr << "Complete, no eof." << std::endl; cLog(lsTRACE) << "Complete, no eof.";
} }
parseData(); parseData();
@@ -279,7 +311,7 @@ void HttpsClient::handleData(const boost::system::error_code& ecResult)
} }
// Call cancel the deadline timer and invoke the completion routine. // Call cancel the deadline timer and invoke the completion routine.
void HttpsClient::invokeComplete(const boost::system::error_code& ecResult, std::string strData) void HttpsClient::invokeComplete(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)
{ {
boost::system::error_code ecCancel; boost::system::error_code ecCancel;
@@ -287,16 +319,22 @@ void HttpsClient::invokeComplete(const boost::system::error_code& ecResult, std:
if (ecCancel) if (ecCancel)
{ {
std::cerr << "Deadline cancel error: " << ecCancel.message() << std::endl; cLog(lsTRACE) << "Deadline cancel error: " << ecCancel.message();
} }
mDeqSites.pop_front(); mDeqSites.pop_front();
if (mDeqSites.empty()) bool bAgain = true;
if (mDeqSites.empty() || !ecResult)
{ {
mComplete(ecResult ? ecResult : ecCancel, strData); // ecResult: !0 = had an error, last entry
// iStatus: result, if no error
// strData: data, if no error
bAgain = mComplete && mComplete(ecResult ? ecResult : ecCancel, iStatus, strData);
} }
else
if (!mDeqSites.empty() && bAgain)
{ {
httpsNext(); httpsNext();
} }
@@ -309,21 +347,21 @@ void HttpsClient::parseData()
static boost::regex reStatus("\\`HTTP/1\\S+ (\\d{3}) .*\\'"); // HTTP/1.1 200 OK static boost::regex reStatus("\\`HTTP/1\\S+ (\\d{3}) .*\\'"); // HTTP/1.1 200 OK
static boost::regex reBody("\\`(?:.*?\\r\\n\\r\\n){1}(.*)\\'"); static boost::regex reBody("\\`(?:.*?\\r\\n\\r\\n){1}(.*)\\'");
boost::smatch smMatch; boost::smatch smStatus;
boost::smatch smBody;
bool bMatch = boost::regex_match(strData, smMatch, reStatus) // Match status code. bool bMatch = boost::regex_match(strData, smStatus, reStatus) // Match status code.
&& !smMatch[1].compare("200") && boost::regex_match(strData, smBody, reBody); // Match body.
&& boost::regex_match(strData, smMatch, reBody); // Match body.
// std::cerr << "Data:" << strData << std::endl; // std::cerr << "Data:" << strData << std::endl;
// std::cerr << "Match: " << bMatch << std::endl; // std::cerr << "Match: " << bMatch << std::endl;
// std::cerr << "Body:" << smMatch[1] << std::endl; // std::cerr << "Body:" << smBody[1] << std::endl;
if (bMatch) if (bMatch)
{ {
boost::system::error_code noErr; boost::system::error_code noErr;
invokeComplete(noErr, smMatch[1]); invokeComplete(noErr, lexical_cast_st<int>(smStatus[1]), smBody[1]);
} }
else else
{ {
@@ -333,33 +371,52 @@ void HttpsClient::parseData()
} }
void HttpsClient::httpsGet( void HttpsClient::httpsGet(
bool bSSL,
boost::asio::io_service& io_service, boost::asio::io_service& io_service,
std::deque<std::string> deqSites, std::deque<std::string> deqSites,
const unsigned short port, const unsigned short port,
const std::string& strPath, const std::string& strPath,
std::size_t responseMax, std::size_t responseMax,
boost::posix_time::time_duration timeout, boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) { boost::function<bool(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete) {
boost::shared_ptr<HttpsClient> client(new HttpsClient(io_service, port, strPath, responseMax)); boost::shared_ptr<HttpsClient> client(new HttpsClient(io_service, port, responseMax));
client->httpsGet(deqSites, timeout, complete); client->httpsGet(bSSL, deqSites, strPath, timeout, complete);
} }
void HttpsClient::httpsGet( void HttpsClient::httpsGet(
bool bSSL,
boost::asio::io_service& io_service, boost::asio::io_service& io_service,
std::string strSite, std::string strSite,
const unsigned short port, const unsigned short port,
const std::string& strPath, const std::string& strPath,
std::size_t responseMax, std::size_t responseMax,
boost::posix_time::time_duration timeout, boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) { boost::function<bool(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete) {
std::deque<std::string> deqSites(1, strSite); std::deque<std::string> deqSites(1, strSite);
boost::shared_ptr<HttpsClient> client(new HttpsClient(io_service, port, strPath, responseMax)); boost::shared_ptr<HttpsClient> client(new HttpsClient(io_service, port, responseMax));
client->httpsGet(deqSites, timeout, complete); client->httpsGet(bSSL, deqSites, strPath, timeout, complete);
}
void HttpsClient::httpsRequest(
bool bSSL,
boost::asio::io_service& io_service,
std::string strSite,
const unsigned short port,
boost::function<void(boost::asio::streambuf& sb, const std::string& strHost)> setRequest,
std::size_t responseMax,
boost::posix_time::time_duration timeout,
boost::function<bool(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete) {
std::deque<std::string> deqSites(1, strSite);
boost::shared_ptr<HttpsClient> client(new HttpsClient(io_service, port, responseMax));
client->httpsRequest(bSSL, deqSites, setRequest, timeout, complete);
} }
// vim:ts=4 // vim:ts=4

View File

@@ -11,6 +11,7 @@
#include <boost/function.hpp> #include <boost/function.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include "AutoSocket.h"
// //
// Async https client. // Async https client.
@@ -21,15 +22,15 @@ class HttpsClient : public boost::enable_shared_from_this<HttpsClient>
private: private:
typedef boost::shared_ptr<HttpsClient> pointer; typedef boost::shared_ptr<HttpsClient> pointer;
boost::asio::ssl::context mCtx; bool mSSL;
AutoSocket mSocket;
boost::asio::ip::tcp::resolver mResolver; boost::asio::ip::tcp::resolver mResolver;
boost::shared_ptr<boost::asio::ip::tcp::resolver::query> mQuery; boost::shared_ptr<boost::asio::ip::tcp::resolver::query> mQuery;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> mSocketSsl;
boost::asio::streambuf mRequest; boost::asio::streambuf mRequest;
boost::asio::streambuf mResponse; boost::asio::streambuf mResponse;
const std::string mStrPath;
const unsigned short mPort; const unsigned short mPort;
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> mComplete; boost::function<void(boost::asio::streambuf& sb, const std::string& strHost)> mBuild;
boost::function<bool(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> mComplete;
boost::asio::deadline_timer mDeadline; boost::asio::deadline_timer mDeadline;
@@ -40,65 +41,76 @@ private:
boost::posix_time::time_duration mTimeout; boost::posix_time::time_duration mTimeout;
void handleDeadline(const boost::system::error_code& ecResult); void handleDeadline(const boost::system::error_code& ecResult);
static void ShandleDeadline(pointer This, const boost::system::error_code& ecResult)
{ This->handleDeadline(ecResult); }
void handleResolve(const boost::system::error_code& ecResult, boost::asio::ip::tcp::resolver::iterator endpoint_iterator); void handleResolve(const boost::system::error_code& ecResult, boost::asio::ip::tcp::resolver::iterator endpoint_iterator);
static void ShandleResolve(pointer This, const boost::system::error_code& ecResult, boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
{ This->handleResolve(ecResult, endpoint_iterator); }
void handleConnect(const boost::system::error_code& ecResult); void handleConnect(const boost::system::error_code& ecResult);
static void ShandleConnect(pointer This, const boost::system::error_code& ecResult)
{ This->handleConnect(ecResult); }
void handleRequest(const boost::system::error_code& ecResult); void handleRequest(const boost::system::error_code& ecResult);
static void ShandleRequest(pointer This, const boost::system::error_code& ecResult)
{ This->handleRequest(ecResult); }
void handleWrite(const boost::system::error_code& ecResult); void handleWrite(const boost::system::error_code& ecResult, std::size_t bytes_transferred);
static void ShandleWrite(pointer This, const boost::system::error_code& ecResult)
{ This->handleWrite(ecResult); }
void handleData(const boost::system::error_code& ecResult); void handleData(const boost::system::error_code& ecResult, std::size_t bytes_transferred);
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 parseData();
void httpsNext(); void httpsNext();
void invokeComplete(const boost::system::error_code& ecResult, std::string strData = ""); void invokeComplete(const boost::system::error_code& ecResult, int iStatus = 0, const std::string& strData = "");
void makeGet(const std::string& strPath, boost::asio::streambuf& sb, const std::string& strHost);
public: public:
HttpsClient( HttpsClient(
boost::asio::io_service& io_service, boost::asio::io_service& io_service,
const unsigned short port, const unsigned short port,
const std::string& strPath,
std::size_t responseMax std::size_t responseMax
); );
void httpsGet( void httpsRequest(
bool bSSL,
std::deque<std::string> deqSites, std::deque<std::string> deqSites,
boost::function<void(boost::asio::streambuf& sb, const std::string& strHost)> build,
boost::posix_time::time_duration timeout, boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete); boost::function<bool(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete);
void httpsGet(
bool bSSL,
std::deque<std::string> deqSites,
const std::string& strPath,
boost::posix_time::time_duration timeout,
boost::function<bool(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete);
static void httpsGet( static void httpsGet(
bool bSSL,
boost::asio::io_service& io_service, boost::asio::io_service& io_service,
std::deque<std::string> deqSites, std::deque<std::string> deqSites,
const unsigned short port, const unsigned short port,
const std::string& strPath, const std::string& strPath,
std::size_t responseMax, std::size_t responseMax,
boost::posix_time::time_duration timeout, boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete); boost::function<bool(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete);
static void httpsGet( static void httpsGet(
bool bSSL,
boost::asio::io_service& io_service, boost::asio::io_service& io_service,
std::string strSite, std::string strSite,
const unsigned short port, const unsigned short port,
const std::string& strPath, const std::string& strPath,
std::size_t responseMax, std::size_t responseMax,
boost::posix_time::time_duration timeout, boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete); boost::function<bool(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete);
static void httpsRequest(
bool bSSL,
boost::asio::io_service& io_service,
std::string strSite,
const unsigned short port,
boost::function<void(boost::asio::streambuf& sb, const std::string& strHost)> build,
std::size_t responseMax,
boost::posix_time::time_duration timeout,
boost::function<bool(const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete);
}; };
#endif #endif
// vim:ts=4 // vim:ts=4

View File

@@ -152,7 +152,7 @@ TER OfferCreateTransactor::takeOffers(
STAmount saTakerFunds = mEngine->getNodes().accountFunds(uTakerAccountID, saTakerPays); STAmount saTakerFunds = mEngine->getNodes().accountFunds(uTakerAccountID, saTakerPays);
SLE::pointer sleOfferAccount; // Owner of offer. SLE::pointer sleOfferAccount; // Owner of offer.
if (!saOfferFunds.isPositive()) if (!saOfferFunds.isPositive()) // Includes zero.
{ {
// Offer is unfunded, possibly due to previous balance action. // Offer is unfunded, possibly due to previous balance action.
cLog(lsINFO) << "takeOffers: offer unfunded: delete"; cLog(lsINFO) << "takeOffers: offer unfunded: delete";

View File

@@ -29,7 +29,7 @@ enum http_status_type
extern std::string JSONRPCRequest(const std::string& strMethod, const Json::Value& params, extern std::string JSONRPCRequest(const std::string& strMethod, const Json::Value& params,
const Json::Value& id); const Json::Value& id);
extern std::string createHTTPPost(const std::string& strPath, const std::string& strMsg, extern std::string createHTTPPost(const std::string& strHost, const std::string& strPath, const std::string& strMsg,
const std::map<std::string, std::string>& mapRequestHeaders); const std::map<std::string, std::string>& mapRequestHeaders);
extern int ReadHTTP(std::basic_istream<char>& stream, extern int ReadHTTP(std::basic_istream<char>& stream,

View File

@@ -2304,11 +2304,8 @@ Json::Value RPCHandler::doLedgerHeader(Json::Value jvRequest)
jvResult["ledger_data"] = strHex(s.peekData()); jvResult["ledger_data"] = strHex(s.peekData());
if (mRole == ADMIN) // This information isn't verified, they should only use it if they trust us.
{ lpLedger->addJson(jvResult, 0);
// As admin, they can trust us, so we provide this information.
lpLedger->addJson(jvResult, 0);
}
return jvResult; return jvResult;
} }

View File

@@ -1,5 +1,6 @@
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include "Application.h"
#include "RPCSub.h" #include "RPCSub.h"
#include "CallRPC.h" #include "CallRPC.h"
@@ -7,7 +8,7 @@
SETUP_LOG(); SETUP_LOG();
RPCSub::RPCSub(const std::string& strUrl, const std::string& strUsername, const std::string& strPassword) RPCSub::RPCSub(const std::string& strUrl, const std::string& strUsername, const std::string& strPassword)
: mUrl(strUrl), mUsername(strUsername), mPassword(strPassword) : mUrl(strUrl), mSSL(false), mUsername(strUsername), mPassword(strPassword)
{ {
std::string strScheme; std::string strScheme;
@@ -15,17 +16,22 @@ RPCSub::RPCSub(const std::string& strUrl, const std::string& strUsername, const
{ {
throw std::runtime_error("Failed to parse url."); throw std::runtime_error("Failed to parse url.");
} }
else if (strScheme == "https")
{
mSSL = true;
}
else if (strScheme != "http") else if (strScheme != "http")
{ {
throw std::runtime_error("Only http is supported."); throw std::runtime_error("Only http and https is supported.");
} }
mSeq = 1; mSeq = 1;
if (mPort < 0) if (mPort < 0)
mPort = 80; mPort = mSSL ? 443 : 80;
} }
// XXX Could probably create a bunch of send jobs in a single get of the lock.
void RPCSub::sendThread() void RPCSub::sendThread()
{ {
Json::Value jvEvent; Json::Value jvEvent;
@@ -58,12 +64,18 @@ void RPCSub::sendThread()
// Send outside of the lock. // Send outside of the lock.
if (bSend) if (bSend)
{ {
// XXX Might not need this in a try.
try try
{ {
cLog(lsDEBUG) << boost::str(boost::format("callRPC calling: %s") % mIp); cLog(lsDEBUG) << boost::str(boost::format("callRPC calling: %s") % mIp);
// Drop result. callRPC(
(void) callRPC(mIp, mPort, mUsername, mPassword, mPath, "event", jvEvent); theApp->getIOService(),
mIp, mPort,
mUsername, mPassword,
mPath, "event",
jvEvent,
mSSL);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {

View File

@@ -15,6 +15,7 @@ class RPCSub : public InfoSub
std::string mUrl; std::string mUrl;
std::string mIp; std::string mIp;
int mPort; int mPort;
bool mSSL;
std::string mUsername; std::string mUsername;
std::string mPassword; std::string mPassword;
std::string mPath; std::string mPath;

View File

@@ -791,16 +791,23 @@ int UniqueNodeList::processValidators(const std::string& strSite, const std::str
} }
// Given a section with IPs, parse and persist it for a validator. // Given a section with IPs, parse and persist it for a validator.
void UniqueNodeList::responseIps(const std::string& strSite, const RippleAddress& naNodePublic, const boost::system::error_code& err, const std::string& strIpsFile) bool UniqueNodeList::responseIps(const std::string& strSite, const RippleAddress& naNodePublic, const boost::system::error_code& err, int iStatus, const std::string& strIpsFile)
{ {
if (!err) bool bReject = !err && iStatus != 200;
{
section secFile = ParseSection(strIpsFile, true);
processIps(strSite, naNodePublic, sectionEntries(secFile, SECTION_IPS)); if (!bReject)
{
if (!err)
{
section secFile = ParseSection(strIpsFile, true);
processIps(strSite, naNodePublic, sectionEntries(secFile, SECTION_IPS));
}
fetchFinish();
} }
fetchFinish(); return bReject;
} }
// Process section [ips_url]. // Process section [ips_url].
@@ -820,13 +827,14 @@ void UniqueNodeList::getIpsUrl(const RippleAddress& naNodePublic, section secSit
&& strScheme == "https") && strScheme == "https")
{ {
HttpsClient::httpsGet( HttpsClient::httpsGet(
true,
theApp->getIOService(), theApp->getIOService(),
strDomain, strDomain,
443, 443,
strPath, strPath,
NODE_FILE_BYTES_MAX, NODE_FILE_BYTES_MAX,
boost::posix_time::seconds(NODE_FETCH_SECONDS), boost::posix_time::seconds(NODE_FETCH_SECONDS),
boost::bind(&UniqueNodeList::responseIps, this, strDomain, naNodePublic, _1, _2)); boost::bind(&UniqueNodeList::responseIps, this, strDomain, naNodePublic, _1, _2, _3));
} }
else else
{ {
@@ -835,16 +843,23 @@ void UniqueNodeList::getIpsUrl(const RippleAddress& naNodePublic, section secSit
} }
// After fetching a ripple.txt from a web site, given a section with validators, parse and persist it. // After fetching a ripple.txt from a web site, given a section with validators, parse and persist it.
void UniqueNodeList::responseValidators(const std::string& strValidatorsUrl, const RippleAddress& naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, const std::string& strValidatorsFile) bool UniqueNodeList::responseValidators(const std::string& strValidatorsUrl, const RippleAddress& naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, int iStatus, const std::string& strValidatorsFile)
{ {
if (!err) bool bReject = !err && iStatus != 200;
{
section secFile = ParseSection(strValidatorsFile, true);
processValidators(strSite, strValidatorsUrl, naNodePublic, vsValidator, sectionEntries(secFile, SECTION_VALIDATORS)); if (!bReject)
{
if (!err)
{
section secFile = ParseSection(strValidatorsFile, true);
processValidators(strSite, strValidatorsUrl, naNodePublic, vsValidator, sectionEntries(secFile, SECTION_VALIDATORS));
}
getIpsUrl(naNodePublic, secSite);
} }
getIpsUrl(naNodePublic, secSite); return bReject;
} }
// Process section [validators_url]. // Process section [validators_url].
@@ -863,13 +878,14 @@ void UniqueNodeList::getValidatorsUrl(const RippleAddress& naNodePublic, section
&& strScheme == "https") && strScheme == "https")
{ {
HttpsClient::httpsGet( HttpsClient::httpsGet(
true,
theApp->getIOService(), theApp->getIOService(),
strDomain, strDomain,
443, 443,
strPath, strPath,
NODE_FILE_BYTES_MAX, NODE_FILE_BYTES_MAX,
boost::posix_time::seconds(NODE_FETCH_SECONDS), boost::posix_time::seconds(NODE_FETCH_SECONDS),
boost::bind(&UniqueNodeList::responseValidators, this, strValidatorsUrl, naNodePublic, secSite, strDomain, _1, _2)); boost::bind(&UniqueNodeList::responseValidators, this, strValidatorsUrl, naNodePublic, secSite, strDomain, _1, _2, _3));
} }
else else
{ {
@@ -905,118 +921,125 @@ void UniqueNodeList::processFile(const std::string& strDomain, const RippleAddre
} }
// Given a ripple.txt, process it. // Given a ripple.txt, process it.
void UniqueNodeList::responseFetch(const std::string& strDomain, const boost::system::error_code& err, const std::string& strSiteFile) bool UniqueNodeList::responseFetch(const std::string& strDomain, const boost::system::error_code& err, int iStatus, const std::string& strSiteFile)
{ {
section secSite = ParseSection(strSiteFile, true); bool bReject = !err && iStatus != 200;
bool bGood = !err;
if (bGood) if (!bReject)
{ {
cLog(lsTRACE) << boost::format("Validator: '%s' received " NODE_FILE_NAME ".") % strDomain; section secSite = ParseSection(strSiteFile, true);
} bool bGood = !err;
else
{
cLog(lsTRACE)
<< boost::format("Validator: '%s' unable to retrieve " NODE_FILE_NAME ": %s")
% strDomain
% err.message();
}
// if (bGood)
// Verify file domain
//
std::string strSite;
if (bGood && !sectionSingleB(secSite, SECTION_DOMAIN, strSite))
{
bGood = false;
cLog(lsTRACE)
<< boost::format("Validator: '%s' bad " NODE_FILE_NAME " missing single entry for " SECTION_DOMAIN ".")
% strDomain;
}
if (bGood && strSite != strDomain)
{
bGood = false;
cLog(lsTRACE)
<< boost::format("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_DOMAIN " does not match: %s")
% strDomain
% strSite;
}
//
// Process public key
//
std::string strNodePublicKey;
if (bGood && !sectionSingleB(secSite, SECTION_PUBLIC_KEY, strNodePublicKey))
{
// Bad [validation_public_key] section.
bGood = false;
cLog(lsTRACE)
<< boost::format("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_PUBLIC_KEY " does not have single entry.")
% strDomain;
}
RippleAddress naNodePublic;
if (bGood && !naNodePublic.setNodePublic(strNodePublicKey))
{
// Bad public key.
bGood = false;
cLog(lsTRACE)
<< boost::format("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_PUBLIC_KEY " is bad: ")
% strDomain
% strNodePublicKey;
}
if (bGood)
{
// cLog(lsTRACE) << boost::format("naNodePublic: '%s'") % naNodePublic.humanNodePublic();
seedDomain sdCurrent;
bool bFound = getSeedDomains(strDomain, sdCurrent);
assert(bFound);
uint256 iSha256 = Serializer::getSHA512Half(strSiteFile);
bool bChangedB = sdCurrent.iSha256 != iSha256;
sdCurrent.strDomain = strDomain;
// XXX If the node public key is changing, delete old public key information?
// XXX Only if no other refs to keep it arround, other wise we have an attack vector.
sdCurrent.naPublicKey = naNodePublic;
// cLog(lsTRACE) << boost::format("sdCurrent.naPublicKey: '%s'") % sdCurrent.naPublicKey.humanNodePublic();
sdCurrent.tpFetch = boost::posix_time::second_clock::universal_time();
sdCurrent.iSha256 = iSha256;
setSeedDomains(sdCurrent, true);
if (bChangedB)
{ {
cLog(lsTRACE) << boost::format("Validator: '%s' processing new " NODE_FILE_NAME ".") % strDomain; cLog(lsTRACE) << boost::format("Validator: '%s' received " NODE_FILE_NAME ".") % strDomain;
processFile(strDomain, naNodePublic, secSite);
} }
else else
{ {
cLog(lsTRACE) << boost::format("Validator: '%s' no change for " NODE_FILE_NAME ".") % strDomain; cLog(lsTRACE)
<< boost::format("Validator: '%s' unable to retrieve " NODE_FILE_NAME ": %s")
% strDomain
% err.message();
}
//
// Verify file domain
//
std::string strSite;
if (bGood && !sectionSingleB(secSite, SECTION_DOMAIN, strSite))
{
bGood = false;
cLog(lsTRACE)
<< boost::format("Validator: '%s' bad " NODE_FILE_NAME " missing single entry for " SECTION_DOMAIN ".")
% strDomain;
}
if (bGood && strSite != strDomain)
{
bGood = false;
cLog(lsTRACE)
<< boost::format("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_DOMAIN " does not match: %s")
% strDomain
% strSite;
}
//
// Process public key
//
std::string strNodePublicKey;
if (bGood && !sectionSingleB(secSite, SECTION_PUBLIC_KEY, strNodePublicKey))
{
// Bad [validation_public_key] section.
bGood = false;
cLog(lsTRACE)
<< boost::format("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_PUBLIC_KEY " does not have single entry.")
% strDomain;
}
RippleAddress naNodePublic;
if (bGood && !naNodePublic.setNodePublic(strNodePublicKey))
{
// Bad public key.
bGood = false;
cLog(lsTRACE)
<< boost::format("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_PUBLIC_KEY " is bad: ")
% strDomain
% strNodePublicKey;
}
if (bGood)
{
// cLog(lsTRACE) << boost::format("naNodePublic: '%s'") % naNodePublic.humanNodePublic();
seedDomain sdCurrent;
bool bFound = getSeedDomains(strDomain, sdCurrent);
assert(bFound);
uint256 iSha256 = Serializer::getSHA512Half(strSiteFile);
bool bChangedB = sdCurrent.iSha256 != iSha256;
sdCurrent.strDomain = strDomain;
// XXX If the node public key is changing, delete old public key information?
// XXX Only if no other refs to keep it arround, other wise we have an attack vector.
sdCurrent.naPublicKey = naNodePublic;
// cLog(lsTRACE) << boost::format("sdCurrent.naPublicKey: '%s'") % sdCurrent.naPublicKey.humanNodePublic();
sdCurrent.tpFetch = boost::posix_time::second_clock::universal_time();
sdCurrent.iSha256 = iSha256;
setSeedDomains(sdCurrent, true);
if (bChangedB)
{
cLog(lsTRACE) << boost::format("Validator: '%s' processing new " NODE_FILE_NAME ".") % strDomain;
processFile(strDomain, naNodePublic, secSite);
}
else
{
cLog(lsTRACE) << boost::format("Validator: '%s' no change for " NODE_FILE_NAME ".") % strDomain;
fetchFinish();
}
}
else
{
// Failed: Update
// XXX If we have public key, perhaps try look up in CAS?
fetchFinish(); fetchFinish();
} }
} }
else
{
// Failed: Update
// XXX If we have public key, perhaps try look up in CAS? return bReject;
fetchFinish();
}
} }
// Get the ripple.txt and process it. // Get the ripple.txt and process it.
@@ -1033,13 +1056,14 @@ void UniqueNodeList::fetchProcess(std::string strDomain)
deqSites.push_back(strDomain); deqSites.push_back(strDomain);
HttpsClient::httpsGet( HttpsClient::httpsGet(
true,
theApp->getIOService(), theApp->getIOService(),
deqSites, deqSites,
443, 443,
NODE_FILE_PATH, NODE_FILE_PATH,
NODE_FILE_BYTES_MAX, NODE_FILE_BYTES_MAX,
boost::posix_time::seconds(NODE_FETCH_SECONDS), boost::posix_time::seconds(NODE_FETCH_SECONDS),
boost::bind(&UniqueNodeList::responseFetch, this, strDomain, _1, _2)); boost::bind(&UniqueNodeList::responseFetch, this, strDomain, _1, _2, _3));
} }
void UniqueNodeList::fetchTimerHandler(const boost::system::error_code& err) void UniqueNodeList::fetchTimerHandler(const boost::system::error_code& err)
@@ -1549,18 +1573,25 @@ bool UniqueNodeList::nodeLoad(boost::filesystem::path pConfig)
return true; return true;
} }
void UniqueNodeList::validatorsResponse(const boost::system::error_code& err, std::string strResponse) bool UniqueNodeList::validatorsResponse(const boost::system::error_code& err, int iStatus, std::string strResponse)
{ {
cLog(lsTRACE) << "Fetch '" VALIDATORS_FILE_NAME "' complete."; bool bReject = !err && iStatus != 200;
if (!err) if (!bReject)
{ {
nodeProcess("network", strResponse, theConfig.VALIDATORS_SITE); cLog(lsTRACE) << "Fetch '" VALIDATORS_FILE_NAME "' complete.";
}
else if (!err)
{ {
cLog(lsWARNING) << "Error: " << err.message(); nodeProcess("network", strResponse, theConfig.VALIDATORS_SITE);
}
else
{
cLog(lsWARNING) << "Error: " << err.message();
}
} }
return bReject;
} }
void UniqueNodeList::nodeNetwork() void UniqueNodeList::nodeNetwork()
@@ -1568,13 +1599,14 @@ void UniqueNodeList::nodeNetwork()
if (!theConfig.VALIDATORS_SITE.empty()) if (!theConfig.VALIDATORS_SITE.empty())
{ {
HttpsClient::httpsGet( HttpsClient::httpsGet(
true,
theApp->getIOService(), theApp->getIOService(),
theConfig.VALIDATORS_SITE, theConfig.VALIDATORS_SITE,
443, 443,
theConfig.VALIDATORS_URI, theConfig.VALIDATORS_URI,
VALIDATORS_FILE_BYTES_MAX, VALIDATORS_FILE_BYTES_MAX,
boost::posix_time::seconds(VALIDATORS_FETCH_SECONDS), boost::posix_time::seconds(VALIDATORS_FETCH_SECONDS),
boost::bind(&UniqueNodeList::validatorsResponse, this, _1, _2)); boost::bind(&UniqueNodeList::validatorsResponse, this, _1, _2, _3));
} }
} }

View File

@@ -98,7 +98,7 @@ private:
bool scoreRound(std::vector<scoreNode>& vsnNodes); bool scoreRound(std::vector<scoreNode>& vsnNodes);
void responseFetch(const std::string& strDomain, const boost::system::error_code& err, const std::string& strSiteFile); bool responseFetch(const std::string& strDomain, const boost::system::error_code& err, int iStatus, const std::string& strSiteFile);
boost::posix_time::ptime mtpScoreNext; // When to start scoring. boost::posix_time::ptime mtpScoreNext; // When to start scoring.
boost::posix_time::ptime mtpScoreStart; // Time currently started scoring. boost::posix_time::ptime mtpScoreStart; // Time currently started scoring.
@@ -122,8 +122,8 @@ private:
void getValidatorsUrl(const RippleAddress& naNodePublic, section secSite); void getValidatorsUrl(const RippleAddress& naNodePublic, section secSite);
void getIpsUrl(const RippleAddress& naNodePublic, section secSite); void getIpsUrl(const RippleAddress& naNodePublic, section secSite);
void responseIps(const std::string& strSite, const RippleAddress& naNodePublic, const boost::system::error_code& err, const std::string& strIpsFile); bool responseIps(const std::string& strSite, const RippleAddress& naNodePublic, const boost::system::error_code& err, int iStatus, const std::string& strIpsFile);
void responseValidators(const std::string& strValidatorsUrl, const RippleAddress& naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, const std::string& strValidatorsFile); bool responseValidators(const std::string& strValidatorsUrl, const RippleAddress& naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, int iStatus, const std::string& strValidatorsFile);
void processIps(const std::string& strSite, const RippleAddress& naNodePublic, section::mapped_type* pmtVecStrIps); void processIps(const std::string& strSite, const RippleAddress& naNodePublic, section::mapped_type* pmtVecStrIps);
int processValidators(const std::string& strSite, const std::string& strValidatorsSrc, const RippleAddress& naNodePublic, validatorSource vsWhy, section::mapped_type* pmtVecStrValidators); int processValidators(const std::string& strSite, const std::string& strValidatorsSrc, const RippleAddress& naNodePublic, validatorSource vsWhy, section::mapped_type* pmtVecStrValidators);
@@ -136,7 +136,7 @@ private:
bool getSeedNodes(const RippleAddress& naNodePublic, seedNode& dstSeedNode); bool getSeedNodes(const RippleAddress& naNodePublic, seedNode& dstSeedNode);
void setSeedNodes(const seedNode& snSource, bool bNext); void setSeedNodes(const seedNode& snSource, bool bNext);
void validatorsResponse(const boost::system::error_code& err, std::string strResponse); bool validatorsResponse(const boost::system::error_code& err, int iStatus, const std::string strResponse);
void nodeProcess(const std::string& strSite, const std::string& strValidators, const std::string& strSource); void nodeProcess(const std::string& strSite, const std::string& strValidators, const std::string& strSource);
public: public:

View File

@@ -39,15 +39,15 @@ Json::Value JSONRPCError(int code, const std::string& message)
// and to be compatible with other JSON-RPC implementations. // and to be compatible with other JSON-RPC implementations.
// //
std::string createHTTPPost(const std::string& strPath, const std::string& strMsg, const std::map<std::string, std::string>& mapRequestHeaders) std::string createHTTPPost(const std::string& strHost, const std::string& strPath, const std::string& strMsg, const std::map<std::string, std::string>& mapRequestHeaders)
{ {
std::ostringstream s; std::ostringstream s;
s << "POST " s << "POST "
<< (strPath.empty() ? "/" : strPath) << (strPath.empty() ? "/" : strPath)
<< " HTTP/1.1\r\n" << " HTTP/1.0\r\n"
<< "User-Agent: " SYSTEM_NAME "-json-rpc/" << FormatFullVersion() << "\r\n" << "User-Agent: " SYSTEM_NAME "-json-rpc/" << FormatFullVersion() << "\r\n"
<< "Host: 127.0.0.1\r\n" << "Host: " << strHost << "\r\n"
<< "Content-Type: application/json\r\n" << "Content-Type: application/json\r\n"
<< "Content-Length: " << strMsg.size() << "\r\n" << "Content-Length: " << strMsg.size() << "\r\n"
<< "Accept: application/json\r\n"; << "Accept: application/json\r\n";