diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj index e617f09ff5..b78571b869 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj +++ b/Builds/VisualStudio2013/RippleD.vcxproj @@ -1811,13 +1811,6 @@ - - - - True - - - @@ -2024,6 +2017,8 @@ + + True @@ -2190,10 +2185,10 @@ True - + True - + True @@ -2203,12 +2198,12 @@ + + - - @@ -2371,37 +2366,6 @@ - - True - - - - - - - - - True - - - True - - - True - - - - - - - - - - - - - True - True @@ -2460,9 +2424,6 @@ True - - True - True @@ -2471,9 +2432,6 @@ True - - True - True @@ -2481,16 +2439,12 @@ - - - - @@ -2615,11 +2569,6 @@ - - True - - - True @@ -3052,6 +3001,62 @@ + + + + + + True + + + + + True + + + + + + + + + True + + + True + + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + True + + + @@ -3185,8 +3190,6 @@ - - ..\..\src\hyperleveldb;..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) ..\..\src\hyperleveldb;..\..\src\snappy\config;..\..\src\snappy\snappy;%(AdditionalIncludeDirectories) @@ -3233,6 +3236,8 @@ + + diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters index c67f11064b..f144100b6a 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters @@ -364,15 +364,6 @@ {1906827A-5174-9515-DC11-9D11C511E0CB} - - {8601C61D-413C-725E-C9E6-BD4F97E40032} - - - {43D68742-4714-D103-EE00-EB10BD045FB6} - - - {AA0D98CC-99E6-61CE-86D7-35156DC4EE55} - {BEDCC703-A2C8-FF25-7E1E-3471BD39ED98} @@ -436,6 +427,15 @@ {93AC3675-D183-4DB4-021E-8F4CA1586866} + + {8A61DBF7-69CB-9043-8312-D44C40EC6AE9} + + + {B0649154-4449-1172-FF4F-9F7A46908774} + + + {9BA46DD5-4B0C-3C1D-6F16-93DE8A9B7313} + {CB0AC82D-AEA3-F41C-847F-D6ECA4971891} @@ -2703,15 +2703,6 @@ ripple\app\main - - ripple\app\main - - - ripple\app\main - - - ripple\app\main - ripple\app\main @@ -2958,6 +2949,9 @@ ripple\app\shamap + + ripple\app\shamap + ripple\app\transactors @@ -3153,10 +3147,10 @@ ripple\common\impl - + ripple\common\impl - + ripple\common\impl @@ -3168,15 +3162,15 @@ ripple\common + + ripple\common + ripple\common ripple\common - - ripple\common - ripple\common @@ -3378,45 +3372,6 @@ ripple\data\utility - - ripple\http\impl - - - ripple\http\impl - - - ripple\http\impl - - - ripple\http\impl - - - ripple\http\impl - - - ripple\http\impl - - - ripple\http\impl - - - ripple\http\impl - - - ripple\http\impl - - - ripple\http\impl - - - ripple\http - - - ripple\http - - - ripple\http\tests - ripple\json\impl @@ -3489,9 +3444,6 @@ ripple\net\impl - - ripple\net\impl - ripple\net\impl @@ -3501,9 +3453,6 @@ ripple\net\impl - - ripple\net\impl - ripple\net\impl @@ -3513,9 +3462,6 @@ ripple\net - - ripple\net - ripple\net @@ -3525,9 +3471,6 @@ ripple\net - - ripple\net - ripple\net @@ -3678,12 +3621,6 @@ ripple\overlay\impl - - ripple\overlay\impl - - - ripple\overlay\impl - ripple\overlay\impl @@ -4179,6 +4116,81 @@ ripple\rpc + + ripple\server + + + ripple\server + + + ripple\server\impl + + + ripple\server\impl + + + ripple\server\impl + + + ripple\server\impl + + + ripple\server\impl + + + ripple\server\impl + + + ripple\server\impl + + + ripple\server\impl + + + ripple\server\impl + + + ripple\server\impl + + + ripple\server\impl + + + ripple\server\impl + + + ripple\server + + + ripple\server + + + ripple\server + + + ripple\server + + + ripple\server + + + ripple\server + + + ripple\server + + + ripple\server + + + ripple\server + + + ripple\server\tests + + + ripple\server + ripple\sitefiles\impl @@ -4359,9 +4371,6 @@ ripple\unity - - ripple\unity - ripple\unity @@ -4419,6 +4428,9 @@ ripple\unity + + ripple\unity + ripple\unity diff --git a/SConstruct b/SConstruct index f9e9601213..09c63f2f48 100644 --- a/SConstruct +++ b/SConstruct @@ -571,7 +571,6 @@ for toolchain in all_toolchains: 'common.cpp', 'core.cpp', 'data.cpp', - 'http.cpp', 'json.cpp', 'net.cpp', 'overlay.cpp', @@ -580,6 +579,7 @@ for toolchain in all_toolchains: 'ripple.proto.cpp', 'resource.cpp', 'rpcx.cpp', + 'server.cpp', 'sitefiles.cpp', 'sslutil.cpp', 'types.cpp', diff --git a/doc/rippled-example.cfg b/doc/rippled-example.cfg index a7a3c614c0..be613f2af0 100644 --- a/doc/rippled-example.cfg +++ b/doc/rippled-example.cfg @@ -6,23 +6,23 @@ # # Contents # -# 1. Peer Networking +# 1. Server # -# 2. Websocket Networking +# 2. Peer Protocol # -# 3. RPC Networking +# 3. SMS Gateway # -# 4. SMS Gateway +# 4. Ripple Protocol # -# 5. Ripple Protocol +# 5. HTTPS Client # -# 6. HTTPS Client +# 6. Database # -# 7. Database +# 7. Diagnostics # -# 8. Diagnostics +# 8. Voting # -# 9. Voting +# 9. Example Settings # #------------------------------------------------------------------------------- # @@ -83,15 +83,196 @@ # #------------------------------------------------------------------------------- # -# 1. Peer Networking +# 1. Server # -#------------------- +#---------- +# +# +# +# rippled offers various server protocols to clients making inbound +# connections. The listening ports rippled uses are "universal" ports +# which may be configured to handshake in one or more of the available +# supported protocols. These universal ports simplify administration: +# A single open port can be used for multiple protocols. +# +# NOTE At least one server port must be defined in order +# to accept incoming network connections. +# +# +# [server] +# +# A list of port names and key/value pairs. A port name must start with a +# letter and contain only letters and numbers. The name is not case-sensitive. +# For each name in this list, rippled will look for a configuration file +# section with the same name and use it to create a listening port. The +# name is informational only; the choice of name does not affect the function +# of the listening port. +# +# Key/value pairs specified in this section are optional, and apply to all +# listening ports unless the port overrides the value in its section. They +# may be considered default values. +# +# Suggestion: +# +# To avoid a conflict with port names and future configuration sections, +# we recommend prepending "port_" to the port name. This prefix is not +# required, but suggested. +# +# This example defines two ports with different port numbers and settings: +# +# [server] +# port_public +# port_private +# port = 80 +# +# [port_public] +# ip=0.0.0.0 +# port = 443 +# protocol=peer,https +# +# [port_private] +# ip=127.0.0.1 +# protocol=http +# +# When rippled is used as a command line client (for example, issuing a +# server stop command), the first port advertising the http or https +# protocol will be used to make the connection. +# +# +# +# [] +# +# A series of key/value pairs that define the settings for the port with +# the corresponding name. These keys are possible: +# +# ip = +# +# Required. Determines the IP address of the network interface to bind +# to. To bind to all available interfaces, uses 0.0.0.0 +# +# port = +# +# Required. Sets the port number to use for this port. +# +# protocol = [ http, https, peer ] +# +# Required. A comma-separated list of protocols to support: +# +# http JSON-RPC over HTTP +# https JSON-RPC over HTTPS +# ws Websockets +# wss Secure Websockets +# peer Peer Protocol +# +# Restrictions: +# +# Only one port may be configured to support the peer protocol. +# A port cannot have websocket and non websocket protocols at the +# same time. It is possible have both Websockets and Secure Websockets +# together in one port. +# +# NOTE If no ports support the peer protocol, rippled cannot +# receive incoming peer connections or become a superpeer. +# +# user = +# password = +# +# When set, these credentials will be required on HTTP/S requests. +# The credentials must be provided using HTTP's Basic Authentication +# headers. If either or both fields are empty, then no credentials are +# required. IP address restrictions, if any, will be checked in addition +# to the credentials specified here. +# +# When acting in the client role, rippled will supply these credentials +# using HTTP's Basic Authentication headers when making outbound HTTP/S +# requests. +# +# admin = no | allow +# +# Controls whether or not administrative commands are allowed. These +# commands may be issued over http, https, ws, or wss if configured +# on the port. If unspecified, the default is to not allow +# administrative commands. +# +# admin_user = +# admin_password = +# +# When set, clients must provide these credentials in the submitted +# JSON for any administrative command requests submitted to the HTTP/S, +# WS, or WSS protocol interfaces. If administrative commands are +# disabled for a port, these credentials have no effect. +# +# When acting in the client role, rippled will supply these credentials +# in the submitted JSON for any administrative command requests when +# invoking JSON-RPC commands on remote servers. +# +# ssl_key = +# ssl_cert = +# ssl_chain = +# +# Use the specified files when configuring SSL on the port. +# +# NOTE If no files are specified and secure protocols are selected, +# rippled will generate an internal self-signed certificate. +# +# The files have these meanings: +# +# ssl_key +# +# Specifies the filename holding the SSL key in PEM format. +# +# ssl_cert +# +# Specifies the path to the SSL certificate file in PEM format. +# This is not needed if the chain includes it. +# +# ssl_chain +# +# If you need a certificate chain, specify the path to the +# certificate chain here. The chain may include the end certificate. +# +# +# +# [rpc_admin_allow] +# +# Specify a list of IP addresses allowed to have admin access. One per line. +# If you want to test the output of non-admin commands add this section and +# just put an ip address not under your control. +# Defaults to 127.0.0.1. +# +# +# +# [rpc_startup] +# +# Specify a list of RPC commands to run at startup. +# +# Examples: +# { "command" : "server_info" } +# { "command" : "log_level", "partition" : "ripplecalc", "severity" : "trace" } +# +# +# +# [websocket_ping_frequency] +# +# +# +# The amount of time to wait in seconds, before sending a websocket 'ping' +# message. Ping messages are used to determine if the remote end of the +# connection is no longer available. +# +# +# +#------------------------------------------------------------------------------- +# +# 2. Peer Protocol +# +#----------------- # # These settings control security and access attributes of the Peer to Peer -# server section of the rippled process. Peer Networking implements the +# server section of the rippled process. Peer Protocol implements the # Ripple Payment protocol. It is over peer connections that transactions -# and validations are passed from to machine to machine, to make up the -# components of closed ledgers. +# and validations are passed from to machine to machine, to determine the +# contents of validated ledgers. # # # @@ -130,21 +311,6 @@ # # # -# [peer_ip] -# -# IP address or domain to bind to allow external connections from peers. -# Defaults to not binding, which disallows external connections from peers. -# -# Examples: 0.0.0.0 - Bind on all interfaces. -# -# -# -# [peer_port] -# -# If peer_ip is supplied, corresponding port to bind to for peer connections. -# -# -# # [peer_private] # # 0 or 1. @@ -238,243 +404,7 @@ # #------------------------------------------------------------------------------- # -# 2. Websocket Networking -# -#------------------------ -# -# These settings control security and access attributes of the Websocket -# server section of the rippled process, primarily used to service -# client requests and backend applications. -# -# -# -# [websocket_public_ip] -# -# IP address or domain to bind to allow untrusted connections from clients. -# In the future, this option will go away and the peer_ip will accept -# websocket client connections. -# -# Examples: 0.0.0.0 - Bind on all interfaces. -# 127.0.0.1 - Bind on localhost interface. Only local programs may connect. -# -# -# -# [websocket_public_port] -# -# Port to bind to allow untrusted connections from clients. In the future, -# this option will go away and the peer_ip will accept websocket client -# connections. -# -# -# -# [websocket_public_secure] -# -# 0, 1 or 2. -# 0: Provide ws service for websocket_public_ip/websocket_public_port. -# 1: Provide both ws and wss service for websocket_public_ip/websocket_public_port. [default] -# 2: Provide wss service only for websocket_public_ip/websocket_public_port. -# -# Browser pages like the Ripple client will not be able to connect to a secure -# websocket connection if a self-signed certificate is used. As the Ripple -# reference client currently shares secrets with its server, this should be -# enabled. -# -# -# -# [websocket_ping_frequency] -# -# -# -# The amount of time to wait in seconds, before sending a websocket 'ping' -# message. Ping messages are used to determine if the remote end of the -# connection is no longer available. -# -# -# -# [websocket_ip] -# -# IP address or domain to bind to allow trusted ADMIN connections from backend -# applications. -# -# Examples: 0.0.0.0 - Bind on all interfaces. -# 127.0.0.1 - Bind on localhost interface. Only local programs may connect. -# -# -# -# [websocket_port] -# -# Port to bind to allow trusted ADMIN connections from backend applications. -# -# -# -# [websocket_secure] -# -# 0, 1, or 2. -# 0: Provide ws service only for websocket_ip/websocket_port. [default] -# 1: Provide ws and wss service for websocket_ip/websocket_port -# 2: Provide wss service for websocket_ip/websocket_port. -# -# -# -# [websocket_ssl_cert] -# -# Specify the path to the SSL certificate file in PEM format. -# This is not needed if the chain includes it. -# -# -# -# [websocket_ssl_chain] -# -# If you need a certificate chain, specify the path to the certificate chain -# here. The chain may include the end certificate. -# -# -# -# [websocket_ssl_key] -# -# Specify the filename holding the SSL key in PEM format. -# -# -# -#------------------------------------------------------------------------------- -# -# 3. RPC Networking -# -#------------------ -# -# This group of settings configures security and access attributes of the -# RPC server section of the rippled process, used to service both local -# and optional remote clients. -# -# -# -# [rpc_allow_remote] -# -# 0 or 1. -# -# 0: Allow RPC connections only from 127.0.0.1. [default] -# 1: Allow RPC connections from any IP. -# -# -# -# [rpc_admin_allow] -# -# Specify a list of IP addresses allowed to have admin access. One per line. -# If you want to test the output of non-admin commands add this section and -# just put an ip address not under your control. -# Defaults to 127.0.0.1. -# -# -# -# [rpc_admin_user] -# -# As a server, require this as the admin user to be specified. Also, require -# rpc_admin_user and rpc_admin_password to be checked for RPC admin functions. -# The request must specify these as the admin_user and admin_password in the -# request object. -# -# As a client, supply this to the server in the request object. -# -# -# -# [rpc_admin_password] -# -# As a server, require this as the admin password to be specified. Also, -# require rpc_admin_user and rpc_admin_password to be checked for RPC admin -# functions. The request must specify these as the admin_user and -# admin_password in the request object. -# -# As a client, supply this to the server in the request object. -# -# -# -# [rpc_ip] -# -# IP address or domain to bind to allow insecure RPC connections. -# Defaults to not binding, which disallows RPC connections. -# -# -# -# [rpc_port] -# -# If rpc_ip is supplied, corresponding port to bind to for peer connections. -# -# -# -# [rpc_user] -# -# As a server, require this user to be specified and require rpc_password to -# be checked for RPC access via the rpc_ip and rpc_port. The user and password -# must be specified via HTTP's basic authentication method. -# As a client, supply this to the server via HTTP's basic authentication -# method. -# -# -# -# [rpc_password] -# -# As a server, require this password to be specified and require rpc_user to -# be checked for RPC access via the rpc_ip and rpc_port. The user and password -# must be specified via HTTP's basic authentication method. -# As a client, supply this to the server via HTTP's basic authentication -# method. -# -# -# -# [rpc_startup] -# -# Specify a list of RPC commands to run at startup. -# -# Examples: -# { "command" : "server_info" } -# { "command" : "log_level", "partition" : "ripplecalc", "severity" : "trace" } -# -# -# -# [rpc_secure] -# -# 0 or 1. -# -# 0: Server certificates are not provided for RPC clients using SSL [default] -# 1: Client RPC connections wil be provided with SSL certificates. -# -# Note that if rpc_secure is enabled, it will also be necessary to configure -# the certificate file settings located in rpc_ssl_cert, rpc_ssl_chain, and -# rpc_ssl_key -# -# -# -# [rpc_ssl_cert] -# -# -# -# A file system path leading to the SSL certificate file to use for secure -# RPC. The file is in PEM format. The file is not needed if the chain -# includes it. -# -# -# -# [rpc_ssl_chain] -# -# -# -# A file system path leading to the file with the certificate chain. -# The chain may include the end certificate. -# -# -# -# [rpc_ssl_key] -# -# -# -# A file system path leading to the file with the SSL key. -# The file is in PEM format. -# -# -# -#------------------------------------------------------------------------------- -# -# 4. SMS Gateway +# 3. SMS Gateway # #--------------- # @@ -510,7 +440,7 @@ # #------------------------------------------------------------------------------- # -# 5. Ripple Protocol +# 4. Ripple Protocol # #------------------- # @@ -654,7 +584,7 @@ # #------------------------------------------------------------------------------- # -# 6. HTTPS Client +# 5. HTTPS Client # #---------------- # @@ -694,7 +624,7 @@ # #------------------------------------------------------------------------------- # -# 7. Database +# 6. Database # #------------ # @@ -767,7 +697,7 @@ # #------------------------------------------------------------------------------- # -# 8. Diagnostics +# 7. Diagnostics # #--------------- # @@ -823,7 +753,7 @@ # #------------------------------------------------------------------------------- # -# 9. Voting +# 8. Voting # #---------- # @@ -873,41 +803,84 @@ # owner_reserve = 5000000 # 5 XRP # #------------------------------------------------------------------------------- - -# Allow other peers to connect to this server. # -[peer_ip] -0.0.0.0 - -[peer_port] -51235 - -# Allow untrusted clients to connect to this server. +# 9. Example Settings # -[websocket_public_ip] -0.0.0.0 - -[websocket_public_port] -5006 - -# Provide trusted websocket ADMIN access to the localhost. +#-------------------- # -[websocket_ip] -127.0.0.1 - -[websocket_port] -6006 - -# Provide trusted json-rpc ADMIN access to the localhost. +# Administrators can use these values as a starting poing for configuring +# their instance of rippled, but each value should be checked to make sure +# it meets the business requirements for the organization. # -[rpc_ip] -127.0.0.1 +# Server +# +# These example configuration settings create these ports: +# +# "peer" +# +# Peer protocol open to everyone. This is required to accept +# incoming rippled connections. This does not affect automatic +# or manual outgoing Peer protocol connections. +# +# "rpc" +# +# Administrative RPC commands over HTTPS, when originating from +# the same machine (via the loopback adapter at 127.0.0.1). +# +# "wss_admin" +# +# Admin level API commands over Secure Websockets, when originating +# from the same machine (via the loopback adapter at 127.0.0.1). +# +# This port is commented out but can be enabled by removing +# the '#' from each corresponding line including the entry under [server] +# +# "wss_public" +# +# Guest level API commands over Secure Websockets, open to everyone. +# +# For HTTPS and Secure Websockets ports, if no certificate and key file +# are specified then a self-signed certificate will be generated on startup. +# If you have a certificate and key file, uncomment the corresponding lines +# and ensure the paths to the files are correct. +# +# NOTE +# +# To accept connections on well known ports such as 80 (HTTP) or +# 443 (HTTPS), most operating systems will require rippled to +# run with administrator privileges, or else rippled will not start. -[rpc_port] -5005 +[server] +port_rpc +port_peer +port_wss_admin +#port_ws_public +#ssl_key = /etc/ssl/private/server.key +#ssl_cert = /etc/ssl/certs/server.crt -[rpc_allow_remote] -0 +[port_rpc] +port = 5005 +ip = 127.0.0.1 +admin = allow +protocol = https + +[port_peer] +port = 51235 +ip = 0.0.0.0 +protocol = peer + +[port_wss_admin] +port = 6006 +ip = 127.0.0.1 +admin = allow +protocol = wss + +#[port_ws_public] +#port = 5005 +#ip = 127.0.0.1 +#protocol = wss + +#------------------------------------------------------------------------------- [node_size] medium @@ -962,22 +935,7 @@ n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5 [rpc_startup] { "command": "log_level", "severity": "warning" } -# Configure SSL for WebSockets. Not enabled by default because not everybody -# has an SSL cert on their server, but if you uncomment the following lines and -# set the path to the SSL certificate and private key the WebSockets protocol -# will be protected by SSL/TLS. -#[websocket_secure] -#1 - -#[websocket_ssl_cert] -#/etc/ssl/certs/server.crt - -#[websocket_ssl_key] -#/etc/ssl/private/server.key - # Defaults to 0 ("no") so that you can use self-signed SSL certificates for # development, or internally. #[ssl_verify] #0 - - diff --git a/src/ripple/app/main/Application.cpp b/src/ripple/app/main/Application.cpp index 7659e0fc67..e22961091f 100644 --- a/src/ripple/app/main/Application.cpp +++ b/src/ripple/app/main/Application.cpp @@ -24,13 +24,15 @@ #include #include #include -#include +#include #include -#include #include #include #include #include +#include +#include +#include #include #include #include @@ -193,12 +195,8 @@ public: std::unique_ptr mTxnDB; std::unique_ptr mLedgerDB; std::unique_ptr mWalletDB; - - std::unique_ptr m_wsSSLContext; - std::unique_ptr m_peers; - std::unique_ptr m_rpcDoor; - std::unique_ptr m_wsPublicDoor; - std::unique_ptr m_wsPrivateDoor; + std::unique_ptr m_overlay; + std::vector > wsDoors_; beast::WaitableEvent m_stop; @@ -305,9 +303,9 @@ public: // VFALCO NOTE LocalCredentials starts the deprecated UNL service , m_deprecatedUNL (make_UniqueNodeList (*m_jobQueue)) - , serverHandler_ (make_RPCHTTPServer (*m_networkOPs, - *m_jobQueue, *m_networkOPs, *m_resourceManager, - setup_RPC(getConfig()["rpc"]))) + , serverHandler_ (make_ServerHandler (*m_networkOPs, + get_io_service(), *m_jobQueue, *m_networkOPs, + *m_resourceManager)) , m_nodeStore (m_nodeStoreManager->make_Database ("NodeStore.main", m_nodeStoreScheduler, m_logs.journal("NodeObject"), @@ -520,7 +518,7 @@ public: Overlay& overlay () { - return *m_peers; + return *m_overlay; } // VFALCO TODO Move these to the .cpp @@ -708,73 +706,42 @@ public: m_treeNodeCache.setTargetAge (getConfig ().getSize (siTreeCacheAge)); //---------------------------------------------------------------------- + // + // Server + // + //---------------------------------------------------------------------- + + serverHandler_->setup (setup_ServerHandler(getConfig(), std::cerr), + m_journal); // VFALCO NOTE Unfortunately, in stand-alone mode some code still // foolishly calls overlay(). When this is fixed we can // move the instantiation inside a conditional: // // if (!getConfig ().RUN_STANDALONE) - m_peers = make_Overlay (setup_Overlay(getConfig()), *m_jobQueue, - *m_resourceManager, *m_siteFiles, - getConfig().getModuleDatabasePath(), *m_resolver, + m_overlay = make_Overlay (setup_Overlay(getConfig()), *m_jobQueue, + *serverHandler_, *m_resourceManager, + getConfig ().getModuleDatabasePath (), *m_resolver, get_io_service()); - add (*m_peers); // add to PropertyStream + add (*m_overlay); // add to PropertyStream - // SSL context used for WebSocket connections. - if (getConfig ().WEBSOCKET_SECURE) + // Create websocket doors + for (auto const& port : serverHandler_->setup().ports) { - m_wsSSLContext.reset (RippleSSLContext::createAuthenticated ( - getConfig ().WEBSOCKET_SSL_KEY, - getConfig ().WEBSOCKET_SSL_CERT, - getConfig ().WEBSOCKET_SSL_CHAIN)); - } - else - { - m_wsSSLContext.reset (RippleSSLContext::createWebSocket ()); - } - - // Create private listening WebSocket socket - // - if (!getConfig ().WEBSOCKET_IP.empty () && getConfig ().WEBSOCKET_PORT) - { - m_wsPrivateDoor.reset (WSDoor::New (*m_resourceManager, - getOPs(), getConfig ().WEBSOCKET_IP, getConfig ().WEBSOCKET_PORT, - false, m_wsSSLContext->get ())); - - if (m_wsPrivateDoor == nullptr) + if (! port.websockets()) + continue; + auto door = make_WSDoor(port, *m_resourceManager, getOPs()); + if (door == nullptr) { - beast::FatalError ("Could not open the WebSocket private interface.", - __FILE__, __LINE__); + m_journal.fatal << "Could not create Websocket for [" << + port.name << "]"; + throw std::exception(); } - } - else - { - m_journal.info << "WebSocket private interface: disabled"; - } - - // Create public listening WebSocket socket - // - if (!getConfig ().WEBSOCKET_PUBLIC_IP.empty () && getConfig ().WEBSOCKET_PUBLIC_PORT) - { - m_wsPublicDoor.reset (WSDoor::New (*m_resourceManager, - getOPs(), getConfig ().WEBSOCKET_PUBLIC_IP, getConfig ().WEBSOCKET_PUBLIC_PORT, - true, m_wsSSLContext->get ())); - - if (m_wsPublicDoor == nullptr) - { - beast::FatalError ("Could not open the WebSocket public interface.", - __FILE__, __LINE__); - } - } - else - { - m_journal.info << "WebSocket public interface: disabled"; + wsDoors_.emplace_back(std::move(door)); } //---------------------------------------------------------------------- - serverHandler_->setup (m_journal); - // Begin connecting to network. if (!getConfig ().RUN_STANDALONE) { @@ -923,12 +890,6 @@ public: doStop (); { - // These two asssignment should no longer be necessary - // once the WSDoor cancels its pending I/O correctly - //m_wsPublicDoor = nullptr; - //m_wsPrivateDoor = nullptr; - //m_wsProxyDoor = nullptr; - // VFALCO TODO Try to not have to do this early, by using observers to // eliminate LoadManager's dependency inversions. // diff --git a/src/ripple/app/main/Application.h b/src/ripple/app/main/Application.h index 4587d568a0..00365fcc3a 100644 --- a/src/ripple/app/main/Application.h +++ b/src/ripple/app/main/Application.h @@ -21,6 +21,12 @@ #define RIPPLE_APP_APPLICATION_H_INCLUDED #include +#include +#include +#include +#include + +namespace boost { namespace asio { class io_service; } } namespace ripple { @@ -31,12 +37,12 @@ namespace NodeStore { class Database; } namespace RPC { class Manager; } // VFALCO TODO Fix forward declares required for header dependency loops -class CollectorManager; class AmendmentTable; +class CollectorManager; class IHashRouter; class Logs; class LoadFeeTrack; -class Overlay; +class LocalCredentials; class UniqueNodeList; class JobQueue; class InboundLedgers; @@ -44,12 +50,13 @@ class LedgerMaster; class LoadManager; class NetworkOPs; class OrderBookDB; +class Overlay; +class PathRequests; class ProofOfWorkFactory; class SerializedLedgerEntry; class TransactionMaster; class TxQueue; -class LocalCredentials; -class PathRequests; +class Validations; class DatabaseCon; @@ -70,7 +77,7 @@ public: other things */ - typedef RippleRecursiveMutex LockType; + typedef std::recursive_mutex LockType; typedef std::unique_lock ScopedLockType; typedef std::unique_ptr ScopedLock; diff --git a/src/ripple/app/main/LocalCredentials.cpp b/src/ripple/app/main/LocalCredentials.cpp index 043795ad2f..91b8730427 100644 --- a/src/ripple/app/main/LocalCredentials.cpp +++ b/src/ripple/app/main/LocalCredentials.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include #include namespace ripple { @@ -88,7 +89,7 @@ bool LocalCredentials::nodeIdentityCreate () RippleAddress naNodePrivate = RippleAddress::createNodePrivate (naSeed); // Make new key. - std::string strDh512 (RippleSSLContext::getRawDHParams (512)); + std::string strDh512 (getRawDHParams (512)); std::string strDh1024 = strDh512; diff --git a/src/ripple/app/main/Main.cpp b/src/ripple/app/main/Main.cpp index 4f5d60c28b..e00106e367 100644 --- a/src/ripple/app/main/Main.cpp +++ b/src/ripple/app/main/Main.cpp @@ -66,7 +66,7 @@ void startServer () RPCHandler rhHandler (getApp().getOPs ()); Resource::Charge loadType = Resource::feeReferenceRPC; - Json::Value jvResult = rhHandler.doCommand (jvCommand, Config::ADMIN, loadType); + Json::Value jvResult = rhHandler.doCommand (jvCommand, Role::ADMIN, loadType); if (!getConfig ().QUIET) std::cerr << "Result: " << jvResult << std::endl; @@ -345,8 +345,9 @@ int run (int argc, char** argv) // if (vm.count ("rpc_ip")) { - getConfig ().setRpcIpAndOptionalPort (vm ["rpc_ip"].as ()); - getConfig().overwrite("rpc", "ip", vm["rpc_ip"].as()); + // VFALCO TODO This is currently broken + //getConfig ().setRpcIpAndOptionalPort (vm ["rpc_ip"].as ()); + //getConfig().overwrite("rpc", "ip", vm["rpc_ip"].as()); } // Override the RPC destination port number @@ -354,8 +355,9 @@ int run (int argc, char** argv) if (vm.count ("rpc_port")) { // VFALCO TODO This should be a short. - getConfig ().setRpcPort (vm ["rpc_port"].as ()); - getConfig().overwrite("rpc", "port", vm["rpc_port"].as()); + // VFALCO TODO This is currently broken + //getConfig ().setRpcPort (vm ["rpc_port"].as ()); + //getConfig().overwrite("rpc", "port", vm["rpc_port"].as()); } if (vm.count ("quorum")) diff --git a/src/ripple/app/main/ServerHandlerImp.cpp b/src/ripple/app/main/ServerHandlerImp.cpp deleted file mode 100644 index 9bff3ea297..0000000000 --- a/src/ripple/app/main/ServerHandlerImp.cpp +++ /dev/null @@ -1,273 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include - -namespace ripple { - -ServerHandler::ServerHandler (Stoppable& parent) - : Stoppable ("ServerHandler", parent) - , Source ("server") -{ -} - -//------------------------------------------------------------------------------ - -ServerHandlerImp::ServerHandlerImp (Stoppable& parent, JobQueue& jobQueue, - NetworkOPs& networkOPs, Resource::Manager& resourceManager, - RPC::Setup const& setup) - : ServerHandler (parent) - , m_resourceManager (resourceManager) - , m_journal (deprecatedLogs().journal("Server")) - , m_jobQueue (jobQueue) - , m_networkOPs (networkOPs) - , m_server (HTTP::make_Server( - *this, deprecatedLogs().journal("Server"))) - , setup_ (setup) -{ - if (setup_.secure) - m_context.reset (RippleSSLContext::createAuthenticated ( - setup_.ssl_key, setup_.ssl_cert, setup_.ssl_chain)); - else - m_context.reset (RippleSSLContext::createBare()); -} - -ServerHandlerImp::~ServerHandlerImp() -{ - m_server = nullptr; -} - -void -ServerHandlerImp::setup (beast::Journal journal) -{ - if (! setup_.ip.empty() && setup_.port != 0) - { - auto ep = beast::IP::Endpoint::from_string (setup_.ip); - - // VFALCO TODO IP address should not have an "unspecified" state - //if (! is_unspecified (ep)) - { - HTTP::Port port; - - if (setup_.secure == 0) - port.security = HTTP::Port::Security::no_ssl; - else if (setup_.secure == 1) - port.security = HTTP::Port::Security::allow_ssl; - else - port.security = HTTP::Port::Security::require_ssl; - port.addr = ep.at_port(0); - if (setup_.port != 0) - port.port = setup_.port; - else - port.port = ep.port(); - port.context = m_context.get (); - - std::vector list; - list.push_back (port); - m_server->ports(list); - } - } - else - { - journal.info << "RPC interface: disabled"; - } -} - -//-------------------------------------------------------------------------- - -void -ServerHandlerImp::onStop() -{ - m_server->close(); -} - -//-------------------------------------------------------------------------- - -void -ServerHandlerImp::onAccept (HTTP::Session& session) -{ - // Reject non-loopback connections if RPC_ALLOW_REMOTE is not set - if (! setup_.allow_remote && - ! beast::IP::is_loopback (session.remoteAddress())) - { - session.close (false); - } -} - -void -ServerHandlerImp::onRequest (HTTP::Session& session) -{ - // Check user/password authorization - auto const headers = build_map (session.request().headers); - if (! HTTPAuthorized (headers)) - { - session.write (HTTPReply (403, "Forbidden")); - session.close (true); - return; - } - - m_jobQueue.addJob (jtCLIENT, "RPC-Client", std::bind ( - &ServerHandlerImp::processSession, this, std::placeholders::_1, - session.detach())); -} - -void -ServerHandlerImp::onClose (HTTP::Session& session, - boost::system::error_code const&) -{ -} - -void -ServerHandlerImp::onStopped (HTTP::Server&) -{ - stopped(); -} - -//-------------------------------------------------------------------------- - -// Dispatched on the job queue -void -ServerHandlerImp::processSession (Job& job, - std::shared_ptr const& session) -{ - session->write (processRequest (to_string(session->body()), - session->remoteAddress().at_port(0))); - - if (session->request().keep_alive()) - { - session->complete(); - } - else - { - session->close (true); - } -} - -std::string -ServerHandlerImp::createResponse ( - int statusCode, - std::string const& description) -{ - return HTTPReply (statusCode, description); -} - -// VFALCO ARGH! returning a single std::string for the entire response? -std::string -ServerHandlerImp::processRequest (std::string const& request, - beast::IP::Endpoint const& remoteIPAddress) -{ - Json::Value jvRequest; - { - Json::Reader reader; - if ((request.size () > 1000000) || - ! reader.parse (request, jvRequest) || - jvRequest.isNull () || - ! jvRequest.isObject ()) - { - return createResponse (400, "Unable to parse request"); - } - } - - auto const role = getConfig ().getAdminRole (jvRequest, remoteIPAddress); - - Resource::Consumer usage; - - if (role == Config::ADMIN) - usage = m_resourceManager.newAdminEndpoint (remoteIPAddress.to_string()); - else - usage = m_resourceManager.newInboundEndpoint(remoteIPAddress); - - if (usage.disconnect ()) - return createResponse (503, "Server is overloaded"); - - // Parse id now so errors from here on will have the id - // - // VFALCO NOTE Except that "id" isn't included in the following errors. - // - Json::Value const id = jvRequest ["id"]; - - Json::Value const method = jvRequest ["method"]; - - if (method.isNull ()) - return createResponse (400, "Null method"); - - if (! method.isString ()) - return createResponse (400, "method is not string"); - - std::string strMethod = method.asString (); - if (strMethod.empty()) - return createResponse (400, "method is empty"); - - // Parse params - Json::Value params = jvRequest ["params"]; - - if (params.isNull ()) - params = Json::Value (Json::arrayValue); - - else if (!params.isArray ()) - return HTTPReply (400, "params unparseable"); - - // VFALCO TODO Shouldn't we handle this earlier? - // - if (role == Config::FORBID) - { - // VFALCO TODO Needs implementing - // FIXME Needs implementing - // XXX This needs rate limiting to prevent brute forcing password. - return HTTPReply (403, "Forbidden"); - } - - - std::string response; - RPCHandler rpcHandler (m_networkOPs); - Resource::Charge loadType = Resource::feeReferenceRPC; - - m_journal.debug << "Query: " << strMethod << params; - - Json::Value const result (rpcHandler.doRpcCommand ( - strMethod, params, role, loadType)); - m_journal.debug << "Reply: " << result; - - usage.charge (loadType); - - response = JSONRPCReply (result, Json::Value (), id); - - return createResponse (200, response); -} - -//------------------------------------------------------------------------------ - -void -ServerHandlerImp::onWrite (beast::PropertyStream::Map& map) -{ - m_server->onWrite (map); -} - -//------------------------------------------------------------------------------ - -std::unique_ptr -make_RPCHTTPServer (beast::Stoppable& parent, JobQueue& jobQueue, - NetworkOPs& networkOPs, Resource::Manager& resourceManager, - RPC::Setup const& setup) -{ - return std::make_unique ( - parent, jobQueue, networkOPs, resourceManager, setup); -} - -} diff --git a/src/ripple/app/shamap/SHAMapTreeNode.h b/src/ripple/app/shamap/SHAMapTreeNode.h index cc18186f93..12d49c216d 100644 --- a/src/ripple/app/shamap/SHAMapTreeNode.h +++ b/src/ripple/app/shamap/SHAMapTreeNode.h @@ -21,6 +21,7 @@ #define RIPPLE_SHAMAPTREENODE_H #include +#include #include #include @@ -203,8 +204,6 @@ private: static std::mutex childLock; }; -using TreeNodeCache = TaggedCache ; - } // ripple #endif diff --git a/src/ripple/http/impl/Port.cpp b/src/ripple/app/shamap/TreeNodeCache.h similarity index 81% rename from src/ripple/http/impl/Port.cpp rename to src/ripple/app/shamap/TreeNodeCache.h index 0213bfc391..26e614ac78 100644 --- a/src/ripple/http/impl/Port.cpp +++ b/src/ripple/app/shamap/TreeNodeCache.h @@ -17,17 +17,17 @@ */ //============================================================================== +#ifndef RIPPLE_APP_SHAMAP_TREENODECACHE_H_INCLUDED +#define RIPPLE_APP_SHAMAP_TREENODECACHE_H_INCLUDED + +#include + namespace ripple { -namespace HTTP { -Port::Port (std::uint16_t port_, beast::IP::Endpoint const& addr_, - Security security_, SSLContext* context_) - : security (security_) - , port (port_) - , addr (addr_) - , context (context_) -{ -} +class SHAMapTreeNode; -} -} +using TreeNodeCache = TaggedCache ; + +} // ripple + +#endif diff --git a/src/ripple/app/websocket/WSConnection.cpp b/src/ripple/app/websocket/WSConnection.cpp index a62aaf036e..8719ce31c1 100644 --- a/src/ripple/app/websocket/WSConnection.cpp +++ b/src/ripple/app/websocket/WSConnection.cpp @@ -19,13 +19,16 @@ #include #include +#include namespace ripple { -WSConnection::WSConnection (Resource::Manager& resourceManager, - Resource::Consumer usage, InfoSub::Source& source, bool isPublic, - beast::IP::Endpoint const& remoteAddress, boost::asio::io_service& io_service) +WSConnection::WSConnection (HTTP::Port const& port, + Resource::Manager& resourceManager, Resource::Consumer usage, + InfoSub::Source& source, bool isPublic, + beast::IP::Endpoint const& remoteAddress, boost::asio::io_service& io_service) : InfoSub (source, usage) + , port_(port) , m_resourceManager (resourceManager) , m_isPublic (isPublic) , m_remoteAddress (remoteAddress) @@ -154,12 +157,10 @@ Json::Value WSConnection::invokeCommand (Json::Value& jvRequest) RPCHandler mRPCHandler (m_netOPs, std::dynamic_pointer_cast (this->shared_from_this ())); Json::Value jvResult (Json::objectValue); - Config::Role const role = m_isPublic - ? Config::GUEST // Don't check on the public interface. - : getConfig ().getAdminRole ( - jvRequest, m_remoteAddress); + Role const role = port_.allow_admin ? adminRole (port_, jvRequest, + m_remoteAddress, getConfig().RPC_ADMIN_ALLOW) : Role::GUEST; - if (Config::FORBID == role) + if (Role::FORBID == role) { jvResult[jss::result] = rpcError (rpcFORBIDDEN); } diff --git a/src/ripple/app/websocket/WSConnection.h b/src/ripple/app/websocket/WSConnection.h index b374acf5e6..994527802f 100644 --- a/src/ripple/app/websocket/WSConnection.h +++ b/src/ripple/app/websocket/WSConnection.h @@ -20,7 +20,10 @@ #ifndef RIPPLE_WSCONNECTION_H #define RIPPLE_WSCONNECTION_H +#include +#include #include +#include namespace ripple { @@ -38,9 +41,10 @@ public: protected: typedef websocketpp::message::data::ptr message_ptr; - WSConnection (Resource::Manager& resourceManager, - Resource::Consumer usage, InfoSub::Source& source, bool isPublic, - beast::IP::Endpoint const& remoteAddress, boost::asio::io_service& io_service); + WSConnection (HTTP::Port const& port, + Resource::Manager& resourceManager, Resource::Consumer usage, + InfoSub::Source& source, bool isPublic, + beast::IP::Endpoint const& remoteAddress, boost::asio::io_service& io_service); WSConnection(WSConnection const&) = delete; WSConnection& operator= (WSConnection const&) = delete; @@ -59,6 +63,7 @@ public: Json::Value invokeCommand (Json::Value& jvRequest); protected: + HTTP::Port const& port_; Resource::Manager& m_resourceManager; Resource::Consumer m_usage; bool const m_isPublic; @@ -90,11 +95,15 @@ public: typedef typename boost::weak_ptr weak_connection_ptr; typedef WSServerHandler server_type; +private: + server_type& m_serverHandler; + weak_connection_ptr m_connection; + public: - WSConnectionType (Resource::Manager& resourceManager, - InfoSub::Source& source, server_type& serverHandler, - connection_ptr const& cpConnection) + WSConnectionType (Resource::Manager& resourceManager, InfoSub::Source& source, + server_type& serverHandler, connection_ptr const& cpConnection) : WSConnection ( + serverHandler.port(), resourceManager, resourceManager.newInboundEndpoint (cpConnection->get_socket ().remote_endpoint ()), source, @@ -197,10 +206,6 @@ public: m_connection, &m_serverHandler, beast::asio::placeholders::error))); } } - -private: - server_type& m_serverHandler; - weak_connection_ptr m_connection; }; } // ripple diff --git a/src/ripple/app/websocket/WSDoor.cpp b/src/ripple/app/websocket/WSDoor.cpp index dfd7528a0e..b70405ff74 100644 --- a/src/ripple/app/websocket/WSDoor.cpp +++ b/src/ripple/app/websocket/WSDoor.cpp @@ -43,18 +43,24 @@ class WSDoorImp , protected beast::Thread , beast::LeakChecked { +private: + typedef RippleRecursiveMutex LockType; + typedef std::lock_guard ScopedLockType; + + std::shared_ptr port_; + Resource::Manager& m_resourceManager; + InfoSub::Source& m_source; + LockType m_endpointLock; + std::shared_ptr m_endpoint; + public: - WSDoorImp (Resource::Manager& resourceManager, - InfoSub::Source& source, std::string const& strIp, int iPort, - bool bPublic, boost::asio::ssl::context& ssl_context) + WSDoorImp (HTTP::Port const& port, Resource::Manager& resourceManager, + InfoSub::Source& source) : WSDoor (source) , Thread ("websocket") + , port_(std::make_shared(port)) , m_resourceManager (resourceManager) , m_source (source) - , m_ssl_context (ssl_context) - , mPublic (bPublic) - , mIp (strIp) - , mPort (iPort) { startThread (); } @@ -67,13 +73,14 @@ public: private: void run () { - WriteLog (lsINFO, WSDoor) << boost::str ( - boost::format ("Websocket: %s: Listening: %s %d ") % - (mPublic ? "Public" : "Private") % mIp % mPort); + WriteLog (lsINFO, WSDoor) << + "Websocket: '" << port_->name << "' listening on " << + port_->ip.to_string() << ":" << std::to_string(port_->port) << + (port_->allow_admin ? "(Admin)" : ""); websocketpp::server_autotls::handler::ptr handler ( new WSServerHandler ( - m_resourceManager, m_source, m_ssl_context, mPublic)); + port_, m_resourceManager, m_source)); { ScopedLockType lock (m_endpointLock); @@ -84,9 +91,7 @@ private: // Call the main-event-loop of the websocket server. try { - m_endpoint->listen ( - boost::asio::ip::tcp::endpoint ( - boost::asio::ip::address ().from_string (mIp), mPort)); + m_endpoint->listen (port_->ip, port_->port); } catch (websocketpp::exception& e) { @@ -135,20 +140,6 @@ private: signalThreadShouldExit (); } - -private: - typedef RippleRecursiveMutex LockType; - typedef std::lock_guard ScopedLockType; - - Resource::Manager& m_resourceManager; - InfoSub::Source& m_source; - boost::asio::ssl::context& m_ssl_context; - LockType m_endpointLock; - - std::shared_ptr m_endpoint; - bool mPublic; - std::string mIp; - int mPort; }; //------------------------------------------------------------------------------ @@ -160,22 +151,22 @@ WSDoor::WSDoor (Stoppable& parent) //------------------------------------------------------------------------------ -WSDoor* WSDoor::New (Resource::Manager& resourceManager, InfoSub::Source& source, - std::string const& strIp, int iPort, bool bPublic, - boost::asio::ssl::context& ssl_context) +std::unique_ptr +make_WSDoor (HTTP::Port const& port, Resource::Manager& resourceManager, + InfoSub::Source& source) { - std::unique_ptr door; + std::unique_ptr door; try { - door = std::make_unique (resourceManager, - source, strIp, iPort, bPublic, ssl_context); + door = std::make_unique (port, resourceManager, source); } catch (...) { } - return door.release (); + return door; +} + } -} // ripple diff --git a/src/ripple/app/websocket/WSDoor.h b/src/ripple/app/websocket/WSDoor.h index 64339817cb..6a0e6050a0 100644 --- a/src/ripple/app/websocket/WSDoor.h +++ b/src/ripple/app/websocket/WSDoor.h @@ -20,6 +20,8 @@ #ifndef RIPPLE_WSDOOR_H_INCLUDED #define RIPPLE_WSDOOR_H_INCLUDED +#include + namespace ripple { /** Handles accepting incoming WebSocket connections. */ @@ -31,11 +33,14 @@ protected: public: virtual ~WSDoor() = default; - static WSDoor* New (Resource::Manager& resourceManager, - InfoSub::Source& source, std::string const& strIp, int iPort, - bool bPublic, boost::asio::ssl::context& ssl_context); + // VFALCO TODO Add this member function to prevent races on shutdown + //virtual void close() = 0; }; -} // ripple +std::unique_ptr +make_WSDoor (HTTP::Port const& port, Resource::Manager& resourceManager, + InfoSub::Source& source); + +} #endif diff --git a/src/ripple/app/websocket/WSServerHandler.h b/src/ripple/app/websocket/WSServerHandler.h index 75becc5cbe..540e4efce2 100644 --- a/src/ripple/app/websocket/WSServerHandler.h +++ b/src/ripple/app/websocket/WSServerHandler.h @@ -21,7 +21,9 @@ #define RIPPLE_WSSERVERHANDLER_H_INCLUDED #include +#include #include +#include namespace ripple { @@ -60,6 +62,7 @@ public: }; private: + std::shared_ptr port_; Resource::Manager& m_resourceManager; InfoSub::Source& m_source; @@ -69,31 +72,31 @@ protected: typedef std::lock_guard ScopedLockType; LockType mLock; -private: - boost::asio::ssl::context& m_ssl_context; - -protected: // For each connection maintain an associated object to track subscriptions. typedef hash_map MapType; MapType mMap; - bool const mPublic; public: - WSServerHandler (Resource::Manager& resourceManager, - InfoSub::Source& source, boost::asio::ssl::context& ssl_context, bool bPublic) - : m_resourceManager (resourceManager) + WSServerHandler (std::shared_ptr const& port, + Resource::Manager& resourceManager, InfoSub::Source& source) + : port_(port) + , m_resourceManager (resourceManager) , m_source (source) - , m_ssl_context (ssl_context) - , mPublic (bPublic) { } WSServerHandler(WSServerHandler const&) = delete; WSServerHandler& operator= (WSServerHandler const&) = delete; - bool getPublic () + HTTP::Port const& + port() const { - return mPublic; + return *port_; + } + + bool getPublic() + { + return port_->allow_admin; }; static void ssend (connection_ptr cpClient, message_ptr mpMessage) @@ -403,9 +406,22 @@ public: return true; } - boost::asio::ssl::context& get_ssl_context () + boost::asio::ssl::context& + get_ssl_context () { - return m_ssl_context; + return *port_->context; + } + + bool + plain_only() + { + return port_->protocol.count("wss") == 0; + } + + bool + secure_only() + { + return port_->protocol.count("ws") == 0; } // Respond to http requests. diff --git a/src/ripple/common/RippleSSLContext.h b/src/ripple/common/RippleSSLContext.h deleted file mode 100644 index 1a33e4565a..0000000000 --- a/src/ripple/common/RippleSSLContext.h +++ /dev/null @@ -1,133 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_COMMON_SSLCONTEXT_H_INCLUDED -#define RIPPLE_COMMON_SSLCONTEXT_H_INCLUDED - -#include -#include -#include - -namespace ripple { - -/** Simple base class for passing a context around. - This lets derived classes hide their implementation from the headers. -*/ -class SSLContext -{ -public: - virtual ~SSLContext (); - - // Saves typing - typedef boost::asio::ssl::context ContextType; - - inline ContextType& get () noexcept - { - return m_context; - } - - inline ContextType const& get () const noexcept - { - return m_context; - } - - // implicit conversion - inline operator ContextType& () noexcept - { - return get (); - } - - inline operator ContextType const& () const noexcept - { - return get (); - } - -protected: - explicit SSLContext (ContextType& context); - - SSLContext(SSLContext const&) = delete; - SSLContext& operator= (SSLContext const&) = delete; - - ContextType& m_context; -}; - -//------------------------------------------------------------------------------ - -/** The SSL contexts used by Ripple. - - This is what Ripple uses for its secure connections. The ECDSA curve - parameters are predefined and verified to be secure. The context is set to - sslv23, Transport Layer Security / General. This is primarily used for - peer to peer servers that don't care about certificates or - identity verification. -*/ -// VFALCO NOTE The comment above is out of date -class RippleSSLContext : public SSLContext -{ -public: - /** Retrieve raw DH parameters. - This is in the format expected by the OpenSSL function d2i_DHparams. - The vector is binary. An empty vector means the key size is unsupported. - @note The string may contain nulls in the middle. Use size() to - determine the actual size. - */ - static std::string getRawDHParams (int keySize); - - /** Creates a bare SSL context with just sslv23 set. - This is used for RPC connections. - */ - static RippleSSLContext* createBare (); - - /** Creates a suitable for WebSocket without authentication. - This is for WebSocket connections that don't use certificates. - */ - static RippleSSLContext* createWebSocket (); - - /** Create a context that allows anonymous connections. - No certificates are required. Peers use this context. - If the cipher list is invalid, a fatal error is raised. - */ - static RippleSSLContext* createAnonymous (std::string const& cipherList); - - /** Create a context with authentication requirements. - This is used for WebSocket connections. - The authentication credentials are loaded from the files with - the specified names. If an error occurs, a fatal error is raised. - */ - static RippleSSLContext* createAuthenticated ( - std::string key_file, std::string cert_file, std::string chain_file); - -protected: - explicit RippleSSLContext (ContextType& context); -}; - -//------------------------------------------------------------------------------ - -/** Create a self-signed SSL context that allows anonymous Diffie Hellman. */ -std::shared_ptr -make_ssl_context(); - -/** Create an authenticated SSL context using the specified files. */ -std::shared_ptr -make_authenticated_ssl_context (std::string const& key_file, - std::string const& cert_file, std::string const& chain_file); - -} - -#endif diff --git a/src/ripple/common/impl/RippleSSLContext.cpp b/src/ripple/common/impl/make_SSLContext.cpp similarity index 66% rename from src/ripple/common/impl/RippleSSLContext.cpp rename to src/ripple/common/impl/make_SSLContext.cpp index 89b4161bb0..c9c21a789e 100644 --- a/src/ripple/common/impl/RippleSSLContext.cpp +++ b/src/ripple/common/impl/make_SSLContext.cpp @@ -17,7 +17,7 @@ */ //============================================================================== -#include +#include #include #include #include @@ -27,8 +27,8 @@ #include namespace ripple { - namespace openssl { +namespace detail { template struct custom_delete; @@ -60,6 +60,15 @@ struct custom_delete } }; +template <> +struct custom_delete +{ + void operator() (DH* dh) const + { + DH_free(dh); + } +}; + template using custom_delete_unique_ptr = std::unique_ptr >; @@ -158,79 +167,59 @@ static void ssl_ctx_use_privatekey (SSL_CTX* const ctx, evp_pkey_ptr& key) } } -} // namespace openssl - -class RippleSSLContextImp : public RippleSSLContext +// track when SSL connections have last negotiated +struct StaticData { -private: - boost::asio::ssl::context m_context; + std::mutex lock; + beast::aged_unordered_set set; - // track when SSL connections have last negotiated - - struct StaticData - { - StaticData() : set (ripple::get_seconds_clock ()) - { - } - - std::mutex lock; - beast::aged_unordered_set set; - }; - -public: - RippleSSLContextImp () - : RippleSSLContext (m_context) - , m_context (boost::asio::ssl::context::sslv23) - { - } - - ~RippleSSLContextImp () - { - } - - static DH* tmp_dh_handler (SSL*, int, int key_length) - { - return DHparams_dup (getDH (key_length)); - } - - static bool disallowRenegotiation (SSL const* ssl, bool isNew); - - static void info_handler (SSL const* ssl, int event, int); - - // Pretty prints an error message - static std::string error_message (std::string const& what, - boost::system::error_code const& ec); - - //-------------------------------------------------------------------------- - - static std::string getRawDHParams (int keySize); - - //-------------------------------------------------------------------------- - - // Does common initialization for all but the bare context type. - static void initCommon (boost::asio::ssl::context& context); - void initCommon (); - - //-------------------------------------------------------------------------- - - static void initAnonymous (boost::asio::ssl::context& context, - std::string const& cipherList); - void initAnonymous (std::string const& cipherList); - - //-------------------------------------------------------------------------- - - static void initAuthenticated (boost::asio::ssl::context& context, - std::string key_file, std::string cert_file, std::string chain_file); - - void initAuthenticated ( - std::string key_file, std::string cert_file, std::string chain_file); - - //-------------------------------------------------------------------------- - - static DH* getDH (int keyLength); + StaticData() + : set (ripple::get_seconds_clock ()) + { } }; -bool RippleSSLContextImp::disallowRenegotiation (SSL const* ssl, bool isNew) +using dh_ptr = custom_delete_unique_ptr; + +static +dh_ptr +make_DH(std::string const& params) +{ + auto const* p ( + reinterpret_cast (¶ms [0])); + DH* const dh = d2i_DHparams (nullptr, &p, params.size ()); + if (p == nullptr) + beast::FatalError ("d2i_DHparams returned nullptr.", + __FILE__, __LINE__); + return dh_ptr(dh); +} + +static +DH* +getDH (int keyLength) +{ + if (keyLength == 512 || keyLength == 1024) + { + static dh_ptr dh512 = make_DH(getRawDHParams (keyLength)); + return dh512.get (); + } + else + { + beast::FatalError ("unsupported key length", __FILE__, __LINE__); + } + + return nullptr; +} + +static +DH* +tmp_dh_handler (SSL*, int, int key_length) +{ + return DHparams_dup (getDH (key_length)); +} + +static +bool +disallowRenegotiation (SSL const* ssl, bool isNew) { // Do not allow a connection to renegotiate // more than once every 4 minutes @@ -268,7 +257,9 @@ bool RippleSSLContextImp::disallowRenegotiation (SSL const* ssl, bool isNew) return false; } -void RippleSSLContextImp::info_handler (SSL const* ssl, int event, int) +static +void +info_handler (SSL const* ssl, int event, int) { if ((ssl->s3) && (event & SSL_CB_HANDSHAKE_START)) { @@ -277,7 +268,9 @@ void RippleSSLContextImp::info_handler (SSL const* ssl, int event, int) } } -std::string RippleSSLContextImp::error_message (std::string const& what, +static +std::string +error_message (std::string const& what, boost::system::error_code const& ec) { std::stringstream ss; @@ -288,40 +281,9 @@ std::string RippleSSLContextImp::error_message (std::string const& what, return ss.str(); } -std::string RippleSSLContextImp::getRawDHParams (int keySize) -{ - std::string params; - - // Original code provided the 512-bit keySize parameters - // when 1024 bits were requested so we will do the same. - if (keySize == 1024) - keySize = 512; - - switch (keySize) - { - case 512: - { - // These are the DH parameters that OpenCoin has chosen for Ripple - // - std::uint8_t const raw [] = { - 0x30, 0x46, 0x02, 0x41, 0x00, 0x98, 0x15, 0xd2, 0xd0, 0x08, 0x32, 0xda, - 0xaa, 0xac, 0xc4, 0x71, 0xa3, 0x1b, 0x11, 0xf0, 0x6c, 0x62, 0xb2, 0x35, - 0x8a, 0x10, 0x92, 0xc6, 0x0a, 0xa3, 0x84, 0x7e, 0xaf, 0x17, 0x29, 0x0b, - 0x70, 0xef, 0x07, 0x4f, 0xfc, 0x9d, 0x6d, 0x87, 0x99, 0x19, 0x09, 0x5b, - 0x6e, 0xdb, 0x57, 0x72, 0x4a, 0x7e, 0xcd, 0xaf, 0xbd, 0x3a, 0x97, 0x55, - 0x51, 0x77, 0x5a, 0x34, 0x7c, 0xe8, 0xc5, 0x71, 0x63, 0x02, 0x01, 0x02 - }; - - params.resize (sizeof (raw)); - std::copy (raw, raw + sizeof (raw), params.begin ()); - } - break; - }; - - return params; -} - -void RippleSSLContextImp::initCommon (boost::asio::ssl::context& context) +static +void +initCommon (boost::asio::ssl::context& context) { context.set_options ( boost::asio::ssl::context::default_workarounds | @@ -338,12 +300,9 @@ void RippleSSLContextImp::initCommon (boost::asio::ssl::context& context) info_handler); } -void RippleSSLContextImp::initCommon () -{ - initCommon(m_context); -} - -void RippleSSLContextImp::initAnonymous ( +static +void +initAnonymous ( boost::asio::ssl::context& context, std::string const& cipherList) { initCommon(context); @@ -367,12 +326,9 @@ void RippleSSLContextImp::initAnonymous ( ssl_ctx_use_privatekey (ctx, pkey); } -void RippleSSLContextImp::initAnonymous (std::string const& cipherList) -{ - initAnonymous(m_context, cipherList); -} - -void RippleSSLContextImp::initAuthenticated (boost::asio::ssl::context& context, +static +void +initAuthenticated (boost::asio::ssl::context& context, std::string key_file, std::string cert_file, std::string chain_file) { initCommon (context); @@ -468,138 +424,53 @@ void RippleSSLContextImp::initAuthenticated (boost::asio::ssl::context& context, } } -void RippleSSLContextImp::initAuthenticated ( - std::string key_file, std::string cert_file, std::string chain_file) -{ - initAuthenticated (m_context, key_file, cert_file, chain_file); -} - -// A simple RAII container for a DH -// -class ScopedDHPointer -{ -private: - ScopedDHPointer (ScopedDHPointer const&) = delete; - ScopedDHPointer& operator= (ScopedDHPointer const&) = delete; - -public: - // Construct from an existing DH - // - explicit ScopedDHPointer (DH* dh) - : m_dh (dh) - { - } - - // Construct from raw DH params - // - explicit ScopedDHPointer (std::string const& params) - { - auto const* p ( - reinterpret_cast (¶ms [0])); - m_dh = d2i_DHparams (nullptr, &p, params.size ()); - if (m_dh == nullptr) - beast::FatalError ("d2i_DHparams returned nullptr.", - __FILE__, __LINE__); - } - - ~ScopedDHPointer () - { - if (m_dh != nullptr) - DH_free (m_dh); - } - - operator DH* () const - { - return get (); - } - - DH* get () const - { - return m_dh; - } - -private: - DH* m_dh; -}; - -DH* RippleSSLContextImp::getDH (int keyLength) -{ - if (keyLength == 512 || keyLength == 1024) - { - static ScopedDHPointer dh512 (getRawDHParams (keyLength)); - - return dh512.get (); - } - else - { - beast::FatalError ("unsupported key length", __FILE__, __LINE__); - } - - return nullptr; -} +} // detail +} // openssl //------------------------------------------------------------------------------ -RippleSSLContext::RippleSSLContext (ContextType& context) - : SSLContext (context) +std::string +getRawDHParams (int keySize) { + std::string params; + + // Original code provided the 512-bit keySize parameters + // when 1024 bits were requested so we will do the same. + if (keySize == 1024) + keySize = 512; + + switch (keySize) + { + case 512: + { + // These are the DH parameters that OpenCoin has chosen for Ripple + // + std::uint8_t const raw [] = { + 0x30, 0x46, 0x02, 0x41, 0x00, 0x98, 0x15, 0xd2, 0xd0, 0x08, 0x32, 0xda, + 0xaa, 0xac, 0xc4, 0x71, 0xa3, 0x1b, 0x11, 0xf0, 0x6c, 0x62, 0xb2, 0x35, + 0x8a, 0x10, 0x92, 0xc6, 0x0a, 0xa3, 0x84, 0x7e, 0xaf, 0x17, 0x29, 0x0b, + 0x70, 0xef, 0x07, 0x4f, 0xfc, 0x9d, 0x6d, 0x87, 0x99, 0x19, 0x09, 0x5b, + 0x6e, 0xdb, 0x57, 0x72, 0x4a, 0x7e, 0xcd, 0xaf, 0xbd, 0x3a, 0x97, 0x55, + 0x51, 0x77, 0x5a, 0x34, 0x7c, 0xe8, 0xc5, 0x71, 0x63, 0x02, 0x01, 0x02 + }; + + params.resize (sizeof (raw)); + std::copy (raw, raw + sizeof (raw), params.begin ()); + } + break; + }; + + return params; } -RippleSSLContext* RippleSSLContext::createBare () -{ - std::unique_ptr context (new RippleSSLContextImp ()); - return context.release (); -} - -RippleSSLContext* RippleSSLContext::createWebSocket () -{ - std::unique_ptr context (new RippleSSLContextImp ()); - context->initCommon (); - return context.release (); -} - -RippleSSLContext* RippleSSLContext::createAnonymous (std::string const& cipherList) -{ - std::unique_ptr context (new RippleSSLContextImp ()); - context->initAnonymous (cipherList); - return context.release (); -} - -RippleSSLContext* RippleSSLContext::createAuthenticated ( - std::string key_file, std::string cert_file, std::string chain_file) -{ - std::unique_ptr context (new RippleSSLContextImp ()); - context->initAuthenticated (key_file, cert_file, chain_file); - return context.release (); -} - -std::string RippleSSLContext::getRawDHParams (int keySize) -{ - return RippleSSLContextImp::getRawDHParams (keySize); -} - -//------------------------------------------------------------------------------ - -SSLContext::SSLContext (ContextType& context) - : m_context (context) -{ -} - -SSLContext::~SSLContext () -{ -} - -//------------------------------------------------------------------------------ - -/** Create a self-signed SSL context that allows anonymous Diffie Hellman. */ std::shared_ptr -make_ssl_context() +make_SSLContext() { std::shared_ptr context = std::make_shared ( boost::asio::ssl::context::sslv23); // By default, allow anonymous DH. - RippleSSLContextImp::initAnonymous ( + openssl::detail::initAnonymous ( *context, "ALL:!LOW:!EXP:!MD5:@STRENGTH"); // VFALCO NOTE, It seems the WebSocket context never has // set_verify_mode called, for either setting of WEBSOCKET_SECURE @@ -607,17 +478,17 @@ make_ssl_context() return context; } -/** Create an authenticated SSL context using the specified files. */ std::shared_ptr -make_authenticated_ssl_context (std::string const& key_file, +make_SSLContextAuthed (std::string const& key_file, std::string const& cert_file, std::string const& chain_file) { std::shared_ptr context = std::make_shared ( boost::asio::ssl::context::sslv23); - RippleSSLContextImp::initAuthenticated(*context, + openssl::detail::initAuthenticated(*context, key_file, cert_file, chain_file); return context; } -} +} // ripple + diff --git a/src/ripple/common/make_SSLContext.h b/src/ripple/common/make_SSLContext.h new file mode 100644 index 0000000000..64887bdcb1 --- /dev/null +++ b/src/ripple/common/make_SSLContext.h @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_COMMON_MAKE_SSLCONTEXT_H_INCLUDED +#define RIPPLE_COMMON_MAKE_SSLCONTEXT_H_INCLUDED + +#include +#include +#include + +namespace ripple { + +/** Retrieve raw DH parameters. + This is in the format expected by the OpenSSL function d2i_DHparams. + The vector is binary. An empty vector means the key size is unsupported. + @note The string may contain nulls in the middle. Use size() to + determine the actual size. +*/ +std::string +getRawDHParams(int keySize); + +/** Create a self-signed SSL context that allows anonymous Diffie Hellman. */ +std::shared_ptr +make_SSLContext(); + +/** Create an authenticated SSL context using the specified files. */ +std::shared_ptr +make_SSLContextAuthed (std::string const& key_file, + std::string const& cert_file, std::string const& chain_file); + +} + +#endif diff --git a/src/ripple/core/Config.h b/src/ripple/core/Config.h index ad03fa1543..84caf9aacc 100644 --- a/src/ripple/core/Config.h +++ b/src/ripple/core/Config.h @@ -62,8 +62,6 @@ parseKeyValueSection (IniFileSections& secSource, std::string const& strSection) //------------------------------------------------------------------------------ -const int SYSTEM_PEER_PORT = 6561; - enum SizedItemName { siSweepInterval, @@ -151,69 +149,9 @@ public: // DEPRECATED boost::filesystem::path VALIDATORS_FILE; // As specifed in rippled.cfg. - //-------------------------------------------------------------------------- - - // Settings related to RPC - - /** Get the client or server RPC IP address. - @note The string may not always be in a valid parsable state. - @return A string representing the address. - */ - std::string getRpcIP () const { return m_rpcIP; } - - /** Get the client or server RPC port number. - @note The port number may be invalid (out of range or zero) - @return The RPC port number. - */ - int getRpcPort () const { return m_rpcPort; } - - /** Set the client or server RPC IP and optional port. - @note The string is not syntax checked. - @param newAddress A string in the format [':'] - */ - void setRpcIpAndOptionalPort (std::string const& newAddress); - - /** Set the client or server RPC IP. - @note The string is not syntax-checked. - @param newIP A string representing the IP address to use. - */ - void setRpcIP (std::string const& newIP) { m_rpcIP = newIP; } - - /** Set the client or server RPC port number. - @note The port number is not range checked. - @param newPort The RPC port number to use. - */ - void setRpcPort (int newPort) { m_rpcPort = newPort; } - - /** Convert the RPC/port combination to a readable string. - */ - std::string const getRpcAddress () - { - return m_rpcIP + ":" + std::to_string (m_rpcPort); - } - - /** Determine the level of administrative permission to grant. - */ - // VFALCO TODO Get this out of here - enum Role - { - GUEST, - USER, - ADMIN, - FORBID - }; - Role getAdminRole (Json::Value const& params, beast::IP::Endpoint const& remoteIp) const; - - /** Listening port number for peer connections. */ - int peerListeningPort; - /** List of Validators entries from rippled.cfg */ std::vector validators; -private: - std::string m_rpcIP; - int m_rpcPort; // VFALCO TODO This should be a short. - private: /** The folder where new module databases should be located */ beast::File m_moduleDbPath; @@ -318,39 +256,15 @@ public: int VALIDATION_QUORUM; // Minimum validations to consider ledger authoritative // Peer networking parameters - std::string PEER_IP; bool PEER_PRIVATE; // True to ask peers not to relay current IP. unsigned int PEERS_MAX; - // Websocket networking parameters - std::string WEBSOCKET_PUBLIC_IP; // XXX Going away. Merge with the inbound peer connction. - int WEBSOCKET_PUBLIC_PORT; - int WEBSOCKET_PUBLIC_SECURE; - - std::string WEBSOCKET_IP; - int WEBSOCKET_PORT; - int WEBSOCKET_SECURE; - int WEBSOCKET_PING_FREQ; - std::string WEBSOCKET_SSL_CERT; - std::string WEBSOCKET_SSL_CHAIN; - std::string WEBSOCKET_SSL_KEY; - // RPC parameters std::vector RPC_ADMIN_ALLOW; - std::string RPC_ADMIN_PASSWORD; - std::string RPC_ADMIN_USER; - std::string RPC_PASSWORD; - std::string RPC_USER; - bool RPC_ALLOW_REMOTE; Json::Value RPC_STARTUP; - int RPC_SECURE; - std::string RPC_SSL_CERT; - std::string RPC_SSL_CHAIN; - std::string RPC_SSL_KEY; - // Path searching int PATH_SEARCH_OLD; int PATH_SEARCH; @@ -399,36 +313,10 @@ public: int getSize (SizedItemName); void setup (std::string const& strConf, bool bQuiet); void load (); - -private: - void build_legacy(); }; -extern Config& getConfig (); - -//------------------------------------------------------------------------------ - -namespace RPC { - -struct Setup -{ - bool allow_remote = false; - std::string admin_user; - std::string admin_password; - std::string ip; - int port = 5001; - std::string user; - std::string password; - int secure = 0; - std::string ssl_cert; - std::string ssl_chain; - std::string ssl_key; -}; - -} - -RPC::Setup -setup_RPC (Section const& s); +// VFALCO DEPRECATED +extern Config& getConfig(); } // ripple diff --git a/src/ripple/core/ConfigSections.h b/src/ripple/core/ConfigSections.h index c6bf45ae6c..3e3b7640b6 100644 --- a/src/ripple/core/ConfigSections.h +++ b/src/ripple/core/ConfigSections.h @@ -22,12 +22,7 @@ namespace ripple { -// VFALCO NOTE -// -// Please use this style for all new sections -// And if you're feeling generous, convert all the -// existing macros to this format as well. -// +// VFALCO DEPRECATED in favor of the BasicConfig interface struct ConfigSection { static std::string nodeDatabase () { return "node_db"; } @@ -58,23 +53,10 @@ struct ConfigSection #define SECTION_PATH_SEARCH "path_search" #define SECTION_PATH_SEARCH_FAST "path_search_fast" #define SECTION_PATH_SEARCH_MAX "path_search_max" -#define SECTION_PEER_IP "peer_ip" -#define SECTION_PEER_PORT "peer_port" #define SECTION_PEER_PRIVATE "peer_private" #define SECTION_PEERS_MAX "peers_max" -#define SECTION_RPC_ALLOW_REMOTE "rpc_allow_remote" #define SECTION_RPC_ADMIN_ALLOW "rpc_admin_allow" -#define SECTION_RPC_ADMIN_USER "rpc_admin_user" -#define SECTION_RPC_ADMIN_PASSWORD "rpc_admin_password" -#define SECTION_RPC_IP "rpc_ip" -#define SECTION_RPC_PORT "rpc_port" -#define SECTION_RPC_USER "rpc_user" -#define SECTION_RPC_PASSWORD "rpc_password" #define SECTION_RPC_STARTUP "rpc_startup" -#define SECTION_RPC_SECURE "rpc_secure" -#define SECTION_RPC_SSL_CERT "rpc_ssl_cert" -#define SECTION_RPC_SSL_CHAIN "rpc_ssl_chain" -#define SECTION_RPC_SSL_KEY "rpc_ssl_key" #define SECTION_SMS_FROM "sms_from" #define SECTION_SMS_KEY "sms_key" #define SECTION_SMS_SECRET "sms_secret" @@ -87,16 +69,7 @@ struct ConfigSection #define SECTION_VALIDATORS_FILE "validators_file" #define SECTION_VALIDATION_QUORUM "validation_quorum" #define SECTION_VALIDATION_SEED "validation_seed" -#define SECTION_WEBSOCKET_PUBLIC_IP "websocket_public_ip" -#define SECTION_WEBSOCKET_PUBLIC_PORT "websocket_public_port" -#define SECTION_WEBSOCKET_PUBLIC_SECURE "websocket_public_secure" #define SECTION_WEBSOCKET_PING_FREQ "websocket_ping_frequency" -#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_WEBSOCKET_SSL_CHAIN "websocket_ssl_chain" -#define SECTION_WEBSOCKET_SSL_KEY "websocket_ssl_key" #define SECTION_VALIDATORS "validators" #define SECTION_VALIDATORS_SITE "validators_site" diff --git a/src/ripple/core/impl/Config.cpp b/src/ripple/core/impl/Config.cpp index 5c69c05ed8..5874b3a67a 100644 --- a/src/ripple/core/impl/Config.cpp +++ b/src/ripple/core/impl/Config.cpp @@ -223,32 +223,13 @@ parseAddresses (OutputSequence& out, InputIterator first, InputIterator last, //------------------------------------------------------------------------------ Config::Config () - : m_rpcPort (5001) { - //-------------------------------------------------------------------------- - // - // VFALCO NOTE Clean member area - // - - peerListeningPort = SYSTEM_PEER_PORT; - - // - // - // - //-------------------------------------------------------------------------- - // // Defaults // - RPC_SECURE = 0; - WEBSOCKET_PORT = 6562; - WEBSOCKET_PUBLIC_PORT = 6563; - WEBSOCKET_PUBLIC_SECURE = 1; - WEBSOCKET_SECURE = 0; WEBSOCKET_PING_FREQ = (5 * 60); - RPC_ALLOW_REMOTE = false; RPC_ADMIN_ALLOW.push_back (beast::IP::Endpoint::from_string("127.0.0.1")); PEER_PRIVATE = false; @@ -419,7 +400,6 @@ void Config::load () std::string strTemp; build (secConfig); - build_legacy(); // XXX Leak IniFileSections::mapped_type* smtTmp; @@ -484,8 +464,6 @@ void Config::load () (void) getSingleSection (secConfig, SECTION_VALIDATORS_SITE, VALIDATORS_SITE); - (void) getSingleSection (secConfig, SECTION_PEER_IP, PEER_IP); - if (getSingleSection (secConfig, SECTION_PEER_PRIVATE, strTemp)) PEER_PRIVATE = beast::lexicalCastThrow (strTemp); @@ -504,18 +482,8 @@ void Config::load () parsedAddresses.cbegin (), parsedAddresses.cend ()); } - (void) getSingleSection (secConfig, SECTION_RPC_ADMIN_PASSWORD, RPC_ADMIN_PASSWORD); - (void) getSingleSection (secConfig, SECTION_RPC_ADMIN_USER, RPC_ADMIN_USER); - (void) getSingleSection (secConfig, SECTION_RPC_IP, m_rpcIP); - (void) getSingleSection (secConfig, SECTION_RPC_PASSWORD, RPC_PASSWORD); - (void) getSingleSection (secConfig, SECTION_RPC_USER, RPC_USER); - insightSettings = parseKeyValueSection (secConfig, SECTION_INSIGHT); - //--------------------------------------- - // - // VFALCO BEGIN CLEAN - // nodeDatabase = parseKeyValueSection ( secConfig, ConfigSection::nodeDatabase ()); @@ -525,20 +493,6 @@ void Config::load () importNodeDatabase = parseKeyValueSection ( secConfig, ConfigSection::importNodeDatabase ()); - if (getSingleSection (secConfig, SECTION_PEER_PORT, strTemp)) - peerListeningPort = beast::lexicalCastThrow (strTemp); - - // - // VFALCO END CLEAN - // - //--------------------------------------- - - if (getSingleSection (secConfig, SECTION_RPC_PORT, strTemp)) - m_rpcPort = beast::lexicalCastThrow (strTemp); - - if (getSingleSection (secConfig, SECTION_RPC_ALLOW_REMOTE, strTemp)) - RPC_ALLOW_REMOTE = beast::lexicalCastThrow (strTemp); - if (getSingleSection (secConfig, SECTION_NODE_SIZE, strTemp)) { if (strTemp == "tiny") @@ -565,37 +519,9 @@ void Config::load () if (getSingleSection (secConfig, SECTION_ELB_SUPPORT, strTemp)) ELB_SUPPORT = beast::lexicalCastThrow (strTemp); - (void) getSingleSection (secConfig, SECTION_WEBSOCKET_IP, WEBSOCKET_IP); - - if (getSingleSection (secConfig, SECTION_WEBSOCKET_PORT, strTemp)) - WEBSOCKET_PORT = beast::lexicalCastThrow (strTemp); - - (void) getSingleSection (secConfig, SECTION_WEBSOCKET_PUBLIC_IP, WEBSOCKET_PUBLIC_IP); - - if (getSingleSection (secConfig, SECTION_WEBSOCKET_PUBLIC_PORT, strTemp)) - WEBSOCKET_PUBLIC_PORT = beast::lexicalCastThrow (strTemp); - - if (getSingleSection (secConfig, SECTION_WEBSOCKET_SECURE, strTemp)) - WEBSOCKET_SECURE = beast::lexicalCastThrow (strTemp); - - if (getSingleSection (secConfig, SECTION_WEBSOCKET_PUBLIC_SECURE, strTemp)) - WEBSOCKET_PUBLIC_SECURE = beast::lexicalCastThrow (strTemp); - if (getSingleSection (secConfig, SECTION_WEBSOCKET_PING_FREQ, strTemp)) WEBSOCKET_PING_FREQ = beast::lexicalCastThrow (strTemp); - getSingleSection (secConfig, SECTION_WEBSOCKET_SSL_CERT, WEBSOCKET_SSL_CERT); - getSingleSection (secConfig, SECTION_WEBSOCKET_SSL_CHAIN, WEBSOCKET_SSL_CHAIN); - getSingleSection (secConfig, SECTION_WEBSOCKET_SSL_KEY, WEBSOCKET_SSL_KEY); - - if (getSingleSection (secConfig, SECTION_RPC_SECURE, strTemp)) - RPC_SECURE = beast::lexicalCastThrow (strTemp); - - getSingleSection (secConfig, SECTION_RPC_SSL_CERT, RPC_SSL_CERT); - getSingleSection (secConfig, SECTION_RPC_SSL_CHAIN, RPC_SSL_CHAIN); - getSingleSection (secConfig, SECTION_RPC_SSL_KEY, RPC_SSL_KEY); - - getSingleSection (secConfig, SECTION_SSL_VERIFY_FILE, SSL_VERIFY_FILE); getSingleSection (secConfig, SECTION_SSL_VERIFY_DIR, SSL_VERIFY_DIR); @@ -816,150 +742,9 @@ beast::URL Config::getValidatorsURL () const return beast::parse_URL (VALIDATORS_SITE).second; } -//------------------------------------------------------------------------------ - -void Config::setRpcIpAndOptionalPort (std::string const& newAddress) -{ - beast::String const s (newAddress.c_str ()); - - int const colonPosition = s.lastIndexOfChar (':'); - - if (colonPosition != -1) - { - beast::String const ipPart = s.substring (0, colonPosition); - beast::String const portPart = s.substring (colonPosition + 1, s.length ()); - - setRpcIP (ipPart.toRawUTF8 ()); - setRpcPort (portPart.getIntValue ()); - } - else - { - setRpcIP (newAddress); - } -} - -//------------------------------------------------------------------------------ - -Config::Role Config::getAdminRole (Json::Value const& params, beast::IP::Endpoint const& remoteIp) const -{ - Config::Role role (Config::FORBID); - - bool const bPasswordSupplied = - params.isMember ("admin_user") || - params.isMember ("admin_password"); - - bool const bPasswordRequired = - ! this->RPC_ADMIN_USER.empty () || - ! this->RPC_ADMIN_PASSWORD.empty (); - - bool bPasswordWrong; - - if (bPasswordSupplied) - { - if (bPasswordRequired) - { - // Required, and supplied, check match - bPasswordWrong = - (this->RPC_ADMIN_USER != - (params.isMember ("admin_user") ? params["admin_user"].asString () : "")) - || - (this->RPC_ADMIN_PASSWORD != - (params.isMember ("admin_user") ? params["admin_password"].asString () : "")); - } - else - { - // Not required, but supplied - bPasswordWrong = false; - } - } - else - { - // Required but not supplied, - bPasswordWrong = bPasswordRequired; - } - - // Meets IP restriction for admin. - beast::IP::Endpoint const remote_addr (remoteIp.at_port (0)); - bool bAdminIP = false; - - for (auto const& allow_addr : RPC_ADMIN_ALLOW) - { - if (allow_addr == remote_addr) - { - bAdminIP = true; - break; - } - } - - if (bPasswordWrong // Wrong - || (bPasswordSupplied && !bAdminIP)) // Supplied and doesn't meet IP filter. - { - role = Config::FORBID; - } - // If supplied, password is correct. - else - { - // Allow admin, if from admin IP and no password is required or it was supplied and correct. - role = bAdminIP && (!bPasswordRequired || bPasswordSupplied) ? Config::ADMIN : Config::GUEST; - } - - return role; -} - beast::File const& Config::getModuleDatabasePath () { return m_moduleDbPath; } -//------------------------------------------------------------------------------ - -void -Config::build_legacy () -{ - //-------------------------------------------------------------------------- - // - // [rpc] - // - //-------------------------------------------------------------------------- - - remap("rpc_allow_remote", "allow_remote", "rpc"); - //remap("rpc_admin_allow", "admin_allow", "rpc"); // Not a key-value pair - remap("rpc_admin_user", "admin_user", "rpc"); - remap("rpc_admin_password", "admin_password", "rpc"); - remap("rpc_ip", "ip", "rpc"); - remap("rpc_port", "port", "rpc"); - remap("rpc_user", "user", "rpc"); - remap("rpc_password", "password", "rpc"); - //remap("rpc_startup", "startup", "rpc"); // Not a key-value pair - remap("rpc_secure", "secure", "rpc"); - remap("rpc_ssl_cert", "ssl_cert", "rpc"); - remap("rpc_ssl_chain", "ssl_chain", "rpc"); - remap("rpc_ssl_key", "ssl_key", "rpc"); - -#if DUMP_CONFIG - beast::debug_ostream log; - log << (BasicConfig const&)*this; -#endif -} - -//------------------------------------------------------------------------------ - -RPC::Setup -setup_RPC (Section const& s) -{ - RPC::Setup c; - set (c.allow_remote, "allow_remote", s); - set (c.admin_user, "admin_user", s); - set (c.admin_password, "admin_password", s); - set (c.ip, "ip", s); - set (c.port, "port", s); - set (c.user, "user", s); - set (c.password, "password", s); - set (c.secure, "secure", s); - set (c.ssl_cert, "ssl_cert", s); - set (c.ssl_chain, "ssl_chain", s); - set (c.ssl_key, "ssl_key", s); - return c; -} - } // ripple diff --git a/src/ripple/http/README.md b/src/ripple/http/README.md deleted file mode 100644 index bf51b45b62..0000000000 --- a/src/ripple/http/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# HTTP Server - -A generic HTTP server. This is used for doing asynchronous RPC -currently, although it could be expanded to perform more activities -such as reporting to the elastic load balancer or providing server -statistics. diff --git a/src/ripple/http/Server.h b/src/ripple/http/Server.h deleted file mode 100644 index 571cfaabcf..0000000000 --- a/src/ripple/http/Server.h +++ /dev/null @@ -1,146 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_HTTP_SERVER_H_INCLUDED -#define RIPPLE_HTTP_SERVER_H_INCLUDED - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ripple { -namespace HTTP { - -//------------------------------------------------------------------------------ - -/** Configuration information for a server listening port. */ -struct Port -{ - enum class Security - { - no_ssl, - allow_ssl, - require_ssl - }; - - Security security = Security::no_ssl; - std::uint16_t port = 0; - beast::IP::Endpoint addr; - SSLContext* context = nullptr; - - Port() = default; - Port (std::uint16_t port_, beast::IP::Endpoint const& addr_, - Security security_, SSLContext* context_); - - static - void - parse (Port& result, Section const& section, std::ostream& log); -}; - -//------------------------------------------------------------------------------ - -class Server; -class Session; - -/** Processes all sessions. - Thread safety: - Must be safe to call concurrently from any number of foreign threads. -*/ -struct Handler -{ - /** Called when the connection is accepted and we know remoteAddress. */ - virtual void onAccept (Session& session) = 0; - - /** Called when we have a complete HTTP request. */ - virtual void onRequest (Session& session) = 0; - - /** Called when the session ends. - Guaranteed to be called once. - @param errorCode Non zero for a failed connection. - */ - virtual void onClose (Session& session, - boost::system::error_code const& ec) = 0; - - /** Called when the server has finished its stop. */ - virtual void onStopped (Server& server) = 0; -}; - -//------------------------------------------------------------------------------ - -/** Multi-threaded, asynchronous HTTP server. */ -class Server -{ -public: - /** Destroy the server. - The server is closed if it is not already closed. This call - blocks until the server has stopped. - */ - virtual - ~Server() = default; - - /** Returns the Journal associated with the server. */ - virtual - beast::Journal - journal() = 0; - - /** Set the listening port settings. - This may only be called once. - */ - virtual - void - ports (std::vector const& v) = 0; - - virtual - void - onWrite (beast::PropertyStream::Map& map) = 0; - - /** Close the server. - The close is performed asynchronously. The handler will be notified - when the server has stopped. The server is considered stopped when - there are no pending I/O completion handlers and all connections - have closed. - Thread safety: - Safe to call concurrently from any thread. - */ - virtual - void - close() = 0; - - /** Parse configuration settings into a list of ports. */ - static - std::vector - parse (BasicConfig const& config, std::ostream& log); -}; - -/** Create the HTTP server using the specified handler. */ -std::unique_ptr -make_Server (Handler& handler, beast::Journal journal); - -//------------------------------------------------------------------------------ - -} // HTTP -} // ripple - -#endif diff --git a/src/ripple/http/TODO.md b/src/ripple/http/TODO.md deleted file mode 100644 index 960cda5dd5..0000000000 --- a/src/ripple/http/TODO.md +++ /dev/null @@ -1 +0,0 @@ -# HTTP Server TODO diff --git a/src/ripple/http/impl/SSLPeer.h b/src/ripple/http/impl/SSLPeer.h deleted file mode 100644 index 1aab1d2c48..0000000000 --- a/src/ripple/http/impl/SSLPeer.h +++ /dev/null @@ -1,126 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_HTTP_SSLPEER_H_INCLUDED -#define RIPPLE_HTTP_SSLPEER_H_INCLUDED - -#include - -namespace ripple { -namespace HTTP { - -class SSLPeer - : public Peer - , public std::enable_shared_from_this -{ -private: - friend class Peer ; - using next_layer_type = boost::asio::ip::tcp::socket; - using socket_type = boost::asio::ssl::stream ; - next_layer_type next_layer_; - socket_type socket_; - -public: - template - SSLPeer (Door& door, beast::Journal journal, endpoint_type endpoint, - ConstBufferSequence const& buffers, next_layer_type&& socket); - - void - run(); - -private: - void - do_handshake (boost::asio::yield_context yield); - - void - do_request(); - - void - do_close(); - - void - on_shutdown (error_code ec); -}; - -//------------------------------------------------------------------------------ - -template -SSLPeer::SSLPeer (Door& door, beast::Journal journal, - endpoint_type endpoint, ConstBufferSequence const& buffers, - boost::asio::ip::tcp::socket&& socket) - : Peer (door, socket.get_io_service(), journal, endpoint, buffers) - , next_layer_ (std::move(socket)) - , socket_ (next_layer_, door_.port().context->get()) -{ -} - -// Called when the acceptor accepts our socket. -void -SSLPeer::run () -{ - door_.server().handler().onAccept (session()); - if (! socket_.lowest_layer().is_open()) - return; - - boost::asio::spawn (strand_, std::bind (&SSLPeer::do_handshake, - shared_from_this(), std::placeholders::_1)); -} - -void -SSLPeer::do_handshake (boost::asio::yield_context yield) -{ - error_code ec; - start_timer(); - read_buf_.consume(socket_.async_handshake( - socket_type::server, read_buf_.data(), yield[ec])); - cancel_timer(); - if (ec) - return fail (ec, "handshake"); - boost::asio::spawn (strand_, std::bind (&SSLPeer::do_read, - shared_from_this(), std::placeholders::_1)); -} - -void -SSLPeer::do_request() -{ - ++request_count_; - door_.server().handler().onRequest (session()); -} - -void -SSLPeer::do_close() -{ - error_code ec; - start_timer(); - socket_.async_shutdown (strand_.wrap (std::bind ( - &SSLPeer::on_shutdown, shared_from_this(), - std::placeholders::_1))); - cancel_timer(); -} - -void -SSLPeer::on_shutdown (error_code ec) -{ - socket_.lowest_layer().close(ec); -} - -} -} - -#endif diff --git a/src/ripple/http/impl/Server.cpp b/src/ripple/http/impl/Server.cpp deleted file mode 100644 index 81e8cfd73b..0000000000 --- a/src/ripple/http/impl/Server.cpp +++ /dev/null @@ -1,74 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include // -#include - -namespace ripple { -namespace HTTP { - -std::unique_ptr -make_Server (Handler& handler, beast::Journal journal) -{ - return std::make_unique(handler, journal); -} - -//------------------------------------------------------------------------------ - -void -Port::parse (Port& port, - Section const& section, std::ostream& log) -{ -} - -std::vector -Server::parse (BasicConfig const& config, std::ostream& log) -{ - std::vector result; - - if (! config.exists("doors")) - { - log << - "Missing section: [doors]\n"; - return result; - } - - Port common; - Port::parse (common, config["doors"], log); - - auto const& names = config.section("doors").values(); - for (auto const& name : names) - { - if (! config.exists(name)) - { - log << - "Missing section: [" << name << "]\n"; - //throw std:: - } - result.push_back(common); - Port::parse (result.back(), config[name], log); - } - - return result; -} - -} -} diff --git a/src/ripple/http/impl/Types.h b/src/ripple/http/impl/Types.h deleted file mode 100644 index e404176632..0000000000 --- a/src/ripple/http/impl/Types.h +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_HTTP_TYPES_H_INCLUDED -#define RIPPLE_HTTP_TYPES_H_INCLUDED - -namespace ripple { -namespace HTTP { - -typedef boost::system::error_code error_code; -typedef boost::asio::ip::tcp Protocol; -typedef boost::asio::ip::address address; -typedef Protocol::endpoint endpoint_t; -typedef Protocol::acceptor acceptor; -typedef Protocol::socket socket; - -inline std::string to_string (address const& addr) -{ - return addr.to_string(); -} - -inline std::string to_string (endpoint_t const& endpoint) -{ - std::stringstream ss; - ss << to_string (endpoint.address()); - if (endpoint.port() != 0) - ss << ":" << std::dec << endpoint.port(); - return std::string (ss.str()); -} - -inline endpoint_t to_asio (Port const& port) -{ - if (port.addr.is_v4()) - { - beast::IP::AddressV4 v4 (port.addr.to_v4()); - std::string const& s (to_string (v4)); - return endpoint_t (address().from_string (s), port.port); - } - - //IP::Endpoint::V6 v6 (ep.v6()); - return endpoint_t (); -} - -inline beast::IP::Endpoint from_asio (endpoint_t const& endpoint) -{ - std::stringstream ss (to_string (endpoint)); - beast::IP::Endpoint ep; - ss >> ep; - return ep; -} - -} -} - -#endif diff --git a/src/ripple/net/impl/RPCCall.cpp b/src/ripple/net/impl/RPCCall.cpp index 3061aefc26..066d0af187 100644 --- a/src/ripple/net/impl/RPCCall.cpp +++ b/src/ripple/net/impl/RPCCall.cpp @@ -17,11 +17,12 @@ */ //============================================================================== +#include #include #include +#include #include #include -#include #include #include @@ -38,6 +39,42 @@ static inline bool isSwitchChar (char c) #endif } +// +// HTTP protocol +// +// This ain't Apache. We're just using HTTP header for the length field +// and to be compatible with other JSON-RPC implementations. +// + +std::string createHTTPPost ( + std::string const& strHost, + std::string const& strPath, + std::string const& strMsg, + std::map const& mapRequestHeaders) +{ + std::ostringstream s; + + // CHECKME this uses a different version than the replies below use. Is + // this by design or an accident or should it be using + // BuildInfo::getFullVersionString () as well? + + s << "POST " + << (strPath.empty () ? "/" : strPath) + << " HTTP/1.0\r\n" + << "User-Agent: " SYSTEM_NAME "-json-rpc/v1\r\n" + << "Host: " << strHost << "\r\n" + << "Content-Type: application/json\r\n" + << "Content-Length: " << strMsg.size () << "\r\n" + << "Accept: application/json\r\n"; + + for (auto const& item : mapRequestHeaders) + s << item.first << ": " << item.second << "\r\n"; + + s << "\r\n" << strMsg; + + return s.str (); +} + class RPCParser { private: @@ -887,6 +924,24 @@ public: //------------------------------------------------------------------------------ +// +// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, +// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were +// unspecified (HTTP errors and contents of 'error'). +// +// 1.0 spec: http://json-rpc.org/wiki/specification +// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http +// + +std::string JSONRPCRequest (std::string const& strMethod, Json::Value const& params, Json::Value const& id) +{ + Json::Value request; + request[jss::method] = strMethod; + request[jss::params] = params; + request[jss::id] = id; + return to_string (request) + "\n"; +} + struct RPCCallImp { // VFALCO NOTE Is this a to-do comment or a doc comment? @@ -909,7 +964,7 @@ struct RPCCallImp 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 (std::string ("server returned HTTP error %d") + std::to_string (iStatus)); + throw std::runtime_error (std::string ("server returned HTTP error ") + std::to_string (iStatus)); else if (strData.empty ()) throw std::runtime_error ("no response from server"); @@ -943,7 +998,6 @@ struct RPCCallImp WriteLog (lsDEBUG, RPCParser) << "requestRPC: strPath='" << strPath << "'"; std::ostream osRequest (&sb); - osRequest << createHTTPPost ( strHost, @@ -987,35 +1041,35 @@ int RPCCall::fromCommandLine (const std::vector& vCmd) } else { - auto setup = setup_RPC (getConfig()["rpc"]); + auto const setup = setup_ServerHandler(getConfig(), std::cerr); Json::Value jvParams (Json::arrayValue); + if (!setup.client.admin_user.empty ()) + jvRequest["admin_user"] = setup.client.admin_user; + + if (!setup.client.admin_password.empty ()) + jvRequest["admin_password"] = setup.client.admin_password; + jvParams.append (jvRequest); - if (!setup.admin_user.empty ()) - jvRequest["admin_user"] = setup.admin_user; - - if (!setup.admin_password.empty ()) - jvRequest["admin_password"] = setup.admin_password; - - boost::asio::io_service isService; - - fromNetwork ( - isService, - setup.ip, - setup.port, - setup.admin_user, - setup.admin_password, - "", - jvRequest.isMember ("method") // Allow parser to rewrite method. - ? jvRequest["method"].asString () : vCmd[0], - jvParams, // Parsed, execute. - setup.secure != 0, // Use SSL - std::bind (RPCCallImp::callRPCHandler, &jvOutput, - std::placeholders::_1)); - - isService.run (); // This blocks until there is no more outstanding async calls. + { + boost::asio::io_service isService; + fromNetwork ( + isService, + setup.client.ip, + setup.client.port, + setup.client.user, + setup.client.password, + "", + jvRequest.isMember ("method") // Allow parser to rewrite method. + ? jvRequest["method"].asString () : vCmd[0], + jvParams, // Parsed, execute. + setup.client.secure != 0, // Use SSL + std::bind (RPCCallImp::callRPCHandler, &jvOutput, + std::placeholders::_1)); + isService.run(); // This blocks until there is no more outstanding async calls. + } if (jvOutput.isMember ("result")) { diff --git a/src/ripple/net/impl/RPCDoor.cpp b/src/ripple/net/impl/RPCDoor.cpp deleted file mode 100644 index e6179bdca4..0000000000 --- a/src/ripple/net/impl/RPCDoor.cpp +++ /dev/null @@ -1,144 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { - -class RPCDoorImp : public RPCDoor, public beast::LeakChecked -{ -public: - RPCDoorImp (boost::asio::io_service& io_service, RPCServer::Handler& handler) - : m_rpcServerHandler (handler) - , mAcceptor (io_service, - boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string (getConfig ().getRpcIP ()), getConfig ().getRpcPort ())) - , mDelayTimer (io_service) - , m_sslContext ((getConfig ().RPC_SECURE == 0) ? - RippleSSLContext::createBare () : - RippleSSLContext::createAuthenticated ( - getConfig ().RPC_SSL_KEY, - getConfig ().RPC_SSL_CERT, - getConfig ().RPC_SSL_CHAIN)) - { - WriteLog (lsINFO, RPCDoor) << - "RPC port: " << getConfig ().getRpcAddress() << - " allow remote: " << getConfig ().RPC_ALLOW_REMOTE; - - startListening (); - } - - //-------------------------------------------------------------------------- - - ~RPCDoorImp () - { - WriteLog (lsINFO, RPCDoor) << - "RPC port: " << getConfig ().getRpcAddress() << - " allow remote: " << getConfig ().RPC_ALLOW_REMOTE; - } - - //-------------------------------------------------------------------------- - - void startListening () - { - RPCServerImp::pointer new_connection (std::make_shared ( - std::ref (mAcceptor.get_io_service ()), - std::ref (m_sslContext->get ()), - std::ref (m_rpcServerHandler))); - - mAcceptor.set_option (boost::asio::ip::tcp::acceptor::reuse_address (true)); - - mAcceptor.async_accept (new_connection->getRawSocket (), - new_connection->getRemoteEndpoint (), - std::bind (&RPCDoorImp::handleConnect, this, - new_connection, beast::asio::placeholders::error)); - } - - //-------------------------------------------------------------------------- - - bool isClientAllowed (std::string const& ip) - { - if (getConfig ().RPC_ALLOW_REMOTE) - return true; - - // VFALCO TODO Represent ip addresses as a structure. Use isLoopback() member here - // - if (ip == "127.0.0.1") - return true; - - return false; - } - - //-------------------------------------------------------------------------- - - void handleConnect (RPCServerImp::pointer new_connection, - boost::system::error_code const& error) - { - bool delay = false; - - if (!error) - { - // Restrict callers by IP - std::string client_ip ( - new_connection->getRemoteEndpoint ().address ().to_string ()); - - if (! isClientAllowed (client_ip)) - { - startListening (); - return; - } - - new_connection->getSocket ().async_handshake (AutoSocket::ssl_socket::server, - std::bind (&RPCServer::connected, new_connection)); - } - else - { - if (error == boost::system::errc::too_many_files_open) - delay = true; - - WriteLog (lsINFO, RPCDoor) << "RPCDoorImp::handleConnect Error: " << error; - } - - if (delay) - { - mDelayTimer.expires_from_now (boost::posix_time::milliseconds (1000)); - mDelayTimer.async_wait (std::bind (&RPCDoorImp::startListening, this)); - } - else - { - startListening (); - } - } - -private: - RPCServer::Handler& m_rpcServerHandler; - boost::asio::ip::tcp::acceptor mAcceptor; - boost::asio::deadline_timer mDelayTimer; - std::unique_ptr m_sslContext; -}; - -//------------------------------------------------------------------------------ - -// VFALCO TODO Return std::unique_ptr here -RPCDoor* RPCDoor::New (boost::asio::io_service& io_service, RPCServer::Handler& handler) -{ - return new RPCDoorImp (io_service, handler); -} - -} diff --git a/src/ripple/overlay/Overlay.h b/src/ripple/overlay/Overlay.h index f399b66e92..5eb9794bb3 100644 --- a/src/ripple/overlay/Overlay.h +++ b/src/ripple/overlay/Overlay.h @@ -21,10 +21,15 @@ #define RIPPLE_OVERLAY_OVERLAY_H_INCLUDED #include +#include +#include +#include #include #include #include #include // +#include +#include namespace boost { namespace asio { namespace ssl { class context; } } } @@ -66,6 +71,20 @@ public: virtual ~Overlay() = default; + /** Accept a legacy protocol handshake connection. */ + virtual + void + onLegacyPeerHello (std::unique_ptr&& ssl_bundle, + boost::asio::const_buffer buffer, + boost::asio::ip::tcp::endpoint remote_address) = 0; + + /** Conditionally accept an incoming HTTP request. */ + virtual + Handoff + onHandoff (std::unique_ptr && bundle, + beast::http::message&& request, + boost::asio::ip::tcp::endpoint remote_address) = 0; + /** Establish a peer connection to the specified endpoint. The call returns immediately, the connection attempt is performed asynchronously. diff --git a/src/ripple/overlay/README.md b/src/ripple/overlay/README.md index 9e597d6746..f892386ae6 100644 --- a/src/ripple/overlay/README.md +++ b/src/ripple/overlay/README.md @@ -65,10 +65,10 @@ custom fields to communicate protocol specific information: GET / HTTP/1.1 User-Agent: Ripple-0.26.0 Local-Address: 192.168.0.101:8421 -Remote-Address: 208.239.76.97:51234 Upgrade: Ripple/1.2, Ripple/1.3 Connection: Upgrade Connect-As: Leaf, Peer +Content-Length: 0 Accept-Encoding: identity, zlib, snappy Protocol-Public-Key: aBRoQibi2jpDofohooFuzZi9nEzKw9Zdfc4ExVNmuXHaJpSPh8uJ Protocol-Session-Cookie: 71ED064155FFADFA38782C5E0158CB26 @@ -79,9 +79,8 @@ HTTP response indicating the connection status: ``` HTTP/1.1 101 Switching Protocols -Server: Ripple-0.26.0-rc1 -Local-Address: 192.168.0.101:8421 -Remote-Address: 63.104.209.13:8421 +Server: Ripple-0.26.5 +Remote-Address: 63.104.209.13 Upgrade: Ripple/1.2 Connection: Upgrade Connect-As: Leaf @@ -90,6 +89,22 @@ Protocol-Public-Key: aBRoQibi2jpDofohooFuzZi9nEzKw9Zdfc4ExVNmuXHaJpSPh8uJ Protocol-Session-Cookie: 71ED064155FFADFA38782C5E0158CB26 ``` +If the remote peer has no available slots, the HTTP status code 503 (Service +Unavailable) is returned, with an optional content body in JSON format that +may contain additional information such as IP and port addresses of other +servers that may have open slots: + +``` +HTTP/1.1 503 Service Unavailable +Server: Ripple-0.26.5 +Remote-Address: 63.104.209.13 +Content-Length: 253 +Content-Type: application/json +{"peer-ips":["54.68.219.39:51235","54.187.191.179:51235", +"107.150.55.21:6561","54.186.230.77:51235","54.187.110.243:51235", +"85.127.34.221:51235","50.43.33.236:51235","54.187.138.75:51235"]} +``` + ### Fields * *URL* @@ -113,15 +128,12 @@ Protocol-Session-Cookie: 71ED064155FFADFA38782C5E0158CB26 Contains information about the software providing the response. The specification is identical to RFC2616 Section 14.38. -* `Local-Address` +* `Remote-Address` (optional) - This field must be present and contain the string representation of the - IP and port address of the local end of the connection as seen by the peer. - -* `Remote-Address` - - This field must be present and contain the string representation of the - IP and port address of the remote end of the connection as seen by the peer. + This optional field contains the string representation of the IP + address of the remote end of the connection as seen by the peer. + By observing values of this field from a sufficient number of different + servers, a peer making outgoing connections can deduce its own IP address. * `Upgrade` diff --git a/src/ripple/overlay/impl/OverlayImpl.cpp b/src/ripple/overlay/impl/OverlayImpl.cpp index 586a3985f2..130a6b2de3 100644 --- a/src/ripple/overlay/impl/OverlayImpl.cpp +++ b/src/ripple/overlay/impl/OverlayImpl.cpp @@ -17,9 +17,9 @@ */ //============================================================================== -#include +#include +#include #include -#include #include #include #include @@ -100,8 +100,8 @@ OverlayImpl::Timer::on_timer (error_code ec) } overlay_.m_peerFinder->once_per_second(); - overlay_.sendpeers(); - overlay_.autoconnect(); + overlay_.sendEndpoints(); + overlay_.autoConnect(); timer_.expires_from_now (std::chrono::seconds(1)); timer_.async_wait(overlay_.strand_.wrap(std::bind( @@ -114,8 +114,8 @@ OverlayImpl::Timer::on_timer (error_code ec) OverlayImpl::OverlayImpl ( Setup const& setup, Stoppable& parent, + ServerHandler& serverHandler, Resource::Manager& resourceManager, - SiteFiles::Manager& siteFiles, beast::File const& pathToDbFileOrDirectory, Resolver& resolver, boost::asio::io_service& io_service) @@ -125,6 +125,7 @@ OverlayImpl::OverlayImpl ( , strand_ (io_service_) , setup_(setup) , journal_ (deprecatedLogs().journal("Overlay")) + , serverHandler_(serverHandler) , m_resourceManager (resourceManager) , m_peerFinder (PeerFinder::make_Manager (*this, io_service, pathToDbFileOrDirectory, get_seconds_clock(), @@ -146,50 +147,113 @@ OverlayImpl::~OverlayImpl () cond_.wait (lock, [this] { return list_.empty(); }); } +//------------------------------------------------------------------------------ + void -OverlayImpl::accept (socket_type&& socket) +OverlayImpl::onLegacyPeerHello ( + std::unique_ptr&& ssl_bundle, + boost::asio::const_buffer buffer, + boost::asio::ip::tcp::endpoint remote_address) { - // An error getting an endpoint means the connection closed. - // Just do nothing and the socket will be closed by the caller. - boost::system::error_code ec; - auto const local_endpoint_native (socket.local_endpoint (ec)); - if (ec) - return; - auto const remote_endpoint_native (socket.remote_endpoint (ec)); + error_code ec; + auto const local_endpoint (ssl_bundle->socket.local_endpoint(ec)); if (ec) return; - auto const local_endpoint ( - beast::IPAddressConversion::from_asio (local_endpoint_native)); - auto const remote_endpoint ( - beast::IPAddressConversion::from_asio (remote_endpoint_native)); + auto const slot = m_peerFinder->new_inbound_slot ( + beast::IPAddressConversion::from_asio(local_endpoint), + beast::IPAddressConversion::from_asio(remote_address)); - PeerFinder::Slot::ptr const slot (m_peerFinder->new_inbound_slot ( - local_endpoint, remote_endpoint)); - - if (slot == nullptr) - return; - - PeerImp::ptr const peer (std::make_shared ( - std::move (socket), remote_endpoint, *this, m_resourceManager, - *m_peerFinder, slot, setup_.context)); - - { - std::lock_guard lock (mutex_); - { - std::pair const result ( - m_peers.emplace (slot, peer)); - assert (result.second); - (void) result.second; - } - list_.emplace(peer.get(), peer); - - // This has to happen while holding the lock, - // otherwise the socket might not be canceled during a stop. - peer->start(); - } + addpeer (std::make_shared(std::move(ssl_bundle), + boost::asio::const_buffers_1(buffer), + beast::IPAddressConversion::from_asio(remote_address), + *this, m_resourceManager, *m_peerFinder, slot)); } +Handoff +OverlayImpl::onHandoff (std::unique_ptr && ssl_bundle, + beast::http::message&& request, + boost::asio::ip::tcp::endpoint remote_address) +{ + Handoff handoff; + if (! isPeerUpgrade(request)) + return handoff; + + error_code ec; + auto const local_endpoint (ssl_bundle->socket.local_endpoint(ec)); + if (ec) + { + // log? + // Since we don't call std::move the socket will be closed. + handoff.moved = false; + return handoff; + } + + // TODO Validate HTTP request + + auto const slot = m_peerFinder->new_inbound_slot ( + beast::IPAddressConversion::from_asio(local_endpoint), + beast::IPAddressConversion::from_asio(remote_address)); + +#if 0 + if (slot == nullptr) +#else + // For now, always redirect. + if (true) +#endif + { + // Full, give them some addresses + handoff.response = makeRedirectResponse(slot, request); + handoff.keep_alive = request.keep_alive(); + return handoff; + } + + addpeer (std::make_shared(std::move(ssl_bundle), + std::move(request), beast::IPAddressConversion::from_asio(remote_address), + *this, m_resourceManager, *m_peerFinder, slot)); + handoff.moved = true; + return handoff; +} + +//------------------------------------------------------------------------------ + +bool +OverlayImpl::isPeerUpgrade(beast::http::message const& request) +{ + if (! request.upgrade()) + return false; + if (request.headers["Upgrade"] != "Ripple/1.2") + return false; + return true; +} + +std::shared_ptr +OverlayImpl::makeRedirectResponse (PeerFinder::Slot::ptr const& slot, + beast::http::message const& request) +{ + Json::Value json(Json::objectValue); + { + auto const result = m_peerFinder->redirect(slot); + Json::Value& ips = (json["peer-ips"] = Json::arrayValue); + for (auto const& _ : m_peerFinder->redirect(slot)) + ips.append(_.address.to_string()); + } + + beast::http::message m; + m.request(false); + m.status(503); + m.reason("Service Unavailable"); + m.version(request.version()); + if (request.version() == std::make_pair(1, 0)) + { + //? + } + auto const response = HTTP::make_JsonWriter (m, json); + return response; +} + +//------------------------------------------------------------------------------ + void OverlayImpl::connect (beast::IP::Endpoint const& remote_endpoint) { @@ -201,24 +265,8 @@ OverlayImpl::connect (beast::IP::Endpoint const& remote_endpoint) if (slot == nullptr) return; - PeerImp::ptr const peer (std::make_shared ( - remote_endpoint, io_service_, *this, m_resourceManager, - *m_peerFinder, slot, setup_.context)); - - { - std::lock_guard lock (mutex_); - { - std::pair const result ( - m_peers.emplace (slot, peer)); - assert (result.second); - (void) result.second; - } - list_.emplace(peer.get(), peer); - - // This has to happen while holding the lock, - // otherwise the socket might not be canceled during a stop. - peer->start (); - } + addpeer (std::make_shared (remote_endpoint, io_service_, *this, + m_resourceManager, *m_peerFinder, slot, setup_.context)); } Peer::ShortId @@ -245,7 +293,7 @@ OverlayImpl::remove (PeerFinder::Slot::ptr const& slot) //-------------------------------------------------------------------------- void -OverlayImpl::onPrepare () +OverlayImpl::onPrepare() { PeerFinder::Config config; @@ -254,22 +302,20 @@ OverlayImpl::onPrepare () config.outPeers = config.calcOutPeers(); - config.wantIncoming = - (! getConfig ().PEER_PRIVATE) && - (getConfig().peerListeningPort != 0); + auto const port = serverHandler_.setup().overlay.port; + config.wantIncoming = + (! getConfig ().PEER_PRIVATE) && (port != 0); // if it's a private peer or we are running as standalone // automatic connections would defeat the purpose. config.autoConnect = !getConfig().RUN_STANDALONE && !getConfig().PEER_PRIVATE; - - config.listeningPort = getConfig().peerListeningPort; - + config.listeningPort = port; config.features = ""; // Enforce business rules - config.applyTuning (); + config.applyTuning(); m_peerFinder->setConfig (config); @@ -311,14 +357,6 @@ OverlayImpl::onPrepare () m_peerFinder->addFixedPeer (name, addresses); }); } - - // Configure the peer doors, which allow the server to accept incoming - // peer connections: - if (! getConfig ().RUN_STANDALONE) - { - m_doorDirect = make_PeerDoor (*this, getConfig ().PEER_IP, - getConfig ().peerListeningPort, io_service_); - } } void @@ -334,11 +372,6 @@ OverlayImpl::onStart () void OverlayImpl::onStop () { - if (m_doorDirect) - m_doorDirect->stop(); - if (m_doorProxy) - m_doorProxy->stop(); - strand_.dispatch(std::bind(&OverlayImpl::close, this)); } @@ -470,6 +503,14 @@ OverlayImpl::remove (Child& child) checkStopped(); } +// Caller must hold the mutex +void +OverlayImpl::checkStopped () +{ + if (isStopping() && areChildrenStopped () && list_.empty()) + stopped(); +} + void OverlayImpl::close() { @@ -487,19 +528,35 @@ OverlayImpl::close() } } -// Check for the stopped condition -// Caller must hold the mutex void -OverlayImpl::checkStopped () +OverlayImpl::addpeer (std::shared_ptr const& peer) { - if (isStopping() && areChildrenStopped () && list_.empty()) - stopped(); + std::lock_guard lock (mutex_); + { + std::pair const result ( + m_peers.emplace (peer->slot(), peer)); + assert (result.second); + (void) result.second; + } + list_.emplace(peer.get(), peer); + + // This has to happen while holding the lock, + // otherwise the socket might not be canceled during a stop. + peer->start(); } void -OverlayImpl::sendpeers() +OverlayImpl::autoConnect() { - auto const result = m_peerFinder->sendpeers(); + auto const result = m_peerFinder->autoconnect(); + for (auto addr : result) + connect (addr); +} + +void +OverlayImpl::sendEndpoints() +{ + auto const result = m_peerFinder->buildEndpointsForPeers(); for (auto const& e : result) { // VFALCO TODO Make sure this doesn't race with closing the peer @@ -515,13 +572,6 @@ OverlayImpl::sendpeers() } } -void -OverlayImpl::autoconnect() -{ - auto const result = m_peerFinder->autoconnect(); - for (auto addr : result) - connect (addr); -} //------------------------------------------------------------------------------ @@ -540,7 +590,7 @@ setup_Overlay (BasicConfig const& config) setup.promote = Overlay::Promote::always; else setup.promote = Overlay::Promote::automatic; - setup.context = make_ssl_context(); + setup.context = make_SSLContext(); return setup; } @@ -548,14 +598,14 @@ std::unique_ptr make_Overlay ( Overlay::Setup const& setup, beast::Stoppable& parent, + ServerHandler& serverHandler, Resource::Manager& resourceManager, - SiteFiles::Manager& siteFiles, beast::File const& pathToDbFileOrDirectory, Resolver& resolver, boost::asio::io_service& io_service) { - return std::make_unique (setup, parent, resourceManager, - siteFiles, pathToDbFileOrDirectory, resolver, io_service); + return std::make_unique (setup, parent, serverHandler, + resourceManager, pathToDbFileOrDirectory, resolver, io_service); } } diff --git a/src/ripple/overlay/impl/OverlayImpl.h b/src/ripple/overlay/impl/OverlayImpl.h index adcf8f7813..cabbc948a6 100644 --- a/src/ripple/overlay/impl/OverlayImpl.h +++ b/src/ripple/overlay/impl/OverlayImpl.h @@ -21,7 +21,8 @@ #define RIPPLE_OVERLAY_OVERLAYIMPL_H_INCLUDED #include - +#include +#include #include #include #include @@ -42,7 +43,6 @@ namespace ripple { -class PeerDoor; class PeerImp; class OverlayImpl : public Overlay @@ -106,6 +106,8 @@ private: Setup setup_; beast::Journal journal_; + ServerHandler& serverHandler_; + Resource::Manager& m_resourceManager; std::unique_ptr m_peerFinder; @@ -119,12 +121,6 @@ private: /** Tracks peers by their session ID */ PeerByShortId m_shortIdMap; - /** The peer door for regular SSL connections */ - std::unique_ptr m_doorDirect; - - /** The peer door for proxy connections */ - std::unique_ptr m_doorProxy; - /** The resolver we use for peer hostnames */ Resolver& m_resolver; @@ -135,9 +131,9 @@ private: public: OverlayImpl (Setup const& setup, Stoppable& parent, - Resource::Manager& resourceManager, SiteFiles::Manager& siteFiles, - beast::File const& pathToDbFileOrDirectory, Resolver& resolver, - boost::asio::io_service& io_service); + ServerHandler& serverHandler, Resource::Manager& resourceManager, + beast::File const& pathToDbFileOrDirectory, + Resolver& resolver, boost::asio::io_service& io_service); ~OverlayImpl (); @@ -147,19 +143,28 @@ public: return setup_; } + ServerHandler& + serverHandler() + { + return serverHandler_; + } + + void + onLegacyPeerHello (std::unique_ptr&& ssl_bundle, + boost::asio::const_buffer buffer, + boost::asio::ip::tcp::endpoint remote_address) override; + + Handoff + onHandoff (std::unique_ptr && bundle, + beast::http::message&& request, + boost::asio::ip::tcp::endpoint remote_address) override; + PeerSequence - getActivePeers () override; + getActivePeers() override; Peer::ptr findPeerByShortID (Peer::ShortId const& id) override; - /** Process an incoming connection using the Peer protocol. - The caller transfers ownership of the socket via rvalue move. - @param socket A socket in the accepted state. - */ - void - accept (socket_type&& socket); - Peer::ShortId next_id(); @@ -186,6 +191,13 @@ private: OverlayImpl (OverlayImpl const&) = delete; OverlayImpl& operator= (OverlayImpl const&) = delete; + bool + isPeerUpgrade (beast::http::message const& request); + + std::shared_ptr + makeRedirectResponse (PeerFinder::Slot::ptr const& slot, + beast::http::message const& request); + void connect (beast::IP::Endpoint const& remote_endpoint) override; @@ -225,17 +237,20 @@ private: void remove (Child& child); - void - close(); - void checkStopped (); void - sendpeers(); + close(); void - autoconnect(); + addpeer (std::shared_ptr const& peer); + + void + autoConnect(); + + void + sendEndpoints(); }; } // ripple diff --git a/src/ripple/overlay/impl/PeerDoor.cpp b/src/ripple/overlay/impl/PeerDoor.cpp deleted file mode 100644 index 6a4eb1a04a..0000000000 --- a/src/ripple/overlay/impl/PeerDoor.cpp +++ /dev/null @@ -1,145 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include -#include - -namespace ripple { - -class PeerDoorImp - : public PeerDoor - , public beast::LeakChecked -{ -private: - using socket_type = boost::asio::ip::tcp::socket; - - OverlayImpl& m_overlay; - beast::Journal m_journal; - boost::asio::ip::tcp::acceptor m_acceptor; - boost::asio::deadline_timer m_acceptDelay; - socket_type m_socket; - -public: - PeerDoorImp (OverlayImpl& overlay, - boost::asio::ip::tcp::endpoint const &ep, - boost::asio::io_service& io_service) - : m_overlay (overlay) - , m_journal (deprecatedLogs().journal("PeerDoor")) - , m_acceptor (io_service, ep) - , m_acceptDelay (io_service) - , m_socket (io_service) - { - m_journal.info << - "Listening on " << beast::IPAddressConversion::from_asio ( - m_acceptor.local_endpoint()); - async_accept(); - } - - void - stop() - { - { - boost::system::error_code ec; - m_acceptDelay.cancel (ec); - } - - { - boost::system::error_code ec; - m_acceptor.cancel (ec); - } - } - - //-------------------------------------------------------------------------- - - // Initiating function for performing an asynchronous accept - // - void async_accept () - { - m_acceptor.async_accept (m_socket, - std::bind (&PeerDoorImp::handleAccept, this, - beast::asio::placeholders::error)); - } - - //-------------------------------------------------------------------------- - - // Called when the deadline timer wait completes - // - void handleTimer (boost::system::error_code ec) - { - if (ec == boost::asio::error::operation_aborted) - return; - - async_accept (); - } - - // Called when the accept socket wait completes - // - void handleAccept (boost::system::error_code ec) - { - if (ec == boost::asio::error::operation_aborted) - return; - - bool delay = false; - - if (! ec) - { - m_overlay.accept (std::move(m_socket)); - } - else - { - if (ec == boost::system::errc::too_many_files_open) - delay = true; - - m_journal.info << "Error " << ec; - } - - m_socket.close(ec); - - if (delay) - { - m_acceptDelay.expires_from_now (boost::posix_time::milliseconds (500)); - m_acceptDelay.async_wait (std::bind (&PeerDoorImp::handleTimer, - this, beast::asio::placeholders::error)); - } - else - { - async_accept (); - } - } -}; - -//------------------------------------------------------------------------------ - -std::unique_ptr -make_PeerDoor (OverlayImpl& overlay, std::string const& ip, int port, - boost::asio::io_service& io_service) -{ - // You have to listen on something! - bassert(port != 0); - - boost::asio::ip::tcp::endpoint ep( - boost::asio::ip::address ().from_string ( - ip.empty () ? "0.0.0.0" : ip), port); - - return std::make_unique(overlay, ep, io_service); -} - -} diff --git a/src/ripple/overlay/impl/PeerImp.cpp b/src/ripple/overlay/impl/PeerImp.cpp index 1da7527607..94194c71d9 100644 --- a/src/ripple/overlay/impl/PeerImp.cpp +++ b/src/ripple/overlay/impl/PeerImp.cpp @@ -17,23 +17,25 @@ */ //============================================================================== +#include #include +#include #include #include #include #include #include // +#include namespace ripple { -PeerImp::PeerImp (socket_type&& socket, beast::IP::Endpoint remoteAddress, - OverlayImpl& overlay, Resource::Manager& resourceManager, - PeerFinder::Manager& peerFinder, PeerFinder::Slot::ptr const& slot, - std::shared_ptr const& context) +PeerImp::PeerImp (std::unique_ptr&& ssl_bundle, + beast::http::message&& request, beast::IP::Endpoint remoteAddress, + OverlayImpl& overlay, Resource::Manager& resourceManager, + PeerFinder::Manager& peerFinder, PeerFinder::Slot::ptr const& slot) : Child (overlay) , journal_ (deprecatedLogs().journal("Peer")) - , ssl_bundle_(std::make_unique( - context, std::move(socket))) + , ssl_bundle_(std::move(ssl_bundle)) , socket_ (ssl_bundle_->socket) , stream_ (ssl_bundle_->stream) , strand_ (socket_.get_io_service()) @@ -79,12 +81,22 @@ PeerImp::~PeerImp () } void -PeerImp::start () +PeerImp::start() { + boost::system::error_code ec; + timer_.expires_from_now (nodeVerifySeconds, ec); + timer_.async_wait (strand_.wrap (std::bind (&PeerImp::handleVerifyTimer, + shared_from_this (), beast::asio::placeholders::error))); + if (ec) + { + journal_.error << "Failed to set verify timer."; + return detach ("start"); + } + if (m_inbound) - do_accept (); + do_accept(); else - do_connect (); + do_connect(); } void @@ -298,20 +310,9 @@ void PeerImp::do_connect () journal_.info << "Connecting to " << remote_address_; usage_ = resourceManager_.newOutboundEndpoint (remote_address_); - if (usage_.disconnect ()) return detach ("do_connect"); - boost::system::error_code ec; - timer_.expires_from_now (nodeVerifySeconds, ec); - timer_.async_wait (strand_.wrap (std::bind (&PeerImp::handleVerifyTimer, - shared_from_this (), beast::asio::placeholders::error))); - if (ec) - { - journal_.error << "Failed to set verify timer."; - return detach ("do_connect"); - } - stream_.next_layer().async_connect ( beast::IPAddressConversion::to_asio_endpoint (remote_address_), strand_.wrap (std::bind (&PeerImp::on_connect, @@ -434,6 +435,70 @@ PeerImp::on_write_http_request (error_code ec, std::size_t bytes_transferred) beast::asio::placeholders::bytes_transferred))); } +template +boost::asio::ip::tcp::endpoint +parse_endpoint (std::string const& s, boost::system::error_code& ec) +{ + beast::IP::Endpoint bep; + std::istringstream is(s); + is >> bep; + if (is.fail()) + { + ec = boost::system::errc::make_error_code( + boost::system::errc::invalid_argument); + return boost::asio::ip::tcp::endpoint{}; + } + + return beast::IPAddressConversion::to_asio_endpoint(bep); +} + +template +void +PeerImp::processResponse (beast::http::message const& m, + Streambuf const& body) +{ + if (http_message_->status() == 503) + { + Json::Value json; + Json::Reader r; + auto const success = r.parse(to_string(body), json); + if (success) + { + if (json.isObject() && json.isMember("peer-ips")) + { + Json::Value const& ips = json["peer-ips"]; + if (ips.isArray()) + { + std::vector eps; + eps.reserve(ips.size()); + for (auto const& v : ips) + { + if (v.isString()) + { + error_code ec; + auto const ep = parse_endpoint(v.asString(), ec); + if (!ec) + eps.push_back(ep); + } + } + peerFinder_.onRedirects(beast::IPAddressConversion:: + to_asio_endpoint(remote_address_), eps); + } + } + } + } + + if (http_message_->status() != 200) + { + if (journal_.info) journal_.info << + "HTTP Response: " << m.status() << " " << m.reason(); + detach("processResponse"); + return; + } + + do_protocol_start(); +} + // Called repeatedly with the http response data void PeerImp::on_read_http_response (error_code ec, std::size_t bytes_transferred) @@ -452,22 +517,7 @@ PeerImp::on_read_http_response (error_code ec, std::size_t bytes_transferred) read_buffer_.consume (bytes_consumed); if (http_parser_->complete()) { - // - // TODO Apply response to connection state, then: - // - Go into protocol loop, or - // - Submit a new request (call on_write_http_request), or - // - Close the connection. - // - if (http_message_->status() != 200) - { - journal_.info << - "HTTP Response: " << http_message_->reason() << - "(" << http_message_->status() << ")"; - detach("on_read_http_response"); - return; - } - do_protocol_start (); - return; + return processResponse(*http_message_, http_body_); } } } @@ -501,7 +551,7 @@ PeerImp::on_read_http_response (error_code ec, std::size_t bytes_transferred) 3. If HTTP request received, send HTTP response 4. Enter protocol loop */ -void PeerImp::do_accept () +void PeerImp::do_accept() { journal_.info << "Accepted " << remote_address_; @@ -512,6 +562,11 @@ void PeerImp::do_accept () return; } + // VFALCO Hack to receive a legacy protocol connection + // from the HTTP server + if (read_buffer_.size() > 0) + return do_protocol_start(); + stream_.set_verify_mode (boost::asio::ssl::verify_none); stream_.async_handshake (boost::asio::ssl::stream_base::server, strand_.wrap (std::bind (&PeerImp::on_accept_ssl, @@ -694,7 +749,7 @@ PeerImp::on_write_http_response (error_code ec, std::size_t bytes_transferred) // connection detail. Also need to establish no man in the middle attack // is in progress. void -PeerImp::do_protocol_start () +PeerImp::do_protocol_start() { if (!sendHello ()) { @@ -2267,7 +2322,6 @@ PeerImp::sendHello () secureCookie_, vchSig); protocol::TMHello h; - h.set_protoversion (to_packed (BuildInfo::getCurrentProtocol())); h.set_protoversionmin (to_packed (BuildInfo::getMinimumProtocol())); h.set_fullversion (BuildInfo::getFullVersionString ()); @@ -2275,7 +2329,8 @@ PeerImp::sendHello () h.set_nodepublic (getApp().getLocalCredentials ().getNodePublic ( ).humanNodePublic ()); h.set_nodeproof (&vchSig[0], vchSig.size ()); - h.set_ipv4port (getConfig ().peerListeningPort); + // DEPRECATED + h.set_ipv4port (overlay_.serverHandler().setup().overlay.port); h.set_testnet (false); // We always advertise ourselves as private in the HELLO message. This diff --git a/src/ripple/overlay/impl/PeerImp.h b/src/ripple/overlay/impl/PeerImp.h index dd08df9bfa..d49936c196 100644 --- a/src/ripple/overlay/impl/PeerImp.h +++ b/src/ripple/overlay/impl/PeerImp.h @@ -178,18 +178,24 @@ private: //-------------------------------------------------------------------------- public: - /** Create an incoming peer from the specified socket */ - PeerImp (socket_type&& socket, beast::IP::Endpoint remoteAddress, - OverlayImpl& overlay, Resource::Manager& resourceManager, - PeerFinder::Manager& peerFinder, PeerFinder::Slot::ptr const& slot, - std::shared_ptr const& context); + PeerImp (PeerImp const&) = delete; + PeerImp& operator= (PeerImp const&) = delete; - /** Create an outgoing peer - @note Construction of outbound peers is a two step process: a second - call is needed (to connect or accept) but we cannot make it from - inside the constructor because you cannot call shared_from_this - from inside constructors. - */ + /** Create an incoming legacy peer from an established ssl connection. */ + template + PeerImp (std::unique_ptr&& ssl_bundle, + ConstBufferSequence const& buffer, beast::IP::Endpoint remoteAddress, + OverlayImpl& overlay, Resource::Manager& resourceManager, + PeerFinder::Manager& peerFinder, + PeerFinder::Slot::ptr const& slot); + + /** Create an active incoming peer from an established ssl connection. */ + PeerImp (std::unique_ptr&& ssl_bundle, + beast::http::message&& request, beast::IP::Endpoint remoteAddress, + OverlayImpl& overlay, Resource::Manager& resourceManager, + PeerFinder::Manager& peerFinder, PeerFinder::Slot::ptr const& slot); + + /** Create an outgoing peer. */ PeerImp (beast::IP::Endpoint remoteAddress, boost::asio::io_service& io_service, OverlayImpl& overlay, Resource::Manager& resourceManager, PeerFinder::Manager& peerFinder, PeerFinder::Slot::ptr const& slot, @@ -198,10 +204,13 @@ public: virtual ~PeerImp (); - PeerImp (PeerImp const&) = delete; - PeerImp& operator= (PeerImp const&) = delete; + PeerFinder::Slot::ptr const& + slot() + { + return slot_; + } - // Begin asynchronous initiation function calls + // Work-around for calling shared_from_this in constructors void start(); @@ -296,6 +305,10 @@ private: void on_write_http_request (error_code ec, std::size_t bytes_transferred); + template + void + processResponse (beast::http::message const& m, Streambuf const& body); + void on_read_http_response (error_code ec, std::size_t bytes_transferred); @@ -506,6 +519,32 @@ private: //------------------------------------------------------------------------------ +template +PeerImp::PeerImp (std::unique_ptr&& ssl_bundle, + ConstBufferSequence const& buffer, beast::IP::Endpoint remoteAddress, + OverlayImpl& overlay, Resource::Manager& resourceManager, + PeerFinder::Manager& peerFinder, + PeerFinder::Slot::ptr const& slot) + : Child (overlay) + , journal_ (deprecatedLogs().journal("Peer")) + , ssl_bundle_(std::move(ssl_bundle)) + , socket_ (ssl_bundle_->socket) + , stream_ (ssl_bundle_->stream) + , strand_ (socket_.get_io_service()) + , timer_ (socket_.get_io_service()) + , remote_address_ (remoteAddress) + , resourceManager_ (resourceManager) + , peerFinder_ (peerFinder) + , overlay_ (overlay) + , m_inbound (true) + , state_ (stateConnected) + , slot_ (slot) + , message_stream_(*this) +{ + read_buffer_.commit(boost::asio::buffer_copy(read_buffer_.prepare( + boost::asio::buffer_size(buffer)), buffer)); +} + template void PeerImp::send_endpoints (FwdIt first, FwdIt last) diff --git a/src/ripple/overlay/make_Overlay.h b/src/ripple/overlay/make_Overlay.h index d5e9213829..d058c101b9 100644 --- a/src/ripple/overlay/make_Overlay.h +++ b/src/ripple/overlay/make_Overlay.h @@ -20,9 +20,9 @@ #ifndef RIPPLE_OVERLAY_MAKE_OVERLAY_H_INCLUDED #define RIPPLE_OVERLAY_MAKE_OVERLAY_H_INCLUDED +#include #include #include -#include #include #include #include @@ -31,9 +31,6 @@ namespace ripple { -// VFALCO This is separated so that users of the Overlay interface do not need -// to know about creation details such as asio or ssl. - Overlay::Setup setup_Overlay (BasicConfig const& config); @@ -42,8 +39,8 @@ std::unique_ptr make_Overlay ( Overlay::Setup const& setup, beast::Stoppable& parent, + ServerHandler& serverHandler, Resource::Manager& resourceManager, - SiteFiles::Manager& siteFiles, beast::File const& pathToDbFileOrDirectory, Resolver& resolver, boost::asio::io_service& io_service); diff --git a/src/ripple/peerfinder/Manager.h b/src/ripple/peerfinder/Manager.h index 50a8de7db8..7a020ff22d 100644 --- a/src/ripple/peerfinder/Manager.h +++ b/src/ripple/peerfinder/Manager.h @@ -225,7 +225,7 @@ public: virtual std::vector>> - sendpeers() = 0; + buildEndpointsForPeers() = 0; /** Perform periodic activity. This should be called once per second. diff --git a/src/ripple/peerfinder/impl/Logic.h b/src/ripple/peerfinder/impl/Logic.h index 4c07d6fc87..eaa5876c90 100644 --- a/src/ripple/peerfinder/impl/Logic.h +++ b/src/ripple/peerfinder/impl/Logic.h @@ -589,7 +589,7 @@ public: } std::vector>> - sendpeers() + buildEndpointsForPeers() { std::vector>> result; diff --git a/src/ripple/peerfinder/impl/Manager.cpp b/src/ripple/peerfinder/impl/Manager.cpp index a6ddb85094..bf06abd00f 100644 --- a/src/ripple/peerfinder/impl/Manager.cpp +++ b/src/ripple/peerfinder/impl/Manager.cpp @@ -198,9 +198,9 @@ public: } std::vector>> - sendpeers() override + buildEndpointsForPeers() override { - return m_logic.sendpeers(); + return m_logic.buildEndpointsForPeers(); } //-------------------------------------------------------------------------- diff --git a/src/ripple/rpc/RPCHandler.h b/src/ripple/rpc/RPCHandler.h index e693d03bdc..fade0b0d11 100644 --- a/src/ripple/rpc/RPCHandler.h +++ b/src/ripple/rpc/RPCHandler.h @@ -20,7 +20,7 @@ #ifndef RIPPLE_APP_RPC_HANDLER #define RIPPLE_APP_RPC_HANDLER -#include +#include #include #include @@ -36,20 +36,20 @@ public: Json::Value doCommand ( Json::Value const& request, - Config::Role role, + Role role, Resource::Charge& loadType); Json::Value doRpcCommand ( std::string const& command, Json::Value const& params, - Config::Role role, + Role role, Resource::Charge& loadType); private: NetworkOPs& netOps_; InfoSub::pointer infoSub_; - Config::Role role_ = Config::FORBID; + Role role_ = Role::FORBID; }; } // ripple diff --git a/src/ripple/rpc/handlers/AccountTx.cpp b/src/ripple/rpc/handlers/AccountTx.cpp index 72366d9c38..1d3797fa29 100644 --- a/src/ripple/rpc/handlers/AccountTx.cpp +++ b/src/ripple/rpc/handlers/AccountTx.cpp @@ -103,7 +103,7 @@ Json::Value doAccountTx (RPC::Context& context) { auto txns = context.netOps_.getTxsAccountB ( raAccount, uLedgerMin, uLedgerMax, bForward, resumeToken, limit, - context.role_ == Config::ADMIN); + context.role_ == Role::ADMIN); for (auto& it: txns) { @@ -124,7 +124,7 @@ Json::Value doAccountTx (RPC::Context& context) { auto txns = context.netOps_.getTxsAccount ( raAccount, uLedgerMin, uLedgerMax, bForward, resumeToken, limit, - context.role_ == Config::ADMIN); + context.role_ == Role::ADMIN); for (auto& it: txns) { diff --git a/src/ripple/rpc/handlers/AccountTxOld.cpp b/src/ripple/rpc/handlers/AccountTxOld.cpp index fa6319b41c..23f5904663 100644 --- a/src/ripple/rpc/handlers/AccountTxOld.cpp +++ b/src/ripple/rpc/handlers/AccountTxOld.cpp @@ -126,7 +126,7 @@ Json::Value doAccountTxOld (RPC::Context& context) { auto txns = context.netOps_.getAccountTxsB ( raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit, - context.role_ == Config::ADMIN); + context.role_ == Role::ADMIN); for (auto it = txns.begin (), end = txns.end (); it != end; ++it) { @@ -148,7 +148,7 @@ Json::Value doAccountTxOld (RPC::Context& context) { auto txns = context.netOps_.getAccountTxs ( raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit, - context.role_ == Config::ADMIN); + context.role_ == Role::ADMIN); for (auto it = txns.begin (), end = txns.end (); it != end; ++it) { diff --git a/src/ripple/rpc/handlers/Connect.cpp b/src/ripple/rpc/handlers/Connect.cpp index 199df16bc8..2ba0b2571c 100644 --- a/src/ripple/rpc/handlers/Connect.cpp +++ b/src/ripple/rpc/handlers/Connect.cpp @@ -46,7 +46,7 @@ Json::Value doConnect (RPC::Context& context) if(context.params_.isMember ("port")) iPort = context.params_["port"].asInt (); else - iPort = SYSTEM_PEER_PORT; + iPort = 6561; auto ip = beast::IP::Endpoint::from_string( context.params_["ip"].asString ()); diff --git a/src/ripple/rpc/handlers/Ledger.cpp b/src/ripple/rpc/handlers/Ledger.cpp index 6e1b445088..36e5ebc6e8 100644 --- a/src/ripple/rpc/handlers/Ledger.cpp +++ b/src/ripple/rpc/handlers/Ledger.cpp @@ -67,7 +67,7 @@ Json::Value doLedger (RPC::Context& context) if (bFull || bAccounts) { - if (context.role_ != Config::ADMIN) + if (context.role_ != Role::ADMIN) { // Until some sane way to get full ledgers has been implemented, // disallow retrieving all state nodes. @@ -75,7 +75,7 @@ Json::Value doLedger (RPC::Context& context) } if (getApp().getFeeTrack().isLoadedLocal() && - context.role_ != Config::ADMIN) + context.role_ != Role::ADMIN) { WriteLog (lsDEBUG, Peer) << "Too busy to give full ledger"; return rpcError(rpcTOO_BUSY); diff --git a/src/ripple/rpc/handlers/LedgerData.cpp b/src/ripple/rpc/handlers/LedgerData.cpp index e194fe0713..00f45a3109 100644 --- a/src/ripple/rpc/handlers/LedgerData.cpp +++ b/src/ripple/rpc/handlers/LedgerData.cpp @@ -73,7 +73,7 @@ Json::Value doLedgerData (RPC::Context& context) limit = jLimit.asInt (); } - if ((limit < 0) || ((limit > maxLimit) && (context.role_ != Config::ADMIN))) + if ((limit < 0) || ((limit > maxLimit) && (context.role_ != Role::ADMIN))) limit = maxLimit; Json::Value jvReply = Json::objectValue; diff --git a/src/ripple/rpc/handlers/RipplePathFind.cpp b/src/ripple/rpc/handlers/RipplePathFind.cpp index 8531bae8af..efb6e48b6f 100644 --- a/src/ripple/rpc/handlers/RipplePathFind.cpp +++ b/src/ripple/rpc/handlers/RipplePathFind.cpp @@ -24,7 +24,7 @@ namespace ripple { // This interface is deprecated. Json::Value doRipplePathFind (RPC::Context& context) { - RPC::LegacyPathFind lpf (context.role_ == Config::ADMIN); + RPC::LegacyPathFind lpf (context.role_ == Role::ADMIN); if (!lpf.isOk ()) return rpcError (rpcTOO_BUSY); @@ -192,7 +192,7 @@ Json::Value doRipplePathFind (RPC::Context& context) && context.params_["depth"].isIntegral()) { int rLev = context.params_["search_depth"].asInt (); - if ((rLev < level) || (context.role_ == Config::ADMIN)) + if ((rLev < level) || (context.role_ == Role::ADMIN)) level = rLev; } diff --git a/src/ripple/rpc/handlers/ServerInfo.cpp b/src/ripple/rpc/handlers/ServerInfo.cpp index 5ba54dea95..7633cd4fd3 100644 --- a/src/ripple/rpc/handlers/ServerInfo.cpp +++ b/src/ripple/rpc/handlers/ServerInfo.cpp @@ -26,7 +26,7 @@ Json::Value doServerInfo (RPC::Context& context) Json::Value ret (Json::objectValue); ret["info"] = context.netOps_.getServerInfo ( - true, context.role_ == Config::ADMIN); + true, context.role_ == Role::ADMIN); return ret; } diff --git a/src/ripple/rpc/handlers/ServerState.cpp b/src/ripple/rpc/handlers/ServerState.cpp index 6075487c3d..ca967d3c07 100644 --- a/src/ripple/rpc/handlers/ServerState.cpp +++ b/src/ripple/rpc/handlers/ServerState.cpp @@ -26,7 +26,7 @@ Json::Value doServerState (RPC::Context& context) Json::Value ret (Json::objectValue); ret["state"] = context.netOps_.getServerInfo ( - false, context.role_ == Config::ADMIN); + false, context.role_ == Role::ADMIN); return ret; } diff --git a/src/ripple/rpc/handlers/Submit.cpp b/src/ripple/rpc/handlers/Submit.cpp index fb9129a344..605a561a5b 100644 --- a/src/ripple/rpc/handlers/Submit.cpp +++ b/src/ripple/rpc/handlers/Submit.cpp @@ -86,7 +86,7 @@ Json::Value doSubmit (RPC::Context& context) try { (void) context.netOps_.processTransaction ( - tpTrans, context.role_ == Config::ADMIN, true, + tpTrans, context.role_ == Role::ADMIN, true, context.params_.isMember ("fail_hard") && context.params_["fail_hard"].asBool ()); } diff --git a/src/ripple/rpc/handlers/Subscribe.cpp b/src/ripple/rpc/handlers/Subscribe.cpp index 875df036e2..b454979f5f 100644 --- a/src/ripple/rpc/handlers/Subscribe.cpp +++ b/src/ripple/rpc/handlers/Subscribe.cpp @@ -46,7 +46,7 @@ Json::Value doSubscribe (RPC::Context& context) if (context.params_.isMember ("url")) { - if (context.role_ != Config::ADMIN) + if (context.role_ != Role::ADMIN) return rpcError (rpcNO_PERMISSION); std::string strUrl = context.params_["url"].asString (); @@ -114,7 +114,7 @@ Json::Value doSubscribe (RPC::Context& context) if (streamName == "server") { context.netOps_.subServer (ispSub, jvResult, - context.role_ == Config::ADMIN); + context.role_ == Role::ADMIN); } else if (streamName == "ledger") { diff --git a/src/ripple/rpc/handlers/TxHistory.cpp b/src/ripple/rpc/handlers/TxHistory.cpp index c9dcdcaa08..8acd557925 100644 --- a/src/ripple/rpc/handlers/TxHistory.cpp +++ b/src/ripple/rpc/handlers/TxHistory.cpp @@ -32,7 +32,7 @@ Json::Value doTxHistory (RPC::Context& context) unsigned int startIndex = context.params_["start"].asUInt (); - if ((startIndex > 10000) && (context.role_ != Config::ADMIN)) + if ((startIndex > 10000) && (context.role_ != Role::ADMIN)) return rpcError (rpcNO_PERMISSION); Json::Value obj; diff --git a/src/ripple/rpc/handlers/Unsubscribe.cpp b/src/ripple/rpc/handlers/Unsubscribe.cpp index ad23519622..ed82a2ceed 100644 --- a/src/ripple/rpc/handlers/Unsubscribe.cpp +++ b/src/ripple/rpc/handlers/Unsubscribe.cpp @@ -37,7 +37,7 @@ Json::Value doUnsubscribe (RPC::Context& context) if (context.params_.isMember ("url")) { - if (context.role_ != Config::ADMIN) + if (context.role_ != Role::ADMIN) return rpcError (rpcNO_PERMISSION); std::string strUrl = context.params_["url"].asString (); diff --git a/src/ripple/rpc/handlers/ValidationCreate.cpp b/src/ripple/rpc/handlers/ValidationCreate.cpp index 8162b50588..3356765d99 100644 --- a/src/ripple/rpc/handlers/ValidationCreate.cpp +++ b/src/ripple/rpc/handlers/ValidationCreate.cpp @@ -24,7 +24,7 @@ namespace ripple { // secret: // optional // } // -// This command requires Config::ADMIN access because it makes no sense to ask +// This command requires Role::ADMIN access because it makes no sense to ask // an untrusted server for this. Json::Value doValidationCreate (RPC::Context& context) { diff --git a/src/ripple/rpc/impl/Context.h b/src/ripple/rpc/impl/Context.h index 0f566d4ee2..4fee046c95 100644 --- a/src/ripple/rpc/impl/Context.h +++ b/src/ripple/rpc/impl/Context.h @@ -21,6 +21,7 @@ #define RIPPLE_RPC_CONTEXT #include +#include namespace ripple { namespace RPC { @@ -33,7 +34,7 @@ struct Context Resource::Charge& loadType_; NetworkOPs& netOps_; InfoSub::pointer infoSub_; - Config::Role role_; + Role role_; }; } // RPC diff --git a/src/ripple/rpc/impl/Handler.cpp b/src/ripple/rpc/impl/Handler.cpp index 66d02ae1a5..01e2a26b43 100644 --- a/src/ripple/rpc/impl/Handler.cpp +++ b/src/ripple/rpc/impl/Handler.cpp @@ -42,66 +42,66 @@ class HandlerTable { HandlerTable HANDLERS({ // Request-response methods - { "account_info", &doAccountInfo, Config::USER, NEEDS_CURRENT_LEDGER }, - { "account_currencies", &doAccountCurrencies, Config::USER, NEEDS_CURRENT_LEDGER }, - { "account_lines", &doAccountLines, Config::USER, NEEDS_CURRENT_LEDGER }, - { "account_offers", &doAccountOffers, Config::USER, NEEDS_CURRENT_LEDGER }, - { "account_tx", &doAccountTxSwitch, Config::USER, NEEDS_NETWORK_CONNECTION }, - { "blacklist", &doBlackList, Config::ADMIN, NO_CONDITION }, - { "book_offers", &doBookOffers, Config::USER, NEEDS_CURRENT_LEDGER }, - { "connect", &doConnect, Config::ADMIN, NO_CONDITION }, - { "consensus_info", &doConsensusInfo, Config::ADMIN, NO_CONDITION }, - { "get_counts", &doGetCounts, Config::ADMIN, NO_CONDITION }, - { "internal", &doInternal, Config::ADMIN, NO_CONDITION }, - { "feature", &doFeature, Config::ADMIN, NO_CONDITION }, - { "fetch_info", &doFetchInfo, Config::ADMIN, NO_CONDITION }, - { "ledger", &doLedger, Config::USER, NEEDS_NETWORK_CONNECTION }, - { "ledger_accept", &doLedgerAccept, Config::ADMIN, NEEDS_CURRENT_LEDGER }, - { "ledger_cleaner", &doLedgerCleaner, Config::ADMIN, NEEDS_NETWORK_CONNECTION }, - { "ledger_closed", &doLedgerClosed, Config::USER, NEEDS_CLOSED_LEDGER }, - { "ledger_current", &doLedgerCurrent, Config::USER, NEEDS_CURRENT_LEDGER }, - { "ledger_data", &doLedgerData, Config::USER, NEEDS_CURRENT_LEDGER }, - { "ledger_entry", &doLedgerEntry, Config::USER, NEEDS_CURRENT_LEDGER }, - { "ledger_header", &doLedgerHeader, Config::USER, NEEDS_CURRENT_LEDGER }, - { "ledger_request", &doLedgerRequest, Config::ADMIN, NO_CONDITION }, - { "log_level", &doLogLevel, Config::ADMIN, NO_CONDITION }, - { "logrotate", &doLogRotate, Config::ADMIN, NO_CONDITION }, - { "owner_info", &doOwnerInfo, Config::USER, NEEDS_CURRENT_LEDGER }, - { "peers", &doPeers, Config::ADMIN, NO_CONDITION }, - { "path_find", &doPathFind, Config::USER, NEEDS_CURRENT_LEDGER }, - { "ping", &doPing, Config::USER, NO_CONDITION }, - { "print", &doPrint, Config::ADMIN, NO_CONDITION }, -// { "profile", &doProfile, Config::USER, NEEDS_CURRENT_LEDGER }, - { "proof_create", &doProofCreate, Config::ADMIN, NO_CONDITION }, - { "proof_solve", &doProofSolve, Config::ADMIN, NO_CONDITION }, - { "proof_verify", &doProofVerify, Config::ADMIN, NO_CONDITION }, - { "random", &doRandom, Config::USER, NO_CONDITION }, - { "ripple_path_find", &doRipplePathFind, Config::USER, NEEDS_CURRENT_LEDGER }, - { "sign", &doSign, Config::USER, NO_CONDITION }, - { "submit", &doSubmit, Config::USER, NEEDS_CURRENT_LEDGER }, - { "server_info", &doServerInfo, Config::USER, NO_CONDITION }, - { "server_state", &doServerState, Config::USER, NO_CONDITION }, - { "sms", &doSMS, Config::ADMIN, NO_CONDITION }, - { "stop", &doStop, Config::ADMIN, NO_CONDITION }, - { "transaction_entry", &doTransactionEntry, Config::USER, NEEDS_CURRENT_LEDGER }, - { "tx", &doTx, Config::USER, NEEDS_NETWORK_CONNECTION }, - { "tx_history", &doTxHistory, Config::USER, NO_CONDITION }, - { "unl_add", &doUnlAdd, Config::ADMIN, NO_CONDITION }, - { "unl_delete", &doUnlDelete, Config::ADMIN, NO_CONDITION }, - { "unl_list", &doUnlList, Config::ADMIN, NO_CONDITION }, - { "unl_load", &doUnlLoad, Config::ADMIN, NO_CONDITION }, - { "unl_network", &doUnlNetwork, Config::ADMIN, NO_CONDITION }, - { "unl_reset", &doUnlReset, Config::ADMIN, NO_CONDITION }, - { "unl_score", &doUnlScore, Config::ADMIN, NO_CONDITION }, - { "validation_create", &doValidationCreate, Config::ADMIN, NO_CONDITION }, - { "validation_seed", &doValidationSeed, Config::ADMIN, NO_CONDITION }, - { "wallet_accounts", &doWalletAccounts, Config::USER, NEEDS_CURRENT_LEDGER }, - { "wallet_propose", &doWalletPropose, Config::ADMIN, NO_CONDITION }, - { "wallet_seed", &doWalletSeed, Config::ADMIN, NO_CONDITION }, + { "account_info", &doAccountInfo, Role::USER, NEEDS_CURRENT_LEDGER }, + { "account_currencies", &doAccountCurrencies, Role::USER, NEEDS_CURRENT_LEDGER }, + { "account_lines", &doAccountLines, Role::USER, NEEDS_CURRENT_LEDGER }, + { "account_offers", &doAccountOffers, Role::USER, NEEDS_CURRENT_LEDGER }, + { "account_tx", &doAccountTxSwitch, Role::USER, NEEDS_NETWORK_CONNECTION }, + { "blacklist", &doBlackList, Role::ADMIN, NO_CONDITION }, + { "book_offers", &doBookOffers, Role::USER, NEEDS_CURRENT_LEDGER }, + { "connect", &doConnect, Role::ADMIN, NO_CONDITION }, + { "consensus_info", &doConsensusInfo, Role::ADMIN, NO_CONDITION }, + { "get_counts", &doGetCounts, Role::ADMIN, NO_CONDITION }, + { "internal", &doInternal, Role::ADMIN, NO_CONDITION }, + { "feature", &doFeature, Role::ADMIN, NO_CONDITION }, + { "fetch_info", &doFetchInfo, Role::ADMIN, NO_CONDITION }, + { "ledger", &doLedger, Role::USER, NEEDS_NETWORK_CONNECTION }, + { "ledger_accept", &doLedgerAccept, Role::ADMIN, NEEDS_CURRENT_LEDGER }, + { "ledger_cleaner", &doLedgerCleaner, Role::ADMIN, NEEDS_NETWORK_CONNECTION }, + { "ledger_closed", &doLedgerClosed, Role::USER, NEEDS_CLOSED_LEDGER }, + { "ledger_current", &doLedgerCurrent, Role::USER, NEEDS_CURRENT_LEDGER }, + { "ledger_data", &doLedgerData, Role::USER, NEEDS_CURRENT_LEDGER }, + { "ledger_entry", &doLedgerEntry, Role::USER, NEEDS_CURRENT_LEDGER }, + { "ledger_header", &doLedgerHeader, Role::USER, NEEDS_CURRENT_LEDGER }, + { "ledger_request", &doLedgerRequest, Role::ADMIN, NO_CONDITION }, + { "log_level", &doLogLevel, Role::ADMIN, NO_CONDITION }, + { "logrotate", &doLogRotate, Role::ADMIN, NO_CONDITION }, + { "owner_info", &doOwnerInfo, Role::USER, NEEDS_CURRENT_LEDGER }, + { "peers", &doPeers, Role::ADMIN, NO_CONDITION }, + { "path_find", &doPathFind, Role::USER, NEEDS_CURRENT_LEDGER }, + { "ping", &doPing, Role::USER, NO_CONDITION }, + { "print", &doPrint, Role::ADMIN, NO_CONDITION }, +// { "profile", &doProfile, Role::USER, NEEDS_CURRENT_LEDGER }, + { "proof_create", &doProofCreate, Role::ADMIN, NO_CONDITION }, + { "proof_solve", &doProofSolve, Role::ADMIN, NO_CONDITION }, + { "proof_verify", &doProofVerify, Role::ADMIN, NO_CONDITION }, + { "random", &doRandom, Role::USER, NO_CONDITION }, + { "ripple_path_find", &doRipplePathFind, Role::USER, NEEDS_CURRENT_LEDGER }, + { "sign", &doSign, Role::USER, NO_CONDITION }, + { "submit", &doSubmit, Role::USER, NEEDS_CURRENT_LEDGER }, + { "server_info", &doServerInfo, Role::USER, NO_CONDITION }, + { "server_state", &doServerState, Role::USER, NO_CONDITION }, + { "sms", &doSMS, Role::ADMIN, NO_CONDITION }, + { "stop", &doStop, Role::ADMIN, NO_CONDITION }, + { "transaction_entry", &doTransactionEntry, Role::USER, NEEDS_CURRENT_LEDGER }, + { "tx", &doTx, Role::USER, NEEDS_NETWORK_CONNECTION }, + { "tx_history", &doTxHistory, Role::USER, NO_CONDITION }, + { "unl_add", &doUnlAdd, Role::ADMIN, NO_CONDITION }, + { "unl_delete", &doUnlDelete, Role::ADMIN, NO_CONDITION }, + { "unl_list", &doUnlList, Role::ADMIN, NO_CONDITION }, + { "unl_load", &doUnlLoad, Role::ADMIN, NO_CONDITION }, + { "unl_network", &doUnlNetwork, Role::ADMIN, NO_CONDITION }, + { "unl_reset", &doUnlReset, Role::ADMIN, NO_CONDITION }, + { "unl_score", &doUnlScore, Role::ADMIN, NO_CONDITION }, + { "validation_create", &doValidationCreate, Role::ADMIN, NO_CONDITION }, + { "validation_seed", &doValidationSeed, Role::ADMIN, NO_CONDITION }, + { "wallet_accounts", &doWalletAccounts, Role::USER, NEEDS_CURRENT_LEDGER }, + { "wallet_propose", &doWalletPropose, Role::ADMIN, NO_CONDITION }, + { "wallet_seed", &doWalletSeed, Role::ADMIN, NO_CONDITION }, // Evented methods - { "subscribe", &doSubscribe, Config::USER, NO_CONDITION }, - { "unsubscribe", &doUnsubscribe, Config::USER, NO_CONDITION }, + { "subscribe", &doSubscribe, Role::USER, NO_CONDITION }, + { "unsubscribe", &doUnsubscribe, Role::USER, NO_CONDITION }, }); } // namespace diff --git a/src/ripple/rpc/impl/Handler.h b/src/ripple/rpc/impl/Handler.h index 6afe26bdc3..1129d8e3ba 100644 --- a/src/ripple/rpc/impl/Handler.h +++ b/src/ripple/rpc/impl/Handler.h @@ -40,7 +40,7 @@ struct Handler const char* name_; Method method_; - Config::Role role_; + Role role_; RPC::Condition condition_; }; diff --git a/src/ripple/rpc/impl/RPCHandler.cpp b/src/ripple/rpc/impl/RPCHandler.cpp index bc724eaec7..a86cee3b55 100644 --- a/src/ripple/rpc/impl/RPCHandler.cpp +++ b/src/ripple/rpc/impl/RPCHandler.cpp @@ -39,7 +39,7 @@ RPCHandler::RPCHandler (NetworkOPs& netOps, InfoSub::pointer infoSub) Json::Value RPCHandler::doRpcCommand ( const std::string& strMethod, Json::Value const& jvParams, - Config::Role role, + Role role, Resource::Charge& loadType) { WriteLog (lsTRACE, RPCHandler) @@ -75,10 +75,10 @@ Json::Value RPCHandler::doRpcCommand ( Json::Value RPCHandler::doCommand ( const Json::Value& params, - Config::Role role, + Role role, Resource::Charge& loadType) { - if (role != Config::ADMIN) + if (role != Role::ADMIN) { // VFALCO NOTE Should we also add up the jtRPC jobs? // @@ -105,7 +105,7 @@ Json::Value RPCHandler::doCommand ( if (!handler) return rpcError (rpcUNKNOWN_COMMAND); - if (handler->role_ == Config::ADMIN && role_ != Config::ADMIN) + if (handler->role_ == Role::ADMIN && role_ != Role::ADMIN) return rpcError (rpcNO_PERMISSION); if ((handler->condition_ & RPC::NEEDS_NETWORK_CONNECTION) && diff --git a/src/ripple/rpc/impl/TransactionSign.cpp b/src/ripple/rpc/impl/TransactionSign.cpp index 10c50c767b..403da3f1c7 100644 --- a/src/ripple/rpc/impl/TransactionSign.cpp +++ b/src/ripple/rpc/impl/TransactionSign.cpp @@ -25,7 +25,6 @@ namespace ripple { //------------------------------------------------------------------------------ - namespace RPC { /** Fill in the fee on behalf of the client. @@ -98,7 +97,7 @@ static Json::Value signPayment( Json::Value& tx_json, RippleAddress const& raSrcAddressID, Ledger::pointer lSnapshot, - int role) + Role role) { RippleAddress dstAccountID; @@ -148,7 +147,7 @@ static Json::Value signPayment( "Cannot build XRP to XRP paths."); { - LegacyPathFind lpf (role == Config::ADMIN); + LegacyPathFind lpf (role == Role::ADMIN); if (!lpf.isOk ()) return rpcError (rpcTOO_BUSY); @@ -190,12 +189,13 @@ static Json::Value signPayment( // as needed, and then there should be a separate function to // submit the tranaction // -Json::Value transactionSign ( +Json::Value +transactionSign ( Json::Value params, bool bSubmit, bool bFailHard, NetworkOPs& netOps, - int role) + Role role) { Json::Value jvResult; @@ -245,7 +245,7 @@ Json::Value transactionSign ( return rpcError (rpcNO_CURRENT); // Check for load - if (getApp().getFeeTrack().isLoadedCluster() && (role != Config::ADMIN)) + if (getApp().getFeeTrack().isLoadedCluster() && (role != Role::ADMIN)) return rpcError(rpcTOO_BUSY); Ledger::pointer lSnapshot = netOps.getCurrentLedger (); @@ -265,7 +265,7 @@ Json::Value transactionSign ( } } - autofill_fee (params, lSnapshot, jvResult, role == Config::ADMIN); + autofill_fee (params, lSnapshot, jvResult, role == Role::ADMIN); if (RPC::contains_error (jvResult)) return jvResult; @@ -383,7 +383,7 @@ Json::Value transactionSign ( { // FIXME: For performance, should use asynch interface tpTrans = netOps.submitTransactionSync (tpTrans, - role == Config::ADMIN, true, bFailHard, bSubmit); + role == Role::ADMIN, true, bFailHard, bSubmit); if (!tpTrans) { diff --git a/src/ripple/rpc/impl/TransactionSign.h b/src/ripple/rpc/impl/TransactionSign.h index 66c8761a84..7bc2fd4413 100644 --- a/src/ripple/rpc/impl/TransactionSign.h +++ b/src/ripple/rpc/impl/TransactionSign.h @@ -20,6 +20,8 @@ #ifndef RIPPLE_RPC_TRANSACTIONSIGN_H_INCLUDED #define RIPPLE_RPC_TRANSACTIONSIGN_H_INCLUDED +#include + namespace ripple { namespace RPC { @@ -28,7 +30,7 @@ Json::Value transactionSign ( bool bSubmit, bool bFailHard, NetworkOPs& netOps, - int role); + Role role); } // RPC } // ripple diff --git a/src/ripple/server/Handler.h b/src/ripple/server/Handler.h new file mode 100644 index 0000000000..d47429a48f --- /dev/null +++ b/src/ripple/server/Handler.h @@ -0,0 +1,109 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_SERVER_HANDLER_H_INCLUDED +#define RIPPLE_SERVER_HANDLER_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { +namespace HTTP { + +class Server; +class Session; + +/** Processes all sessions. + Thread safety: + Must be safe to call concurrently from any number of foreign threads. +*/ +struct Handler +{ + /** Called when the connection is accepted and we know remoteAddress. */ + // DEPRECATED + virtual void onAccept (Session& session) = 0; + + /** Called when a connection is accepted. + @return `true` If we should keep the connection. + */ + virtual + bool + onAccept (Session& session, + boost::asio::ip::tcp::endpoint remote_address) = 0; + + /** Called when a legacy peer protocol handshake is detected. + If the called function does not take ownership, then the + connection is closed. + @param buffer The unconsumed bytes in the protocol handshake + @param ssl_bundle The active connection. + */ + virtual + void + onLegacyPeerHello (std::unique_ptr&& ssl_bundle, + boost::asio::const_buffer buffer, + boost::asio::ip::tcp::endpoint remote_address) = 0; + + /** Called to process a complete HTTP request. + The handler can do one of three things: + - Ignore the request (return default constructed What) + - Return a response (by setting response in the What) + - Take ownership of the socket by using rvalue move + and setting moved = `true` in the What. + If the handler ignores the request, the legacy onRequest + is called. + */ + /** @{ */ + virtual + Handoff + onHandoff (Session& session, + std::unique_ptr && bundle, + beast::http::message&& request, + boost::asio::ip::tcp::endpoint remote_address) = 0; + + virtual + Handoff + onHandoff (Session& session, boost::asio::ip::tcp::socket&& socket, + beast::http::message&& request, + boost::asio::ip::tcp::endpoint remote_address) = 0; + /** @} */ + + /** Called when we have a complete HTTP request. */ + // VFALCO TODO Pass the beast::http::message as a parameter + virtual void onRequest (Session& session) = 0; + + /** Called when the session ends. + Guaranteed to be called once. + @param errorCode Non zero for a failed connection. + */ + virtual void onClose (Session& session, + boost::system::error_code const& ec) = 0; + + /** Called when the server has finished its stop. */ + virtual void onStopped (Server& server) = 0; +}; + +} // HTTP +} // ripple + +#endif diff --git a/src/ripple/unity/http.h b/src/ripple/server/Handoff.h similarity index 61% rename from src/ripple/unity/http.h rename to src/ripple/server/Handoff.h index 439d80f862..a2eb26e396 100644 --- a/src/ripple/unity/http.h +++ b/src/ripple/server/Handoff.h @@ -17,19 +17,33 @@ */ //============================================================================== -#ifndef RIPPLE_HTTP_H_INCLUDED -#define RIPPLE_HTTP_H_INCLUDED +#ifndef RIPPLE_SERVER_HANDOFF_H_INCLUDED +#define RIPPLE_SERVER_HANDOFF_H_INCLUDED -// VFALCO This entire file is deprecated now, I'm working on a replacement +#include +#include -// VFALCO NOTE this sucks that we have to include asio in the header -// just for HTTPMessage! -#include +namespace ripple { -#include -#include -#include -#include -#include +/** Used to indicate the result of a server connection handoff. */ +struct Handoff +{ + // When `true`, the Session will close the socket. The + // Handler may optionally take socket ownership using std::move + bool moved = false; + + // If response is set, this determines the keep alive + bool keep_alive = false; + + // When set, this will be sent back + std::shared_ptr response; + + bool handled() const + { + return moved || response; + } +}; + +} // ripple #endif diff --git a/src/ripple/server/JsonWriter.h b/src/ripple/server/JsonWriter.h new file mode 100644 index 0000000000..9914dbcb3e --- /dev/null +++ b/src/ripple/server/JsonWriter.h @@ -0,0 +1,165 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_SERVER_JSONWRITER_H_INCLUDED +#define RIPPLE_SERVER_JSONWRITER_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +namespace ripple { +namespace HTTP { + +namespace detail { + +/** Writer that sends to Streambufs sequentially. */ +template +class message_writer : public Writer +{ +private: + Streambuf prebody_; + Streambuf body_; + std::size_t hint_ = 0; + std::vector data_; + +public: + message_writer (Streambuf&& prebody, Streambuf&& body); + + bool + complete() override; + + bool + prepare (std::size_t n, std::function) override; + + std::vector + data() override; + + void + consume (std::size_t n) override; + +private: + std::vector + data(Streambuf& buf); +}; + +template +message_writer::message_writer ( + Streambuf&& prebody, Streambuf&& body) + : prebody_(std::move(prebody)) + , body_(std::move(body)) +{ +} + +template +bool +message_writer::complete() +{ + return prebody_.size() == 0 && body_.size() == 0; +} + +template +bool +message_writer::prepare ( + std::size_t n, std::function) +{ + hint_ = n; + return true; +} + +template +std::vector +message_writer::data() +{ + return (prebody_.size() > 0) ? + data(prebody_) : data(body_); +} + +template +void +message_writer::consume (std::size_t n) +{ + if (prebody_.size() > 0) + return prebody_.consume(n); + body_.consume(n); +} + +template +std::vector +message_writer::data(Streambuf& buf) +{ + data_.resize(0); + for (auto iter = buf.data().begin(); + hint_ > 0 && iter != buf.data().end(); ++iter) + { + auto const n = std::min(hint_, + boost::asio::buffer_size(*iter)); + data_.emplace_back(boost::asio::buffer_cast< + void const*>(*iter), n); + hint_ -= n; + } + return data_; +} + +} // detail + +using streambufs_writer = detail::message_writer; + +//------------------------------------------------------------------------------ + +/** Write a Json::Value to a Streambuf. */ +template +void +write(Streambuf& buf, Json::Value const& json) +{ + stream(json, + [&buf](void const* data, std::size_t n) + { + buf.commit(boost::asio::buffer_copy( + buf.prepare(n), boost::asio::buffer(data, n))); + }); +} + +/** Returns a Writer that streams the provided HTTP message and Json body. + The message is modified to include the correct headers. +*/ +template +std::shared_ptr +make_JsonWriter (beast::http::message& m, Json::Value const& json) +{ + beast::asio::streambuf prebody; + beast::asio::streambuf body; + write(body, json); + // VFALCO TODO Better way to set a field + m.headers.erase ("Content-Length"); + m.headers.append("Content-Length", std::to_string(body.size())); + m.headers.erase ("Content-Type"); + m.headers.append("Content-Type", "application/json"); + write(prebody, m); + return std::make_shared( + std::move(prebody), std::move(body)); +} + +} +} + +#endif diff --git a/src/ripple/server/Port.h b/src/ripple/server/Port.h new file mode 100644 index 0000000000..a4394bc6ab --- /dev/null +++ b/src/ripple/server/Port.h @@ -0,0 +1,86 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_SERVER_PORT_H_INCLUDED +#define RIPPLE_SERVER_PORT_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +namespace boost { namespace asio { namespace ssl { class context; } } } + +namespace ripple { +namespace HTTP { + +/** Configuration information for a Server listening port. */ +struct Port +{ + std::string name; + boost::asio::ip::address ip; + std::uint16_t port = 0; + std::set protocol; + bool allow_admin = false; + std::string user; + std::string password; + std::string admin_user; + std::string admin_password; + std::string ssl_key; + std::string ssl_cert; + std::string ssl_chain; + std::shared_ptr context; + + // Returns `true` if any websocket protocols are specified + template + bool + websockets() const; + + // Returns a string containing the list of protocols + template + std::string + protocols() const; +}; + +//------------------------------------------------------------------------------ + +template +bool +Port::websockets() const +{ + return protocol.count("ws") > 0 || protocol.count("wss") > 0; +} + +template +std::string +Port::protocols() const +{ + std::string s; + for (auto iter = protocol.cbegin(); + iter != protocol.cend(); ++iter) + s += (iter != protocol.cbegin() ? "," : "") + *iter; + return s; +} + +} // HTTP +} // ripple + +#endif diff --git a/src/ripple/server/README.md b/src/ripple/server/README.md new file mode 100644 index 0000000000..fb0bbaeed4 --- /dev/null +++ b/src/ripple/server/README.md @@ -0,0 +1,3 @@ +# Server + +This contains the HTTP Server and ServerHandler diff --git a/src/ripple/server/Role.h b/src/ripple/server/Role.h new file mode 100644 index 0000000000..66ba128cd7 --- /dev/null +++ b/src/ripple/server/Role.h @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_SERVER_ROLE_H_INCLUDED +#define RIPPLE_SERVER_ROLE_H_INCLUDED + +#include +#include +#include +#include + +namespace ripple { + +/** Indicates the level of administrative permission to grant. */ +enum class Role +{ + GUEST, + USER, + ADMIN, + FORBID +}; + +/** Return the allowed privilege role. + jsonRPC must meet the requirements of the JSON-RPC + specification. It must be of type Object, containing the key params + which is an array with at least one object. Inside this object + are the optional keys 'admin_user' and 'admin_password' used to + validate the credentials. +*/ +Role +adminRole (HTTP::Port const& port, Json::Value const& jsonRPC, + beast::IP::Endpoint const& remoteIp, + std::vector const& admin_allow); + +} // ripple + +#endif diff --git a/src/ripple/server/Server.h b/src/ripple/server/Server.h new file mode 100644 index 0000000000..81d3dee613 --- /dev/null +++ b/src/ripple/server/Server.h @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_SERVER_SERVER_H_INCLUDED +#define RIPPLE_SERVER_SERVER_H_INCLUDED + +#include +#include +#include + +namespace ripple { +namespace HTTP { + +/** Multi-threaded, asynchronous HTTP server. */ +class Server +{ +public: + /** Destroy the server. + The server is closed if it is not already closed. This call + blocks until the server has stopped. + */ + virtual + ~Server() = default; + + /** Returns the Journal associated with the server. */ + virtual + beast::Journal + journal() = 0; + + /** Set the listening port settings. + This may only be called once. + */ + virtual + void + ports (std::vector const& v) = 0; + + virtual + void + onWrite (beast::PropertyStream::Map& map) = 0; + + /** Close the server. + The close is performed asynchronously. The handler will be notified + when the server has stopped. The server is considered stopped when + there are no pending I/O completion handlers and all connections + have closed. + Thread safety: + Safe to call concurrently from any thread. + */ + virtual + void + close() = 0; +}; + +} // HTTP +} // ripple + +#endif diff --git a/src/ripple/app/main/ServerHandler.h b/src/ripple/server/ServerHandler.h similarity index 56% rename from src/ripple/app/main/ServerHandler.h rename to src/ripple/server/ServerHandler.h index ef2abef0e4..f7754a9a2a 100644 --- a/src/ripple/app/main/ServerHandler.h +++ b/src/ripple/server/ServerHandler.h @@ -17,13 +17,17 @@ */ //============================================================================== -#ifndef RIPPLE_APP_MAIN_SERVERHANDLER_H_INCLUDED -#define RIPPLE_APP_MAIN_SERVERHANDLER_H_INCLUDED +#ifndef RIPPLE_SERVER_SERVERHANDLER_H_INCLUDED +#define RIPPLE_SERVER_SERVERHANDLER_H_INCLUDED -#include +#include +#include +#include #include #include #include // +#include +#include namespace ripple { @@ -35,6 +39,35 @@ protected: ServerHandler (Stoppable& parent); public: + struct Setup + { + std::vector ports; + + // Memberspace + struct client_t + { + bool secure; + std::string ip; + std::uint16_t port; + std::string user; + std::string password; + std::string admin_user; + std::string admin_password; + }; + + // Configuration when acting in client role + client_t client; + + // Configuration for the Overlay + struct overlay_t + { + boost::asio::ip::address ip; + std::uint16_t port = 0; + }; + + overlay_t overlay; + }; + virtual ~ServerHandler() = default; @@ -44,13 +77,18 @@ public: */ virtual void - setup (beast::Journal journal) = 0; + setup (Setup const& setup, beast::Journal journal) = 0; + + /** Returns the setup associated with the handler. */ + virtual + Setup const& + setup() const = 0; }; -std::unique_ptr -make_ServerHandler (beast::Stoppable& parent, JobQueue& jobQueue, - NetworkOPs& networkOPs, Resource::Manager& resourceManager, - RPC::Setup const& setup); +//------------------------------------------------------------------------------ + +ServerHandler::Setup +setup_ServerHandler (BasicConfig const& c, std::ostream& log); } // ripple diff --git a/src/ripple/http/Session.h b/src/ripple/server/Session.h similarity index 91% rename from src/ripple/http/Session.h rename to src/ripple/server/Session.h index 424821449e..6e609ef7cc 100644 --- a/src/ripple/http/Session.h +++ b/src/ripple/server/Session.h @@ -17,16 +17,18 @@ */ //============================================================================== -#ifndef RIPPLE_HTTP_SESSION_H_INCLUDED -#define RIPPLE_HTTP_SESSION_H_INCLUDED +#ifndef RIPPLE_SERVER_SESSION_H_INCLUDED +#define RIPPLE_SERVER_SESSION_H_INCLUDED +#include #include #include #include #include -#include +#include #include #include +#include namespace ripple { @@ -56,6 +58,11 @@ public: beast::Journal journal() = 0; + /** Returns the Port settings for this connection. */ + virtual + Port const& + port() = 0; + /** Returns the remote address of the connection. */ virtual beast::IP::Endpoint @@ -97,6 +104,12 @@ public: virtual void write (void const* buffer, std::size_t bytes) = 0; + + virtual + void + write (std::shared_ptr const& writer, + bool keep_alive) = 0; + /** @} */ /** Detach the session. diff --git a/src/ripple/server/Writer.h b/src/ripple/server/Writer.h new file mode 100644 index 0000000000..96aa4ff513 --- /dev/null +++ b/src/ripple/server/Writer.h @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_SERVER_WRITER_H_INCLUDED +#define RIPPLE_SERVER_WRITER_H_INCLUDED + +#include +#include +#include + +namespace ripple { +namespace HTTP { + +class Writer +{ +public: + virtual ~Writer() = default; + + /** Returns `true` if there is no more data to pull. */ + virtual + bool + complete() = 0; + + /** Removes bytes from the input sequence. */ + virtual + void + consume (std::size_t bytes) = 0; + + /** Add data to the input sequence. + @param bytes A hint to the number of bytes desired. + @param resume A functor to later resume execution. + @return `true` if the writer is ready to provide more data. + */ + virtual + bool + prepare (std::size_t bytes, + std::function resume) = 0; + + /** Returns a ConstBufferSequence representing the input sequence. */ + virtual + std::vector + data() = 0; +}; + +} +} + +#endif diff --git a/src/ripple/http/impl/Door.cpp b/src/ripple/server/impl/Door.cpp similarity index 75% rename from src/ripple/http/impl/Door.cpp rename to src/ripple/server/impl/Door.cpp index f34f993d39..1f48a96f42 100644 --- a/src/ripple/http/impl/Door.cpp +++ b/src/ripple/server/impl/Door.cpp @@ -17,9 +17,9 @@ */ //============================================================================== -#include -#include -#include +#include +#include +#include #include #include #include @@ -93,11 +93,11 @@ Door::Child::~Child() //------------------------------------------------------------------------------ Door::detector::detector (Door& door, socket_type&& socket, - endpoint_type endpoint) + endpoint_type remote_address) : Child(door) - , socket_ (std::move(socket)) - , timer_ (socket_.get_io_service()) - , remote_endpoint_ (endpoint) + , socket_(std::move(socket)) + , timer_(socket_.get_io_service()) + , remote_address_(remote_address) { } @@ -144,27 +144,35 @@ Door::detector::do_detect (boost::asio::yield_context yield) error_code unused; timer_.cancel(unused); if (! ec) - return door_.create(ssl, std::move(buf), - std::move(socket_), remote_endpoint_); + return door_.create(ssl, buf.data(), + std::move(socket_), remote_address_); if (ec != boost::asio::error::operation_aborted) if (door_.server_.journal().trace) door_.server_.journal().trace << "Error detecting ssl: " << ec.message() << - " from " << remote_endpoint_; + " from " << remote_address_; } //------------------------------------------------------------------------------ Door::Door (boost::asio::io_service& io_service, ServerImpl& server, Port const& port) - : port_(port) + : port_(std::make_shared(port)) , server_(server) , acceptor_(io_service) , strand_(io_service) + , ssl_ ( + port_->protocol.count("https") > 0 || + //port_->protocol.count("wss") > 0 || + port_->protocol.count("peer") > 0) + , plain_ ( + //port_->protocol.count("ws") > 0 || + port_->protocol.count("http") > 0) { server_.add (*this); error_code ec; - endpoint_type const local_address = to_asio(port); + endpoint_type const local_address = + endpoint_type(port.ip, port.port); acceptor_.open(local_address.protocol(), ec); if (ec) @@ -206,7 +214,7 @@ Door::Door (boost::asio::io_service& io_service, } if (server_.journal().info) server_.journal().info << - "Bound to endpoint " << to_string (acceptor_.local_endpoint()); + "Bound to endpoint " << acceptor_.local_endpoint(); } Door::~Door() @@ -263,52 +271,28 @@ Door::add (std::shared_ptr const& child) list_.emplace(child.get(), child); } +template void -Door::create (bool ssl, beast::asio::streambuf&& buf, +Door::create (bool ssl, ConstBufferSequence const& buffers, socket_type&& socket, endpoint_type remote_address) { if (server_.closed()) return; - error_code ec; - switch (port_.security) + + if (ssl) { - case Port::Security::no_ssl: - if (ssl) - ec = boost::system::errc::make_error_code ( - boost::system::errc::invalid_argument); - - case Port::Security::require_ssl: - if (! ssl) - ec = boost::system::errc::make_error_code ( - boost::system::errc::invalid_argument); - - case Port::Security::allow_ssl: - if (! ec) - { - if (ssl) - { - auto const peer = std::make_shared (*this, - server_.journal(), remote_address, buf.data(), - std::move(socket)); - add(peer); - peer->run(); - return; - } - - auto const peer = std::make_shared (*this, - server_.journal(), remote_address, buf.data(), - std::move(socket)); - add(peer); - peer->run(); - return; - } - break; + auto const peer = std::make_shared (*this, + server_.journal(), remote_address, buffers, + std::move(socket)); + add(peer); + return peer->run(); } - if (ec) - if (server_.journal().trace) server_.journal().trace << - "Error detecting ssl: " << ec.message() << - " from " << remote_address; + auto const peer = std::make_shared (*this, + server_.journal(), remote_address, buffers, + std::move(socket)); + add(peer); + peer->run(); } void @@ -317,9 +301,9 @@ Door::do_accept (boost::asio::yield_context yield) for(;;) { error_code ec; - endpoint_type endpoint; + endpoint_type remote_address; socket_type socket (acceptor_.get_io_service()); - acceptor_.async_accept (socket, endpoint, yield[ec]); + acceptor_.async_accept (socket, remote_address, yield[ec]); if (ec && ec != boost::asio::error::operation_aborted) if (server_.journal().error) server_.journal().error << "accept: " << ec.message(); @@ -327,29 +311,19 @@ Door::do_accept (boost::asio::yield_context yield) break; if (ec) continue; - if (port_.security == Port::Security::no_ssl) - { - auto const peer = std::make_shared (*this, - server_.journal(), endpoint, boost::asio::null_buffers(), - std::move(socket)); - add(peer); - peer->run(); - } - else if (port_.security == Port::Security::require_ssl) - { - auto const peer = std::make_shared (*this, - server_.journal(), endpoint, boost::asio::null_buffers(), - std::move(socket)); - add(peer); - peer->run(); - } - else + + if (ssl_ && plain_) { auto const c = std::make_shared ( - *this, std::move(socket), endpoint); + *this, std::move(socket), remote_address); add(c); c->run(); } + else if (ssl_ || plain_) + { + create(ssl_, boost::asio::null_buffers{}, + std::move(socket), remote_address); + } } } diff --git a/src/ripple/http/impl/Door.h b/src/ripple/server/impl/Door.h similarity index 90% rename from src/ripple/http/impl/Door.h rename to src/ripple/server/impl/Door.h index 77f80bb6f7..a7f2f589a5 100644 --- a/src/ripple/http/impl/Door.h +++ b/src/ripple/server/impl/Door.h @@ -17,11 +17,10 @@ */ //============================================================================== -#ifndef RIPPLE_HTTP_DOOR_H_INCLUDED -#define RIPPLE_HTTP_DOOR_H_INCLUDED +#ifndef RIPPLE_SERVER_DOOR_H_INCLUDED +#define RIPPLE_SERVER_DOOR_H_INCLUDED -#include -#include +#include #include #include #include @@ -71,11 +70,11 @@ private: private: socket_type socket_; timer_type timer_; - endpoint_type remote_endpoint_; + endpoint_type remote_address_; public: detector (Door& door, socket_type&& socket, - endpoint_type endpoint); + endpoint_type remote_address); void run(); void close() override; @@ -84,7 +83,7 @@ private: void do_detect (yield_context yield); }; - Port port_; + std::shared_ptr port_; ServerImpl& server_; acceptor_type acceptor_; boost::asio::io_service::strand strand_; @@ -92,6 +91,8 @@ private: std::condition_variable cond_; boost::container::flat_map< Child*, std::weak_ptr> list_; + bool ssl_; + bool plain_; public: Door (boost::asio::io_service& io_service, @@ -113,7 +114,7 @@ public: Port const& port() const { - return port_; + return *port_; } // Work-around because we can't call shared_from_this in ctor @@ -132,7 +133,8 @@ public: private: void add (std::shared_ptr const& child); - void create (bool ssl, beast::asio::streambuf&& buf, + template + void create (bool ssl, ConstBufferSequence const& buffers, socket_type&& socket, endpoint_type remote_address); void do_accept (yield_context yield); diff --git a/src/ripple/net/impl/RPCUtil.cpp b/src/ripple/server/impl/JSONRPCUtil.cpp similarity index 64% rename from src/ripple/net/impl/RPCUtil.cpp rename to src/ripple/server/impl/JSONRPCUtil.cpp index ed14924b17..7ca584456a 100644 --- a/src/ripple/net/impl/RPCUtil.cpp +++ b/src/ripple/server/impl/JSONRPCUtil.cpp @@ -17,23 +17,17 @@ */ //============================================================================== +#include +#include #include #include #include +#include namespace ripple { unsigned int const gMaxHTTPHeaderSize = 0x02000000; -std::string gFormatStr ("v1"); - -// VFALCO TODO clean up this nonsense -std::string FormatFullVersion () -{ - return (gFormatStr); -} - - Json::Value JSONRPCError (int code, std::string const& message) { Json::Value error (Json::objectValue); @@ -44,42 +38,6 @@ Json::Value JSONRPCError (int code, std::string const& message) return error; } -// -// HTTP protocol -// -// This ain't Apache. We're just using HTTP header for the length field -// and to be compatible with other JSON-RPC implementations. -// - -std::string createHTTPPost ( - std::string const& strHost, - std::string const& strPath, - std::string const& strMsg, - std::map const& mapRequestHeaders) -{ - std::ostringstream s; - - // CHECKME this uses a different version than the replies below use. Is - // this by design or an accident or should it be using - // BuildInfo::getFullVersionString () as well? - - s << "POST " - << (strPath.empty () ? "/" : strPath) - << " HTTP/1.0\r\n" - << "User-Agent: " SYSTEM_NAME "-json-rpc/" << FormatFullVersion () << "\r\n" - << "Host: " << strHost << "\r\n" - << "Content-Type: application/json\r\n" - << "Content-Length: " << strMsg.size () << "\r\n" - << "Accept: application/json\r\n"; - - for (auto const& item : mapRequestHeaders) - s << item.first << ": " << item.second << "\r\n"; - - s << "\r\n" << strMsg; - - return s.str (); -} - std::string getHTTPHeaderTimestamp () { // CHECKME This is probably called often enough that optimizing it makes @@ -114,8 +72,7 @@ std::string HTTPReply (int nStatus, std::string const& strMsg) // CHECKME this returns a different version than the replies below. Is // this by design or an accident or should it be using // BuildInfo::getFullVersionString () as well? - ret.append ("Server: " SYSTEM_NAME "-json-rpc/"); - ret.append (FormatFullVersion ()); + ret.append ("Server: " SYSTEM_NAME "-json-rpc/v1"); ret.append ("\r\n"); // Be careful in modifying this! If you change the contents you MUST @@ -152,8 +109,9 @@ std::string HTTPReply (int nStatus, std::string const& strMsg) ret.append ("Connection: Keep-Alive\r\n"); - if (getConfig ().RPC_ALLOW_REMOTE) - ret.append ("Access-Control-Allow-Origin: *\r\n"); + // VFALCO TODO Determine if/when this header should be added + //if (getConfig ().RPC_ALLOW_REMOTE) + // ret.append ("Access-Control-Allow-Origin: *\r\n"); ret.append ("Content-Length: "); ret.append (std::to_string(strMsg.size () + 2)); @@ -242,67 +200,6 @@ int ReadHTTP (std::basic_istream& stream, std::map (calloc (s.size (), sizeof (char))); - - b64 = BIO_new (BIO_f_base64 ()); - BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_new_mem_buf (const_cast (s.data ()), s.size ()); - bmem = BIO_push (b64, bmem); - BIO_read (bmem, buffer, s.size ()); - BIO_free_all (bmem); - - std::string result (buffer); - free (buffer); - return result; -} - -bool HTTPAuthorized (const std::map& mapHeaders) -{ - bool const credentialsRequired (! getConfig().RPC_USER.empty() && - ! getConfig().RPC_PASSWORD.empty()); - if (! credentialsRequired) - return true; - - auto const it = mapHeaders.find ("authorization"); - if ((it == mapHeaders.end ()) || (it->second.substr (0, 6) != "Basic ")) - return false; - - std::string strUserPass64 = it->second.substr (6); - boost::trim (strUserPass64); - std::string strUserPass = DecodeBase64 (strUserPass64); - std::string::size_type nColon = strUserPass.find (":"); - - if (nColon == std::string::npos) - return false; - - std::string strUser = strUserPass.substr (0, nColon); - std::string strPassword = strUserPass.substr (nColon + 1); - return (strUser == getConfig ().RPC_USER) && (strPassword == getConfig ().RPC_PASSWORD); -} - -// -// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, -// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were -// unspecified (HTTP errors and contents of 'error'). -// -// 1.0 spec: http://json-rpc.org/wiki/specification -// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http -// - -std::string JSONRPCRequest (std::string const& strMethod, Json::Value const& params, Json::Value const& id) -{ - Json::Value request; - request[jss::method] = strMethod; - request[jss::params] = params; - request[jss::id] = id; - return to_string (request) + "\n"; -} - std::string JSONRPCReply (Json::Value const& result, Json::Value const& error, Json::Value const& id) { Json::Value reply (Json::objectValue); diff --git a/src/ripple/net/RPCUtil.h b/src/ripple/server/impl/JSONRPCUtil.h similarity index 66% rename from src/ripple/net/RPCUtil.h rename to src/ripple/server/impl/JSONRPCUtil.h index 23c05b4632..3ef5ad458d 100644 --- a/src/ripple/net/RPCUtil.h +++ b/src/ripple/server/impl/JSONRPCUtil.h @@ -17,35 +17,23 @@ */ //============================================================================== -#ifndef RIPPLE_NET_RPC_RPCUTIL_H_INCLUDED -#define RIPPLE_NET_RPC_RPCUTIL_H_INCLUDED +#ifndef RIPPLE_SERVER_JSONRPCUTIL_H_INCLUDED +#define RIPPLE_SERVER_JSONRPCUTIL_H_INCLUDED #include namespace ripple { -// VFALCO TODO Wrap these up into a class. It looks like they just do some -// convenience packaging of JSON data from the pieces. It looks -// Ripple client protocol-specific. -// -extern std::string JSONRPCRequest (std::string const& strMethod, Json::Value const& params, - Json::Value const& id); +// VFALCO These functions are all deprecated they are inefficient and have poor signatures. extern std::string JSONRPCReply (Json::Value const& result, Json::Value const& error, Json::Value const& id); extern Json::Value JSONRPCError (int code, std::string const& message); -extern std::string createHTTPPost (std::string const& strHost, std::string const& strPath, std::string const& strMsg, - const std::map& mapRequestHeaders); - +// VFALCO This needs to be rewritten to use beast::http::message extern std::string HTTPReply (int nStatus, std::string const& strMsg); -// VFALCO TODO Create a HTTPHeaders class with a nice interface instead of the std::map -// -extern bool HTTPAuthorized (std::map const& mapHeaders); - // VFALCO NOTE This one looks like it does some sort of stream i/o -// extern int ReadHTTP (std::basic_istream& stream, std::map& mapHeadersRet, std::string& strMessageRet); diff --git a/src/ripple/http/impl/Peer.h b/src/ripple/server/impl/Peer.h similarity index 80% rename from src/ripple/http/impl/Peer.h rename to src/ripple/server/impl/Peer.h index e6e5e1fbdb..0cfde6de8f 100644 --- a/src/ripple/http/impl/Peer.h +++ b/src/ripple/server/impl/Peer.h @@ -17,14 +17,13 @@ */ //============================================================================== -#ifndef RIPPLE_HTTP_PEER_H_INCLUDED -#define RIPPLE_HTTP_PEER_H_INCLUDED +#ifndef RIPPLE_SERVER_PEER_H_INCLUDED +#define RIPPLE_SERVER_PEER_H_INCLUDED -#include -#include -#include -#include -#include +#include +#include +#include +#include #include #include // for is_short_read? #include @@ -54,12 +53,14 @@ class Peer { protected: using clock_type = std::chrono::system_clock; + using error_code = boost::system::error_code; using endpoint_type = boost::asio::ip::tcp::endpoint; using waitable_timer = boost::asio::basic_waitable_timer ; + using yield_context = boost::asio::yield_context; enum { - // Size of our receive buffer + // Size of our read/write buffer bufferSize = 4 * 1024, // Max seconds without completing a message @@ -85,7 +86,7 @@ protected: boost::asio::io_service::work work_; boost::asio::io_service::strand strand_; waitable_timer timer_; - endpoint_type endpoint_; + endpoint_type remote_address_; beast::Journal journal_; std::string id_; @@ -111,7 +112,7 @@ protected: public: template Peer (Door& door, boost::asio::io_service& io_service, - beast::Journal journal, endpoint_type endpoint, + beast::Journal journal, endpoint_type remote_address, ConstBufferSequence const& buffers); virtual @@ -145,10 +146,14 @@ protected: on_timer (error_code ec); void - do_read (boost::asio::yield_context yield); + do_read (yield_context yield); void - do_write (boost::asio::yield_context yield); + do_write (yield_context yield); + + void + do_writer (std::shared_ptr const& writer, + bool keep_alive, yield_context yield); virtual void @@ -166,10 +171,16 @@ protected: return door_.server().journal(); } + Port const& + port() override + { + return door_.port(); + } + beast::IP::Endpoint remoteAddress() override { - return from_asio (endpoint_); + return beast::IPAddressConversion::from_asio(remote_address_); } beast::http::message& @@ -187,6 +198,10 @@ protected: void write (void const* buffer, std::size_t bytes) override; + void + write (std::shared_ptr const& writer, + bool keep_alive) override; + std::shared_ptr detach() override; @@ -202,13 +217,13 @@ protected: template template Peer::Peer (Door& door, boost::asio::io_service& io_service, - beast::Journal journal, endpoint_type endpoint, + beast::Journal journal, endpoint_type remote_address, ConstBufferSequence const& buffers) : Child(door) , work_ (io_service) , strand_ (io_service) , timer_ (io_service) - , endpoint_ (endpoint) + , remote_address_ (remote_address) , journal_ (journal) { read_buf_.commit(boost::asio::buffer_copy(read_buf_.prepare ( @@ -217,7 +232,7 @@ Peer::Peer (Door& door, boost::asio::io_service& io_service, nid_ = ++sid; id_ = std::string("#") + std::to_string(nid_) + " "; if (journal_.trace) journal_.trace << id_ << - "accept: " << endpoint.address(); + "accept: " << remote_address_.address(); when_ = clock_type::now(); when_str_ = beast::Time::getCurrentTime().formatted ( "%Y-%b-%d %H:%M:%S").toStdString(); @@ -251,7 +266,7 @@ Peer::close() (void(Peer::*)(void))&Peer::close, impl().shared_from_this())); error_code ec; - impl().socket_.lowest_layer().close(ec); + impl().stream_.lowest_layer().close(ec); } //------------------------------------------------------------------------------ @@ -265,7 +280,7 @@ Peer::fail (error_code ec, char const* what) ec_ = ec; if (journal_.trace) journal_.trace << id_ << std::string(what) << ": " << ec.message(); - impl().socket_.lowest_layer().close (ec); + impl().stream_.lowest_layer().close (ec); } } @@ -308,7 +323,7 @@ Peer::on_timer (error_code ec) template void -Peer::do_read (boost::asio::yield_context yield) +Peer::do_read (yield_context yield) { complete_ = false; @@ -322,7 +337,7 @@ Peer::do_read (boost::asio::yield_context yield) { start_timer(); auto const bytes_transferred = boost::asio::async_read ( - impl().socket_, read_buf_.prepare (bufferSize), + impl().stream_, read_buf_.prepare (bufferSize), boost::asio::transfer_at_least(1), yield[ec]); cancel_timer(); @@ -375,7 +390,7 @@ Peer::do_read (boost::asio::yield_context yield) // The write queue must not be empty upon entry. template void -Peer::do_write (boost::asio::yield_context yield) +Peer::do_write (yield_context yield) { error_code ec; std::size_t bytes = 0; @@ -405,7 +420,7 @@ Peer::do_write (boost::asio::yield_context yield) break; start_timer(); - bytes = boost::asio::async_write (impl().socket_, + bytes = boost::asio::async_write (impl().stream_, boost::asio::buffer (data, bytes), boost::asio::transfer_at_least(1), yield[ec]); cancel_timer(); @@ -424,6 +439,45 @@ Peer::do_write (boost::asio::yield_context yield) impl().shared_from_this(), std::placeholders::_1)); } +template +void +Peer::do_writer (std::shared_ptr const& writer, + bool keep_alive, yield_context yield) +{ + std::function resume; + { + auto const p = impl().shared_from_this(); + resume = std::function ( + [this, p, writer, keep_alive]() + { + boost::asio::spawn (strand_, std::bind ( + &Peer::do_writer, p, writer, keep_alive, + std::placeholders::_1)); + }); + } + + for(;;) + { + if (! writer->prepare (bufferSize, resume)) + return; + error_code ec; + auto const bytes_transferred = boost::asio::async_write ( + impl().stream_, writer->data(), boost::asio::transfer_at_least(1), + yield[ec]); + if (ec) + return fail (ec, "writer"); + writer->consume(bytes_transferred); + if (writer->complete()) + break; + } + + if (! keep_alive) + return do_close(); + + boost::asio::spawn (strand_, std::bind (&Peer::do_read, + impl().shared_from_this(), std::placeholders::_1)); +} + //------------------------------------------------------------------------------ // Send a copy of the data. @@ -446,6 +500,17 @@ Peer::write (void const* buffer, std::size_t bytes) impl().shared_from_this(), std::placeholders::_1)); } +template +void +Peer::write (std::shared_ptr const& writer, + bool keep_alive) +{ + boost::asio::spawn (strand_, std::bind ( + &Peer::do_writer, impl().shared_from_this(), + writer, keep_alive, std::placeholders::_1)); +} + +// DEPRECATED // Make the Session asynchronous template std::shared_ptr @@ -454,6 +519,7 @@ Peer::detach() return impl().shared_from_this(); } +// DEPRECATED // Called to indicate the response has been written (but not sent) template void @@ -474,6 +540,7 @@ Peer::complete() impl().shared_from_this(), std::placeholders::_1)); } +// DEPRECATED // Called from the Handler to close the session. template void @@ -494,7 +561,7 @@ Peer::close (bool graceful) } error_code ec; - impl().socket_.lowest_layer().close (ec); + impl().stream_.lowest_layer().close (ec); } } diff --git a/src/ripple/http/impl/PlainPeer.h b/src/ripple/server/impl/PlainPeer.h similarity index 67% rename from src/ripple/http/impl/PlainPeer.h rename to src/ripple/server/impl/PlainPeer.h index 3b96607b2b..aee9db9042 100644 --- a/src/ripple/http/impl/PlainPeer.h +++ b/src/ripple/server/impl/PlainPeer.h @@ -17,10 +17,11 @@ */ //============================================================================== -#ifndef RIPPLE_HTTP_PLAINPEER_H_INCLUDED -#define RIPPLE_HTTP_PLAINPEER_H_INCLUDED +#ifndef RIPPLE_SERVER_PLAINPEER_H_INCLUDED +#define RIPPLE_SERVER_PLAINPEER_H_INCLUDED -#include +#include +#include namespace ripple { namespace HTTP { @@ -32,7 +33,8 @@ class PlainPeer private: friend class Peer ; using socket_type = boost::asio::ip::tcp::socket; - socket_type socket_; + + socket_type stream_; public: template @@ -54,10 +56,10 @@ private: template PlainPeer::PlainPeer (Door& door, beast::Journal journal, - endpoint_type endpoint, ConstBufferSequence const& buffers, - boost::asio::ip::tcp::socket&& socket) - : Peer (door, socket.get_io_service(), journal, endpoint, buffers) - , socket_(std::move(socket)) + endpoint_type remote_address, ConstBufferSequence const& buffers, + socket_type&& socket) + : Peer (door, socket.get_io_service(), journal, remote_address, buffers) + , stream_(std::move(socket)) { } @@ -65,7 +67,7 @@ void PlainPeer::run () { door_.server().handler().onAccept (session()); - if (! socket_.is_open()) + if (! stream_.is_open()) return; boost::asio::spawn (strand_, std::bind (&PlainPeer::do_read, @@ -75,27 +77,36 @@ PlainPeer::run () void PlainPeer::do_request() { - // Perform half-close when Connection: close and not SSL - error_code ec; - if (! message_.keep_alive()) - socket_.shutdown (socket_type::shutdown_receive, ec); - - if (! ec) - { - ++request_count_; - door_.server().handler().onRequest (session()); + ++request_count_; + auto const what = door_.server().handler().onHandoff (session(), + std::move(stream_), std::move(message_), remote_address_); + if (what.moved) return; + error_code ec; + if (what.response) + { + // half-close on Connection: close + if (! what.keep_alive) + stream_.shutdown (socket_type::shutdown_receive, ec); + if (ec) + return fail (ec, "request"); + return write(what.response, what.keep_alive); } + // Perform half-close when Connection: close and not SSL + if (! message_.keep_alive()) + stream_.shutdown (socket_type::shutdown_receive, ec); if (ec) - fail (ec, "request"); + return fail (ec, "request"); + // legacy + door_.server().handler().onRequest (session()); } void PlainPeer::do_close() { error_code ec; - socket_.shutdown (socket_type::shutdown_send, ec); + stream_.shutdown (socket_type::shutdown_send, ec); } } diff --git a/src/ripple/server/impl/Role.cpp b/src/ripple/server/impl/Role.cpp new file mode 100644 index 0000000000..37f0b4a2c4 --- /dev/null +++ b/src/ripple/server/impl/Role.cpp @@ -0,0 +1,93 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include + +namespace ripple { + +Role +adminRole (HTTP::Port const& port, Json::Value const& params, + beast::IP::Endpoint const& remoteIp, + std::vector const& admin_allow) +{ + Role role (Role::FORBID); + + bool const bPasswordSupplied = + params.isMember ("admin_user") || + params.isMember ("admin_password"); + + bool const bPasswordRequired = + ! port.admin_user.empty() || ! port.admin_password.empty(); + + bool bPasswordWrong; + + if (bPasswordSupplied) + { + if (bPasswordRequired) + { + // Required, and supplied, check match + bPasswordWrong = + (port.admin_user != + (params.isMember ("admin_user") ? params["admin_user"].asString () : "")) + || + (port.admin_password != + (params.isMember ("admin_user") ? params["admin_password"].asString () : "")); + } + else + { + // Not required, but supplied + bPasswordWrong = false; + } + } + else + { + // Required but not supplied, + bPasswordWrong = bPasswordRequired; + } + + // Meets IP restriction for admin. + beast::IP::Endpoint const remote_addr (remoteIp.at_port (0)); + bool bAdminIP = false; + + // VFALCO TODO Don't use this! + for (auto const& allow_addr : admin_allow) + { + if (allow_addr == remote_addr) + { + bAdminIP = true; + break; + } + } + + if (bPasswordWrong // Wrong + || (bPasswordSupplied && !bAdminIP)) // Supplied and doesn't meet IP filter. + { + role = Role::FORBID; + } + // If supplied, password is correct. + else + { + // Allow admin, if from admin IP and no password is required or it was supplied and correct. + role = bAdminIP && (!bPasswordRequired || bPasswordSupplied) ? Role::ADMIN : Role::GUEST; + } + + return role; +} + +} diff --git a/src/ripple/server/impl/SSLPeer.h b/src/ripple/server/impl/SSLPeer.h new file mode 100644 index 0000000000..8b95e0d772 --- /dev/null +++ b/src/ripple/server/impl/SSLPeer.h @@ -0,0 +1,203 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_SERVER_SSLPEER_H_INCLUDED +#define RIPPLE_SERVER_SSLPEER_H_INCLUDED + +#include +#include +#include // + +namespace ripple { +namespace HTTP { + +class SSLPeer + : public Peer + , public std::enable_shared_from_this +{ +private: + friend class Peer ; + using socket_type = boost::asio::ip::tcp::socket; + using stream_type = boost::asio::ssl::stream ; + + std::unique_ptr ssl_bundle_; + stream_type& stream_; + +public: + template + SSLPeer (Door& door, beast::Journal journal, endpoint_type remote_address, + ConstBufferSequence const& buffers, socket_type&& socket); + + void + run(); + +private: + void + do_handshake (yield_context yield); + + void + do_request(); + + void + do_close(); + + void + on_shutdown (error_code ec); +}; + +//------------------------------------------------------------------------------ + +// Detects the legacy peer protocol handshake. */ +template +static +std::pair +detect_peer_protocol (Socket& socket, StreamBuf& buf, Yield yield) +{ + std::pair result; + result.second = false; + for(;;) + { + std::size_t const max = 6; // max bytes needed + unsigned char data[max]; + auto const n = boost::asio::buffer_copy( + boost::asio::buffer(data), buf.data()); + + /* Protocol messages are framed by a 6 byte header which includes + a big-endian 4-byte length followed by a big-endian 2-byte type. + The type for 'hello' is 1. + */ + if (n>=1 && data[0] != 0) + break;; + if (n>=2 && data[1] != 0) + break; + if (n>=5 && data[4] != 0) + break; + if (n>=6) + { + if (data[5] == 1) + result.second = true; + break; + } + std::size_t const bytes_transferred = boost::asio::async_read( + socket, buf.prepare(max - n), boost::asio::transfer_at_least(1), + yield[result.first]); + if (result.first) + break; + buf.commit(bytes_transferred); + } + return result; +} + +template +SSLPeer::SSLPeer (Door& door, beast::Journal journal, + endpoint_type remote_address, ConstBufferSequence const& buffers, + socket_type&& socket) + : Peer (door, socket.get_io_service(), journal, remote_address, buffers) + , ssl_bundle_(std::make_unique( + port().context, std::move(socket))) + , stream_(ssl_bundle_->stream) +{ +} + +// Called when the acceptor accepts our socket. +void +SSLPeer::run() +{ + door_.server().handler().onAccept (session()); + if (! stream_.lowest_layer().is_open()) + return; + + boost::asio::spawn (strand_, std::bind (&SSLPeer::do_handshake, + shared_from_this(), std::placeholders::_1)); +} + +void +SSLPeer::do_handshake (yield_context yield) +{ + error_code ec; + stream_.set_verify_mode (boost::asio::ssl::verify_none); + start_timer(); + read_buf_.consume(stream_.async_handshake( + stream_type::server, read_buf_.data(), yield[ec])); + cancel_timer(); + if (ec) + return fail (ec, "handshake"); + bool const legacy = port().protocol.count("peer") > 0; + bool const http = + port().protocol.count("peer") > 0 || + //|| port().protocol.count("wss") > 0 + port().protocol.count("https") > 0; + if (legacy) + { + auto const result = detect_peer_protocol(stream_, read_buf_, yield); + if (result.first) + return fail (result.first, "detect_legacy_handshake"); + if (result.second) + { + std::vector storage (read_buf_.size()); + boost::asio::mutable_buffers_1 buffer ( + boost::asio::mutable_buffer(storage.data(), storage.size())); + boost::asio::buffer_copy(buffer, read_buf_.data()); + return door_.server().handler().onLegacyPeerHello( + std::move(ssl_bundle_), buffer, remote_address_); + } + } + if (http) + { + boost::asio::spawn (strand_, std::bind (&SSLPeer::do_read, + shared_from_this(), std::placeholders::_1)); + return; + } + // this will be destroyed +} + +void +SSLPeer::do_request() +{ + ++request_count_; + auto const what = door_.server().handler().onHandoff (session(), + std::move(ssl_bundle_), std::move(message_), remote_address_); + if (what.moved) + return; + if (what.response) + return write(what.response, what.keep_alive); + // legacy + door_.server().handler().onRequest (session()); +} + +void +SSLPeer::do_close() +{ + start_timer(); + stream_.async_shutdown (strand_.wrap (std::bind ( + &SSLPeer::on_shutdown, shared_from_this(), + std::placeholders::_1))); + cancel_timer(); +} + +void +SSLPeer::on_shutdown (error_code ec) +{ + stream_.lowest_layer().close(ec); +} + +} +} + +#endif diff --git a/src/ripple/server/impl/ServerHandlerImp.cpp b/src/ripple/server/impl/ServerHandlerImp.cpp new file mode 100644 index 0000000000..c8a86b39e6 --- /dev/null +++ b/src/ripple/server/impl/ServerHandlerImp.cpp @@ -0,0 +1,739 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // +#include +#include +#include +#include +#include + +namespace ripple { + +ServerHandler::ServerHandler (Stoppable& parent) + : Stoppable ("ServerHandler", parent) + , Source ("server") +{ +} + +//------------------------------------------------------------------------------ + +ServerHandlerImp::ServerHandlerImp (Stoppable& parent, + boost::asio::io_service& io_service, JobQueue& jobQueue, + NetworkOPs& networkOPs, Resource::Manager& resourceManager) + : ServerHandler (parent) + , m_resourceManager (resourceManager) + , m_journal (deprecatedLogs().journal("Server")) + , m_jobQueue (jobQueue) + , m_networkOPs (networkOPs) + , m_server (HTTP::make_Server( + *this, io_service, deprecatedLogs().journal("Server"))) +{ +} + +ServerHandlerImp::~ServerHandlerImp() +{ + m_server = nullptr; +} + +void +ServerHandlerImp::setup (Setup const& setup, beast::Journal journal) +{ + setup_ = setup; + m_server->ports (setup.ports); +} + +//------------------------------------------------------------------------------ + +void +ServerHandlerImp::onStop() +{ + m_server->close(); +} + +//------------------------------------------------------------------------------ + +void +ServerHandlerImp::onAccept (HTTP::Session& session) +{ +} + +bool +ServerHandlerImp::onAccept (HTTP::Session& session, + boost::asio::ip::tcp::endpoint endpoint) +{ + return true; +} + +void +ServerHandlerImp::onLegacyPeerHello ( + std::unique_ptr&& ssl_bundle, + boost::asio::const_buffer buffer, + boost::asio::ip::tcp::endpoint remote_address) +{ + // VFALCO TODO Inject Overlay + getApp().overlay().onLegacyPeerHello(std::move(ssl_bundle), + buffer, remote_address); +} + +auto +ServerHandlerImp::onHandoff (HTTP::Session& session, + std::unique_ptr && bundle, + beast::http::message&& request, + boost::asio::ip::tcp::endpoint remote_address) -> + Handoff +{ + if (session.port().protocol.count("wss") > 0 && + isWebsocketUpgrade (request)) + { + // Pass to websockets + Handoff handoff; + // handoff.moved = true; + return handoff; + } + if (session.port().protocol.count("peer") > 0) + return getApp().overlay().onHandoff (std::move(bundle), + std::move(request), remote_address); + // Pass through to legacy onRequest + return Handoff{}; +} + +auto +ServerHandlerImp::onHandoff (HTTP::Session& session, + boost::asio::ip::tcp::socket&& socket, + beast::http::message&& request, + boost::asio::ip::tcp::endpoint remote_address) -> + Handoff +{ + if (session.port().protocol.count("ws") > 0 && + isWebsocketUpgrade (request)) + { + // Pass to websockets + Handoff handoff; + // handoff.moved = true; + return handoff; + } + // Pass through to legacy onRequest + return Handoff{}; +} + +void +ServerHandlerImp::onRequest (HTTP::Session& session) +{ + // Check user/password authorization + if (! authorized (session.port(), + build_map(session.request().headers))) + { + session.write (HTTPReply (403, "Forbidden")); + session.close (true); + return; + } + + m_jobQueue.addJob (jtCLIENT, "RPC-Client", std::bind ( + &ServerHandlerImp::processSession, this, std::placeholders::_1, + session.detach())); +} + +void +ServerHandlerImp::onClose (HTTP::Session& session, + boost::system::error_code const&) +{ +} + +void +ServerHandlerImp::onStopped (HTTP::Server&) +{ + stopped(); +} + +//------------------------------------------------------------------------------ + +// Dispatched on the job queue +void +ServerHandlerImp::processSession (Job& job, + std::shared_ptr const& session) +{ + session->write (processRequest (session->port(), + to_string(session->body()), session->remoteAddress().at_port(0))); + + if (session->request().keep_alive()) + { + session->complete(); + } + else + { + session->close (true); + } +} + +std::string +ServerHandlerImp::createResponse ( + int statusCode, + std::string const& description) +{ + return HTTPReply (statusCode, description); +} + +// VFALCO ARGH! returning a single std::string for the entire response? +std::string +ServerHandlerImp::processRequest (HTTP::Port const& port, + std::string const& request, beast::IP::Endpoint const& remoteIPAddress) +{ + Json::Value jsonRPC; + { + Json::Reader reader; + if ((request.size () > 1000000) || + ! reader.parse (request, jsonRPC) || + jsonRPC.isNull () || + ! jsonRPC.isObject ()) + { + return createResponse (400, "Unable to parse request"); + } + } + + auto const& admin_allow = getConfig().RPC_ADMIN_ALLOW; + auto role = Role::FORBID; + if (jsonRPC.isObject() && jsonRPC.isMember("params") && + jsonRPC["params"].isArray() && jsonRPC["params"].size() > 0 && + jsonRPC["params"][Json::UInt(0)].isObject()) + role = adminRole(port, jsonRPC["params"][Json::UInt(0)], + remoteIPAddress, admin_allow); + else + role = adminRole(port, Json::objectValue, + remoteIPAddress, admin_allow); + + Resource::Consumer usage; + + if (role == Role::ADMIN) + usage = m_resourceManager.newAdminEndpoint (remoteIPAddress.to_string()); + else + usage = m_resourceManager.newInboundEndpoint(remoteIPAddress); + + if (usage.disconnect ()) + return createResponse (503, "Server is overloaded"); + + // Parse id now so errors from here on will have the id + // + // VFALCO NOTE Except that "id" isn't included in the following errors. + // + Json::Value const id = jsonRPC ["id"]; + + Json::Value const method = jsonRPC ["method"]; + + if (method.isNull ()) + return createResponse (400, "Null method"); + + if (! method.isString ()) + return createResponse (400, "method is not string"); + + std::string strMethod = method.asString (); + if (strMethod.empty()) + return createResponse (400, "method is empty"); + + // Parse params + Json::Value params = jsonRPC ["params"]; + + if (params.isNull ()) + params = Json::Value (Json::arrayValue); + + else if (!params.isArray ()) + return HTTPReply (400, "params unparseable"); + + // VFALCO TODO Shouldn't we handle this earlier? + // + if (role == Role::FORBID) + { + // VFALCO TODO Needs implementing + // FIXME Needs implementing + // XXX This needs rate limiting to prevent brute forcing password. + return HTTPReply (403, "Forbidden"); + } + + + std::string response; + RPCHandler rpcHandler (m_networkOPs); + Resource::Charge loadType = Resource::feeReferenceRPC; + + m_journal.debug << "Query: " << strMethod << params; + + Json::Value const result (rpcHandler.doRpcCommand ( + strMethod, params, role, loadType)); + m_journal.debug << "Reply: " << result; + + usage.charge (loadType); + + response = JSONRPCReply (result, Json::Value (), id); + + return createResponse (200, response); +} + +//------------------------------------------------------------------------------ + +// Returns `true` if the HTTP request is a Websockets Upgrade +// http://en.wikipedia.org/wiki/HTTP/1.1_Upgrade_header#Use_with_WebSockets +bool +ServerHandlerImp::isWebsocketUpgrade (beast::http::message const& request) +{ + if (request.upgrade()) + return request.headers["Upgrade"] == "websocket"; + return false; +} + +// VFALCO DEPRECATED +static std::string +DecodeBase64 (std::string s) +{ + // FIXME: This performs badly + BIO* b64, *bmem; + // Its 2014 and we're using calloc? + char* buffer = static_cast (calloc (s.size (), sizeof (char))); + b64 = BIO_new (BIO_f_base64 ()); + BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_new_mem_buf (const_cast (s.data ()), s.size ()); + bmem = BIO_push (b64, bmem); + BIO_read (bmem, buffer, s.size ()); + BIO_free_all (bmem); + std::string result (buffer); + free (buffer); + return result; +} + +// VFALCO TODO Rewrite to use beast::http::headers +bool +ServerHandlerImp::authorized (HTTP::Port const& port, + std::map const& h) +{ + if (port.user.empty() || port.password.empty()) + return true; + + auto const it = h.find ("authorization"); + if ((it == h.end ()) || (it->second.substr (0, 6) != "Basic ")) + return false; + std::string strUserPass64 = it->second.substr (6); + boost::trim (strUserPass64); + std::string strUserPass = DecodeBase64 (strUserPass64); + std::string::size_type nColon = strUserPass.find (":"); + if (nColon == std::string::npos) + return false; + std::string strUser = strUserPass.substr (0, nColon); + std::string strPassword = strUserPass.substr (nColon + 1); + return strUser == port.user && strPassword == port.password; +} + +//------------------------------------------------------------------------------ + +void +ServerHandlerImp::onWrite (beast::PropertyStream::Map& map) +{ + m_server->onWrite (map); +} + +//------------------------------------------------------------------------------ + +// Copied from Config::getAdminRole and modified to use the Port +Role +adminRole (HTTP::Port const& port, + Json::Value const& params, beast::IP::Endpoint const& remoteIp) +{ + Role role (Role::FORBID); + + bool const bPasswordSupplied = + params.isMember ("admin_user") || + params.isMember ("admin_password"); + + bool const bPasswordRequired = + ! port.admin_user.empty() || ! port.admin_password.empty(); + + bool bPasswordWrong; + + if (bPasswordSupplied) + { + if (bPasswordRequired) + { + // Required, and supplied, check match + bPasswordWrong = + (port.admin_user != + (params.isMember ("admin_user") ? params["admin_user"].asString () : "")) + || + (port.admin_password != + (params.isMember ("admin_user") ? params["admin_password"].asString () : "")); + } + else + { + // Not required, but supplied + bPasswordWrong = false; + } + } + else + { + // Required but not supplied, + bPasswordWrong = bPasswordRequired; + } + + // Meets IP restriction for admin. + beast::IP::Endpoint const remote_addr (remoteIp.at_port (0)); + bool bAdminIP = false; + + for (auto const& allow_addr : getConfig().RPC_ADMIN_ALLOW) + { + if (allow_addr == remote_addr) + { + bAdminIP = true; + break; + } + } + + if (bPasswordWrong // Wrong + || (bPasswordSupplied && !bAdminIP)) // Supplied and doesn't meet IP filter. + { + role = Role::FORBID; + } + // If supplied, password is correct. + else + { + // Allow admin, if from admin IP and no password is required or it was supplied and correct. + role = bAdminIP && (!bPasswordRequired || bPasswordSupplied) ? Role::ADMIN : Role::GUEST; + } + + return role; +} + +//------------------------------------------------------------------------------ + +namespace detail { + +// Parse a comma-delimited list of values. +std::vector +parse_csv (std::string const& in, std::ostream& log) +{ + auto first = in.cbegin(); + auto const last = in.cend(); + std::vector result; + if (first != last) + { + static boost::regex const re( + "^" // start of line + "(?:\\s*)" // whitespace (optional) + "([a-zA-Z][_a-zA-Z0-9]*)" // identifier + "(?:\\s*)" // whitespace (optional) + "(?:,?)" // comma (optional) + "(?:\\s*)" // whitespace (optional) + , boost::regex_constants::optimize + ); + for(;;) + { + boost::smatch m; + if (! boost::regex_search(first, last, m, re, + boost::regex_constants::match_continuous)) + { + log << "Expected \n"; + throw std::exception(); + } + result.push_back(m[1]); + first = m[0].second; + if (first == last) + break; + } + } + return result; +} + +// Intermediate structure used for parsing +struct ParsedPort +{ + std::string name; + std::set protocol; + std::string user; + std::string password; + std::string admin_user; + std::string admin_password; + std::string ssl_key; + std::string ssl_cert; + std::string ssl_chain; + + boost::optional ip; + boost::optional port; + boost::optional allow_admin; +}; + +void +parse_Port (ParsedPort& port, Section const& section, std::ostream& log) +{ + { + auto result = section.find("ip"); + if (result.second) + { + try + { + port.ip = boost::asio::ip::address::from_string(result.first); + } + catch(...) + { + log << "Invalid value '" << result.first << + "' for key 'ip' in [" << section.name() << "]\n"; + throw std::exception(); + } + } + } + + { + auto const result = section.find("port"); + if (result.second) + { + auto const ul = std::stoul(result.first); + if (ul > std::numeric_limits::max()) + { + log << + "Value '" << result.first << "' for key 'port' is out of range\n"; + throw std::exception(); + } + if (ul == 0) + { + log << + "Value '0' for key 'port' is invalid\n"; + throw std::exception(); + } + port.port = static_cast(ul); + } + } + + { + auto const result = section.find("protocol"); + if (result.second) + { + for (auto const& s : parse_csv(result.first, log)) + port.protocol.insert(s); + } + } + + { + auto const result = section.find("admin"); + if (result.second) + { + if (result.first == "no") + { + port.allow_admin = false; + } + else if (result.first == "allow") + { + port.allow_admin = true; + } + else + { + log << "Invalid value '" << result.first << + "' for key 'admin' in [" << section.name() << "]\n"; + throw std::exception(); + } + } + } + + set(port.user, "user", section); + set(port.password, "password", section); + set(port.admin_user, "admin_user", section); + set(port.admin_password, "admin_password", section); + set(port.ssl_key, "ssl_key", section); + set(port.ssl_cert, "ssl_cert", section); + set(port.ssl_chain, "ssl_chain", section); +} + +HTTP::Port +to_Port(ParsedPort const& parsed, std::ostream& log) +{ + HTTP::Port p; + p.name = parsed.name; + + if (! parsed.ip) + { + log << "Missing 'ip' in [" << p.name << "]\n"; + throw std::exception(); + } + p.ip = *parsed.ip; + + if (! parsed.port) + { + log << "Missing 'port' in [" << p.name << "]\n"; + throw std::exception(); + } + else if (*parsed.port == 0) + { + log << "Port " << *parsed.port << "in [" << p.name << "] is invalid\n"; + throw std::exception(); + } + p.port = *parsed.port; + + if (! parsed.allow_admin) + p.allow_admin = false; + else + p.allow_admin = *parsed.allow_admin; + + if (parsed.protocol.empty()) + { + log << "Missing 'protocol' in [" << p.name << "]\n"; + throw std::exception(); + } + p.protocol = parsed.protocol; + if (p.websockets() && + (parsed.protocol.count("peer") > 0 || + parsed.protocol.count("http") > 0 || + parsed.protocol.count("https") > 0)) + { + log << "Invalid protocol combination in [" << p.name << "]\n"; + throw std::exception(); + } + + p.user = parsed.user; + p.password = parsed.password; + p.admin_user = parsed.admin_user; + p.admin_password = parsed.admin_password; + p.ssl_key = parsed.ssl_key; + p.ssl_cert = parsed.ssl_cert; + p.ssl_chain = parsed.ssl_chain; + + if (p.ssl_key.empty() && p.ssl_cert.empty() && + p.ssl_chain.empty()) + p.context = make_SSLContext(); + else + p.context = make_SSLContextAuthed ( + p.ssl_key, p.ssl_cert, p.ssl_chain); + + return p; +} + +std::vector +parse_Ports (BasicConfig const& config, std::ostream& log) +{ + std::vector result; + + if (! config.exists("server")) + { + log << + "Missing section: [server]\n"; + return result; + } + + ParsedPort common; + parse_Port (common, config["server"], log); + + auto const& names = config.section("server").values(); + result.reserve(names.size()); + for (auto const& name : names) + { + if (! config.exists(name)) + { + log << + "Missing section: [" << name << "]\n"; + throw std::exception(); + } + ParsedPort parsed = common; + parsed.name = name; + parse_Port(parsed, config[name], log); + result.push_back(to_Port(parsed, log)); + } + + std::size_t count = 0; + for (auto const& p : result) + if (p.protocol.count("peer") > 0) + ++count; + if (count > 1) + { + log << "Error: More than one peer protocol configured in [server]\n"; + throw std::exception(); + } + if (count == 0) + log << "Warning: No peer protocol configured\n"; + + return result; +} + +// Fill out the client portion of the Setup +void +setup_Client (ServerHandler::Setup& setup) +{ + decltype(setup.ports)::const_iterator iter; + for (iter = setup.ports.cbegin(); + iter != setup.ports.cend(); ++iter) + if (iter->protocol.count("http") > 0 || + iter->protocol.count("https") > 0) + break; + if (iter == setup.ports.cend()) + return; + setup.client.secure = + iter->protocol.count("https") > 0; + setup.client.ip = iter->ip.to_string(); + // VFALCO HACK! to make localhost work + if (setup.client.ip == "0.0.0.0") + setup.client.ip = "127.0.0.1"; + setup.client.port = iter->port; + setup.client.user = iter->user; + setup.client.password = iter->password; + setup.client.admin_user = iter->admin_user; + setup.client.admin_password = iter->admin_password; +} + +// Fill out the overlay portion of the Setup +void +setup_Overlay (ServerHandler::Setup& setup) +{ + auto const iter = std::find_if(setup.ports.cbegin(), setup.ports.cend(), + [](HTTP::Port const& port) + { + return port.protocol.count("peer") > 0; + }); + if (iter == setup.ports.cend()) + { + setup.overlay.port = 0; + return; + } + setup.overlay.ip = iter->ip; + setup.overlay.port = iter->port; +} + +} + +ServerHandler::Setup +setup_ServerHandler (BasicConfig const& config, std::ostream& log) +{ + ServerHandler::Setup setup; + setup.ports = detail::parse_Ports (config, log); + detail::setup_Client(setup); + detail::setup_Overlay(setup); + return setup; +} + +std::unique_ptr +make_ServerHandler (beast::Stoppable& parent, + boost::asio::io_service& io_service, JobQueue& jobQueue, + NetworkOPs& networkOPs, Resource::Manager& resourceManager) +{ + return std::make_unique (parent, io_service, + jobQueue, networkOPs, resourceManager); +} + +} diff --git a/src/ripple/app/main/ServerHandlerImp.h b/src/ripple/server/impl/ServerHandlerImp.h similarity index 60% rename from src/ripple/app/main/ServerHandlerImp.h rename to src/ripple/server/impl/ServerHandlerImp.h index 2e14d7f1b4..8f69241119 100644 --- a/src/ripple/app/main/ServerHandlerImp.h +++ b/src/ripple/server/impl/ServerHandlerImp.h @@ -17,12 +17,12 @@ */ //============================================================================== -#ifndef RIPPLE_APP_MAIN_SERVERHANDLERIMP_H_INCLUDED -#define RIPPLE_APP_MAIN_SERVERHANDLERIMP_H_INCLUDED +#ifndef RIPPLE_SERVER_SERVERHANDLERIMP_H_INCLUDED +#define RIPPLE_SERVER_SERVERHANDLERIMP_H_INCLUDED -#include -#include -#include +#include +#include +#include #include namespace ripple { @@ -39,19 +39,24 @@ private: JobQueue& m_jobQueue; NetworkOPs& m_networkOPs; std::unique_ptr m_server; - std::unique_ptr m_context; - RPC::Setup setup_; + Setup setup_; public: - ServerHandlerImp (Stoppable& parent, JobQueue& jobQueue, - NetworkOPs& networkOPs, Resource::Manager& resourceManager, - RPC::Setup const& setup); + ServerHandlerImp (Stoppable& parent, boost::asio::io_service& io_service, + JobQueue& jobQueue, NetworkOPs& networkOPs, + Resource::Manager& resourceManager); ~ServerHandlerImp(); private: void - setup (beast::Journal journal) override; + setup (Setup const& setup, beast::Journal journal) override; + + Setup const& + setup() const override + { + return setup_; + } // // Stoppable @@ -67,6 +72,25 @@ private: void onAccept (HTTP::Session& session) override; + bool + onAccept (HTTP::Session& session, + boost::asio::ip::tcp::endpoint endpoint) override; + + void + onLegacyPeerHello (std::unique_ptr&& ssl_bundle, + boost::asio::const_buffer buffer, + boost::asio::ip::tcp::endpoint remote_address) override; + + Handoff + onHandoff (HTTP::Session& session, + std::unique_ptr && bundle, + beast::http::message&& request, + boost::asio::ip::tcp::endpoint remote_address) override; + + Handoff + onHandoff (HTTP::Session& session, boost::asio::ip::tcp::socket&& socket, + beast::http::message&& request, + boost::asio::ip::tcp::endpoint remote_address) override; void onRequest (HTTP::Session& session) override; @@ -87,7 +111,7 @@ private: createResponse (int statusCode, std::string const& description); std::string - processRequest (std::string const& request, + processRequest (HTTP::Port const& port, std::string const& request, beast::IP::Endpoint const& remoteIPAddress); // @@ -96,15 +120,16 @@ private: void onWrite (beast::PropertyStream::Map& map) override; + +private: + bool + isWebsocketUpgrade (beast::http::message const& request); + + bool + authorized (HTTP::Port const& port, + std::map const& h); }; -//------------------------------------------------------------------------------ - -std::unique_ptr -make_RPCHTTPServer (beast::Stoppable& parent, JobQueue& jobQueue, - NetworkOPs& networkOPs, Resource::Manager& resourceManager, - RPC::Setup const& setup); - } #endif diff --git a/src/ripple/http/impl/ServerImpl.cpp b/src/ripple/server/impl/ServerImpl.cpp similarity index 86% rename from src/ripple/http/impl/ServerImpl.cpp rename to src/ripple/server/impl/ServerImpl.cpp index c6b082d489..f124e080da 100644 --- a/src/ripple/http/impl/ServerImpl.cpp +++ b/src/ripple/server/impl/ServerImpl.cpp @@ -17,8 +17,12 @@ */ //============================================================================== -#include -#include +#if DOXYGEN +#include +#endif + +#include +#include #include #include #include @@ -32,15 +36,15 @@ namespace ripple { namespace HTTP { -ServerImpl::ServerImpl (Handler& handler, beast::Journal journal) +ServerImpl::ServerImpl (Handler& handler, + boost::asio::io_service& io_service, beast::Journal journal) : handler_ (handler) , journal_ (journal) + , io_service_ (io_service) , strand_ (io_service_) - , work_ (boost::in_place (std::ref (io_service_))) + , work_ (boost::in_place (std::ref(io_service))) , hist_{} { - thread_ = std::thread (std::bind ( - &ServerImpl::run, this)); } ServerImpl::~ServerImpl() @@ -52,7 +56,6 @@ ServerImpl::~ServerImpl() while (! list_.empty()) cond_.wait(lock); } - thread_.join(); } void @@ -61,8 +64,9 @@ ServerImpl::ports (std::vector const& ports) if (closed()) throw std::logic_error("ports() on closed HTTP::Server"); for(auto const& _ : ports) - std::make_shared( - io_service_, *this, _)->run(); + if (! _.websockets()) + std::make_shared( + io_service_, *this, _)->run(); } void @@ -198,13 +202,13 @@ ServerImpl::ceil_log2 (unsigned long long x) return y; } -void -ServerImpl::run() +//-------------------------------------------------------------------------- + +std::unique_ptr +make_Server (Handler& handler, + boost::asio::io_service& io_service, beast::Journal journal) { - static std::atomic id; - beast::Thread::setCurrentThreadName ( - std::string("HTTP::Server #") + std::to_string (++id)); - io_service_.run(); + return std::make_unique(handler, io_service, journal); } } diff --git a/src/ripple/http/impl/ServerImpl.h b/src/ripple/server/impl/ServerImpl.h similarity index 90% rename from src/ripple/http/impl/ServerImpl.h rename to src/ripple/server/impl/ServerImpl.h index 43dd31a346..88ee207e86 100644 --- a/src/ripple/http/impl/ServerImpl.h +++ b/src/ripple/server/impl/ServerImpl.h @@ -17,11 +17,12 @@ */ //============================================================================== -#ifndef RIPPLE_HTTP_SERVERIMPL_H_INCLUDED -#define RIPPLE_HTTP_SERVERIMPL_H_INCLUDED +#ifndef RIPPLE_SERVER_SERVERIMPL_H_INCLUDED +#define RIPPLE_SERVER_SERVERIMPL_H_INCLUDED #include -#include +#include +#include #include #include #include @@ -77,22 +78,24 @@ private: typedef std::vector > Doors; Handler& handler_; - std::thread thread_; + beast::Journal journal_; + boost::asio::io_service& io_service_; + boost::asio::io_service::strand strand_; + boost::optional work_; + std::mutex mutable mutex_; std::condition_variable cond_; list_type list_; - beast::Journal journal_; - boost::asio::io_service io_service_; - boost::asio::io_service::strand strand_; - boost::optional work_; std::deque stats_; - std::array hist_; int high_ = 0; -public: - ServerImpl (Handler& handler, beast::Journal journal); + std::array hist_; - ~ServerImpl (); +public: + ServerImpl (Handler& handler, + boost::asio::io_service& io_service, beast::Journal journal); + + ~ServerImpl(); beast::Journal journal() override @@ -138,9 +141,6 @@ private: static int ceil_log2 (unsigned long long x); - - void - run(); }; diff --git a/src/ripple/net/RPCDoor.h b/src/ripple/server/make_Server.h similarity index 71% rename from src/ripple/net/RPCDoor.h rename to src/ripple/server/make_Server.h index d18ccf3193..30e72dd115 100644 --- a/src/ripple/net/RPCDoor.h +++ b/src/ripple/server/make_Server.h @@ -17,23 +17,23 @@ */ //============================================================================== -#ifndef RIPPLE_NET_BASICS_RPCDOOR_H_INCLUDED -#define RIPPLE_NET_BASICS_RPCDOOR_H_INCLUDED +#ifndef RIPPLE_SERVER_MAKE_SERVER_H_INCLUDED +#define RIPPLE_SERVER_MAKE_SERVER_H_INCLUDED -#include +#include +#include +#include +#include namespace ripple { +namespace HTTP { -/** Listening socket for RPC requests. -*/ -class RPCDoor -{ -public: - static RPCDoor* New (boost::asio::io_service& io_service, RPCServer::Handler& handler); - - virtual ~RPCDoor () { } -}; +/** Create the HTTP server using the specified handler. */ +std::unique_ptr +make_Server (Handler& handler, + boost::asio::io_service& io_service, beast::Journal journal); +} // HTTP } // ripple #endif diff --git a/src/ripple/overlay/impl/PeerDoor.h b/src/ripple/server/make_ServerHandler.h similarity index 65% rename from src/ripple/overlay/impl/PeerDoor.h rename to src/ripple/server/make_ServerHandler.h index 7afd635a28..d1d33e28cb 100644 --- a/src/ripple/overlay/impl/PeerDoor.h +++ b/src/ripple/server/make_ServerHandler.h @@ -17,30 +17,25 @@ */ //============================================================================== -#ifndef RIPPLE_PEERDOOR_H_INCLUDED -#define RIPPLE_PEERDOOR_H_INCLUDED +#ifndef RIPPLE_SERVER_MAKE_SERVERHANDLER_H_INCLUDED +#define RIPPLE_SERVER_MAKE_SERVERHANDLER_H_INCLUDED -#include +#include +#include +#include +#include +#include +#include #include // +#include +#include namespace ripple { -/** Handles incoming connections from peers. */ -class PeerDoor -{ -public: - virtual - ~PeerDoor() = default; +std::unique_ptr +make_ServerHandler (beast::Stoppable& parent, boost::asio::io_service& io_service, + JobQueue& jobQueue, NetworkOPs& networkOPs, Resource::Manager& resourceManager); - virtual - void stop() = 0; -}; - -// VFALCO DEPRECATED This will be replaced by a universal door -std::unique_ptr -make_PeerDoor (OverlayImpl& overlay, std::string const& ip, int port, - boost::asio::io_service& io_service); - -} +} // ripple #endif diff --git a/src/ripple/http/tests/Server.test.cpp b/src/ripple/server/tests/Server.test.cpp similarity index 72% rename from src/ripple/http/tests/Server.test.cpp rename to src/ripple/server/tests/Server.test.cpp index 447c92331f..c369c4ed4c 100644 --- a/src/ripple/http/tests/Server.test.cpp +++ b/src/ripple/server/tests/Server.test.cpp @@ -17,11 +17,12 @@ */ //============================================================================== -#include -#include -#include +#include +#include +#include #include #include +#include #include #include #include @@ -37,6 +38,35 @@ public: testPort = 1001 }; + class TestThread + { + private: + boost::asio::io_service io_service_; + boost::optional work_; + std::thread thread_; + + public: + TestThread() + : work_(boost::in_place(std::ref(io_service_))) + , thread_([&]() { this->io_service_.run(); }) + { + } + + ~TestThread() + { + work_ = boost::none; + thread_.join(); + } + + boost::asio::io_service& + get_io_service() + { + return io_service_; + } + }; + + //-------------------------------------------------------------------------- + class TestSink : public beast::Journal::Sink { beast::unit_test::suite& suite_; @@ -55,6 +85,8 @@ public: } }; + //-------------------------------------------------------------------------- + struct TestHandler : Handler { void @@ -62,6 +94,37 @@ public: { } + bool + onAccept (Session& session, + boost::asio::ip::tcp::endpoint endpoint) override + { + return true; + } + + void + onLegacyPeerHello (std::unique_ptr&& ssl_bundle, + boost::asio::const_buffer buffer, + boost::asio::ip::tcp::endpoint remote_address) override + { + } + + Handoff + onHandoff (Session& session, + std::unique_ptr && bundle, + beast::http::message&& request, + boost::asio::ip::tcp::endpoint remote_address) override + { + return Handoff{}; + } + + Handoff + onHandoff (Session& session, boost::asio::ip::tcp::socket&& socket, + beast::http::message&& request, + boost::asio::ip::tcp::endpoint remote_address) override + { + return Handoff{}; + } + void onRequest (Session& session) override { @@ -84,6 +147,8 @@ public: } }; + //-------------------------------------------------------------------------- + // Connect to an address template bool @@ -227,16 +292,18 @@ public: run() { TestSink sink {*this}; + TestThread thread; sink.severity (beast::Journal::Severity::kAll); beast::Journal journal {sink}; TestHandler handler; - auto s = make_Server (handler, journal); + auto s = make_Server (handler, + thread.get_io_service(), journal); std::vector list; - std::unique_ptr c ( - RippleSSLContext::createBare ()); - list.emplace_back (testPort, beast::IP::Endpoint ( - beast::IP::AddressV4 (127, 0, 0, 1), 0), - Port::Security::no_ssl, c.get()); + list.resize(1); + list.back().port = testPort; + list.back().ip = boost::asio::ip::address::from_string ( + "127.0.0.1"); + list.back().protocol.insert("http"); s->ports (list); test_request(); diff --git a/src/ripple/unity/app.cpp b/src/ripple/unity/app.cpp index 4d9e68f4c9..fa3295db09 100644 --- a/src/ripple/unity/app.cpp +++ b/src/ripple/unity/app.cpp @@ -24,12 +24,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include #include diff --git a/src/ripple/unity/app5.cpp b/src/ripple/unity/app5.cpp index de56ebd840..43aedb7cb9 100644 --- a/src/ripple/unity/app5.cpp +++ b/src/ripple/unity/app5.cpp @@ -25,7 +25,6 @@ #include #include -#include #include diff --git a/src/ripple/unity/common.cpp b/src/ripple/unity/common.cpp index 25f833b244..d60da05cdd 100644 --- a/src/ripple/unity/common.cpp +++ b/src/ripple/unity/common.cpp @@ -20,8 +20,8 @@ #include #include +#include #include -#include #include #include diff --git a/src/ripple/unity/net.cpp b/src/ripple/unity/net.cpp index 0d56e04a17..5b5a37a304 100644 --- a/src/ripple/unity/net.cpp +++ b/src/ripple/unity/net.cpp @@ -32,9 +32,7 @@ #include #include #include -#include // Is this still needed? #include #include #include -#include #include diff --git a/src/ripple/unity/net.h b/src/ripple/unity/net.h index 5149a0b60e..92265801f8 100644 --- a/src/ripple/unity/net.h +++ b/src/ripple/unity/net.h @@ -32,11 +32,9 @@ #include #include #include -#include #include #include -#include #include #include #include diff --git a/src/ripple/unity/overlay.cpp b/src/ripple/unity/overlay.cpp index 2436d9f922..6b85720485 100644 --- a/src/ripple/unity/overlay.cpp +++ b/src/ripple/unity/overlay.cpp @@ -23,7 +23,5 @@ #include #include #include -#include - #include diff --git a/src/ripple/unity/http.cpp b/src/ripple/unity/server.cpp similarity index 80% rename from src/ripple/unity/http.cpp rename to src/ripple/unity/server.cpp index ffff6ebb09..5d1d1cd969 100644 --- a/src/ripple/unity/http.cpp +++ b/src/ripple/unity/server.cpp @@ -19,8 +19,9 @@ #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include diff --git a/src/websocket/src/sockets/autotls.hpp b/src/websocket/src/sockets/autotls.hpp index 6028040974..217373ce60 100644 --- a/src/websocket/src/sockets/autotls.hpp +++ b/src/websocket/src/sockets/autotls.hpp @@ -51,14 +51,6 @@ public: static void handle_shutdown(autotls_socket_ptr, const boost::system::error_code&) { } - void set_secure_only() { - m_secure_only = true; - } - - void set_plain_only() { - m_plain_only = true; - } - // should be private friended? autotls_socket::handshake_type get_handshake_type() { if (static_cast< endpoint_type* >(this)->is_server()) { @@ -70,10 +62,12 @@ public: class handler_interface { public: - virtual ~handler_interface() {} + virtual ~handler_interface() = default; virtual void on_tcp_init() {}; virtual boost::asio::ssl::context& get_ssl_context () = 0; + virtual bool plain_only() = 0; + virtual bool secure_only() = 0; }; // Connection specific details @@ -106,10 +100,10 @@ public: void init() { boost::asio::ssl::context& ssl_context ( m_connection.get_handler()->get_ssl_context()); - + m_socket_ptr = autotls_socket_ptr (new autotls_socket ( - m_endpoint.get_io_service(), ssl_context, m_endpoint.m_secure_only, - m_endpoint.m_plain_only)); + m_endpoint.get_io_service(), ssl_context, m_connection.get_handler()->secure_only(), + m_connection.get_handler()->plain_only())); } void async_init(boost::function callback) @@ -164,11 +158,12 @@ public: connection_type& m_connection; }; protected: - autotls (boost::asio::io_service& m) : m_io_service(m), m_secure_only(false), m_plain_only(false) {} + autotls (boost::asio::io_service& m) : m_io_service(m) + { + } + private: boost::asio::io_service& m_io_service; - bool m_secure_only; - bool m_plain_only; }; } // namespace socket