From ac0eaa912b4a47211a55debc7f29e0dc879c1364 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Fri, 31 Oct 2014 13:32:28 -0700 Subject: [PATCH] Universal Port (RIPD-160): This changes the behavior and configuration specification of the listening ports that rippled uses to accept incoming connections for the supported protocols: peer (Peer Protocol), http (JSON-RPC over HTTP), https (JSON-RPC) over HTTPS, ws (Websockets Clients), and wss (Secure Websockets Clients). Each listening port is now capable of handshaking in multiple protocols specified in the configuration file (subject to some restrictions). Each port can be configured to provide its own SSL certificate, or to use a self-signed certificate. Ports can be configured to share settings, this allows multiple ports to use the same certificate or values. The list of ports is dynamic, administrators can open as few or as many ports as they like. Authentication settings such as user/password or admin user/admin password (for administrative commands on RPC or Websockets interfaces) can also be specified per-port. As the configuration file has changed significantly, administrators will need to update their ripple.cfg files and carefully review the documentation and new settings. Changes: * rippled-example.cfg updated with documentation and new example settings: All obsolete websocket, rpc, and peer configuration sections have been removed, the documentation updated, and a new documented set of example settings added. * HTTP::Writer abstraction for sending HTTP server requests and responses * HTTP::Handler handler improvements to support Universal Port * HTTP::Handler handler supports legacy Peer protocol handshakes * HTTP::Port uses shared_ptr * HTTP::PeerImp and Overlay use ssl_bundle to support Universal Port * New JsonWriter to stream message and body through HTTP server * ServerHandler refactored to support Universal Port and legacy peers * ServerHandler Setup struct updated for Universal Port * Refactor some PeerFinder members * WSDoor and Websocket code stores and uses the HTTP::Port configuration * Websocket autotls class receives the current secure/plain SSL setting * Remove PeerDoor and obsolete Overlay peer accept code * Remove obsolete RPCDoor and synchronous RPC handling code * Remove other obsolete classes, types, and files * Command line tool uses ServerHandler Setup for port and authorization info * Fix handling of admin_user, admin_password in administrative commands * Fix adminRole to check credentials for Universal Port * Updated Overlay README.md * Overlay sends IP:port redirects on HTTP Upgrade peer connection requests: Incoming peers who handshake using the HTTP Upgrade mechanism don't get a slot, and always get HTTP Status 503 redirect containing a JSON content-body with a set of alternate IP and port addresses to try, learned from PeerFinder. A future commit related to the Hub and Spoke feature will change the response to grant the peer a slot when there are peer slots available. * HTTP responses to outgoing Peer connect requests parse redirect IP:ports: When the [overlay] configuration section (which is experimental) has http_handshake = 1, HTTP redirect responses will have the JSON content-body parsed to obtain the redirect IP:port addresses. * Use a single io_service for HTTP::Server and Overlay: This is necessary to allow HTTP::Server to pass sockets to and from Overlay and eventually Websockets. Unfortunately Websockets is not so easily changed to use an externally provided io_service. This will be addressed in a future commit, and is one step necessary ease the restriction on ports configured to offer Websocket protocols in the .cfg file. --- Builds/VisualStudio2013/RippleD.vcxproj | 123 +-- .../VisualStudio2013/RippleD.vcxproj.filters | 178 +++-- SConstruct | 2 +- doc/rippled-example.cfg | 588 +++++++------- src/ripple/app/main/Application.cpp | 101 +-- src/ripple/app/main/Application.h | 17 +- src/ripple/app/main/LocalCredentials.cpp | 3 +- src/ripple/app/main/Main.cpp | 12 +- src/ripple/app/main/ServerHandlerImp.cpp | 273 ------- src/ripple/app/shamap/SHAMapTreeNode.h | 3 +- .../Port.cpp => app/shamap/TreeNodeCache.h} | 22 +- src/ripple/app/websocket/WSConnection.cpp | 17 +- src/ripple/app/websocket/WSConnection.h | 25 +- src/ripple/app/websocket/WSDoor.cpp | 63 +- src/ripple/app/websocket/WSDoor.h | 13 +- src/ripple/app/websocket/WSServerHandler.h | 44 +- src/ripple/common/RippleSSLContext.h | 133 ---- ...ppleSSLContext.cpp => make_SSLContext.cpp} | 357 +++------ src/ripple/common/make_SSLContext.h | 49 ++ src/ripple/core/Config.h | 116 +-- src/ripple/core/ConfigSections.h | 29 +- src/ripple/core/impl/Config.cpp | 215 ----- src/ripple/http/README.md | 6 - src/ripple/http/Server.h | 146 ---- src/ripple/http/TODO.md | 1 - src/ripple/http/impl/SSLPeer.h | 126 --- src/ripple/http/impl/Server.cpp | 74 -- src/ripple/http/impl/Types.h | 71 -- src/ripple/net/impl/RPCCall.cpp | 108 ++- src/ripple/net/impl/RPCDoor.cpp | 144 ---- src/ripple/overlay/Overlay.h | 19 + src/ripple/overlay/README.md | 36 +- src/ripple/overlay/impl/OverlayImpl.cpp | 246 +++--- src/ripple/overlay/impl/OverlayImpl.h | 63 +- src/ripple/overlay/impl/PeerDoor.cpp | 145 ---- src/ripple/overlay/impl/PeerImp.cpp | 135 +++- src/ripple/overlay/impl/PeerImp.h | 67 +- src/ripple/overlay/make_Overlay.h | 7 +- src/ripple/peerfinder/Manager.h | 2 +- src/ripple/peerfinder/impl/Logic.h | 2 +- src/ripple/peerfinder/impl/Manager.cpp | 4 +- src/ripple/rpc/RPCHandler.h | 8 +- src/ripple/rpc/handlers/AccountTx.cpp | 4 +- src/ripple/rpc/handlers/AccountTxOld.cpp | 4 +- src/ripple/rpc/handlers/Connect.cpp | 2 +- src/ripple/rpc/handlers/Ledger.cpp | 4 +- src/ripple/rpc/handlers/LedgerData.cpp | 2 +- src/ripple/rpc/handlers/RipplePathFind.cpp | 4 +- src/ripple/rpc/handlers/ServerInfo.cpp | 2 +- src/ripple/rpc/handlers/ServerState.cpp | 2 +- src/ripple/rpc/handlers/Submit.cpp | 2 +- src/ripple/rpc/handlers/Subscribe.cpp | 4 +- src/ripple/rpc/handlers/TxHistory.cpp | 2 +- src/ripple/rpc/handlers/Unsubscribe.cpp | 2 +- src/ripple/rpc/handlers/ValidationCreate.cpp | 2 +- src/ripple/rpc/impl/Context.h | 3 +- src/ripple/rpc/impl/Handler.cpp | 116 +-- src/ripple/rpc/impl/Handler.h | 2 +- src/ripple/rpc/impl/RPCHandler.cpp | 8 +- src/ripple/rpc/impl/TransactionSign.cpp | 16 +- src/ripple/rpc/impl/TransactionSign.h | 4 +- src/ripple/server/Handler.h | 109 +++ src/ripple/{unity/http.h => server/Handoff.h} | 36 +- src/ripple/server/JsonWriter.h | 165 ++++ src/ripple/server/Port.h | 86 ++ src/ripple/server/README.md | 3 + src/ripple/server/Role.h | 53 ++ src/ripple/server/Server.h | 73 ++ .../{app/main => server}/ServerHandler.h | 54 +- src/ripple/{http => server}/Session.h | 19 +- src/ripple/server/Writer.h | 64 ++ src/ripple/{http => server}/impl/Door.cpp | 116 ++- src/ripple/{http => server}/impl/Door.h | 20 +- .../impl/JSONRPCUtil.cpp} | 117 +-- .../RPCUtil.h => server/impl/JSONRPCUtil.h} | 20 +- src/ripple/{http => server}/impl/Peer.h | 113 ++- src/ripple/{http => server}/impl/PlainPeer.h | 51 +- src/ripple/server/impl/Role.cpp | 93 +++ src/ripple/server/impl/SSLPeer.h | 203 +++++ src/ripple/server/impl/ServerHandlerImp.cpp | 739 ++++++++++++++++++ .../main => server/impl}/ServerHandlerImp.h | 63 +- .../{http => server}/impl/ServerImpl.cpp | 34 +- src/ripple/{http => server}/impl/ServerImpl.h | 30 +- .../{net/RPCDoor.h => server/make_Server.h} | 24 +- .../make_ServerHandler.h} | 33 +- .../{http => server}/tests/Server.test.cpp | 85 +- src/ripple/unity/app.cpp | 2 - src/ripple/unity/app5.cpp | 1 - src/ripple/unity/common.cpp | 2 +- src/ripple/unity/net.cpp | 2 - src/ripple/unity/net.h | 2 - src/ripple/unity/overlay.cpp | 2 - src/ripple/unity/{http.cpp => server.cpp} | 11 +- src/websocket/src/sockets/autotls.hpp | 25 +- 94 files changed, 3376 insertions(+), 3052 deletions(-) delete mode 100644 src/ripple/app/main/ServerHandlerImp.cpp rename src/ripple/{http/impl/Port.cpp => app/shamap/TreeNodeCache.h} (81%) delete mode 100644 src/ripple/common/RippleSSLContext.h rename src/ripple/common/impl/{RippleSSLContext.cpp => make_SSLContext.cpp} (66%) create mode 100644 src/ripple/common/make_SSLContext.h delete mode 100644 src/ripple/http/README.md delete mode 100644 src/ripple/http/Server.h delete mode 100644 src/ripple/http/TODO.md delete mode 100644 src/ripple/http/impl/SSLPeer.h delete mode 100644 src/ripple/http/impl/Server.cpp delete mode 100644 src/ripple/http/impl/Types.h delete mode 100644 src/ripple/net/impl/RPCDoor.cpp delete mode 100644 src/ripple/overlay/impl/PeerDoor.cpp create mode 100644 src/ripple/server/Handler.h rename src/ripple/{unity/http.h => server/Handoff.h} (61%) create mode 100644 src/ripple/server/JsonWriter.h create mode 100644 src/ripple/server/Port.h create mode 100644 src/ripple/server/README.md create mode 100644 src/ripple/server/Role.h create mode 100644 src/ripple/server/Server.h rename src/ripple/{app/main => server}/ServerHandler.h (56%) rename src/ripple/{http => server}/Session.h (91%) create mode 100644 src/ripple/server/Writer.h rename src/ripple/{http => server}/impl/Door.cpp (75%) rename src/ripple/{http => server}/impl/Door.h (90%) rename src/ripple/{net/impl/RPCUtil.cpp => server/impl/JSONRPCUtil.cpp} (64%) rename src/ripple/{net/RPCUtil.h => server/impl/JSONRPCUtil.h} (66%) rename src/ripple/{http => server}/impl/Peer.h (80%) rename src/ripple/{http => server}/impl/PlainPeer.h (67%) create mode 100644 src/ripple/server/impl/Role.cpp create mode 100644 src/ripple/server/impl/SSLPeer.h create mode 100644 src/ripple/server/impl/ServerHandlerImp.cpp rename src/ripple/{app/main => server/impl}/ServerHandlerImp.h (60%) rename src/ripple/{http => server}/impl/ServerImpl.cpp (86%) rename src/ripple/{http => server}/impl/ServerImpl.h (90%) rename src/ripple/{net/RPCDoor.h => server/make_Server.h} (71%) rename src/ripple/{overlay/impl/PeerDoor.h => server/make_ServerHandler.h} (65%) rename src/ripple/{http => server}/tests/Server.test.cpp (72%) rename src/ripple/unity/{http.cpp => server.cpp} (80%) 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