diff --git a/Builds/CMake/RippledCore.cmake b/Builds/CMake/RippledCore.cmake index a6d97ddf8..c8f0b0c58 100644 --- a/Builds/CMake/RippledCore.cmake +++ b/Builds/CMake/RippledCore.cmake @@ -176,7 +176,6 @@ install ( install ( FILES src/ripple/crypto/GenerateDeterministicKey.h - src/ripple/crypto/KeyType.h src/ripple/crypto/RFC1751.h src/ripple/crypto/csprng.h DESTINATION include/ripple/crypto) @@ -214,6 +213,7 @@ install ( src/ripple/protocol/Indexes.h src/ripple/protocol/InnerObjectFormats.h src/ripple/protocol/Issue.h + src/ripple/protocol/KeyType.h src/ripple/protocol/Keylet.h src/ripple/protocol/KnownFormats.h src/ripple/protocol/LedgerFormats.h @@ -602,12 +602,13 @@ else () #]===============================] src/ripple/overlay/impl/Cluster.cpp src/ripple/overlay/impl/ConnectAttempt.cpp + src/ripple/overlay/impl/Handshake.cpp src/ripple/overlay/impl/Message.cpp src/ripple/overlay/impl/OverlayImpl.cpp src/ripple/overlay/impl/PeerImp.cpp src/ripple/overlay/impl/PeerReservationTable.cpp src/ripple/overlay/impl/PeerSet.cpp - src/ripple/overlay/impl/TMHello.cpp + src/ripple/overlay/impl/ProtocolVersion.cpp src/ripple/overlay/impl/TrafficCount.cpp #[===============================[ nounity, main sources: @@ -920,7 +921,7 @@ else () nounity, test sources: subdir: overlay #]===============================] - src/test/overlay/TMHello_test.cpp + src/test/overlay/ProtocolVersion_test.cpp src/test/overlay/cluster_test.cpp src/test/overlay/short_read_test.cpp #[===============================[ @@ -933,7 +934,6 @@ else () nounity, test sources: subdir: protocol #]===============================] - src/test/protocol/BuildInfo_test.cpp src/test/protocol/IOUAmount_test.cpp src/test/protocol/InnerObjectFormats_test.cpp src/test/protocol/Issue_test.cpp diff --git a/cfg/rippled-example.cfg b/cfg/rippled-example.cfg index 3450d9ef6..c43f0e2d1 100644 --- a/cfg/rippled-example.cfg +++ b/cfg/rippled-example.cfg @@ -733,6 +733,24 @@ # node is a validator. # # +# +# [network_id] +# +# Specify the network which this server is configured to connect to and +# track. If set, the server will not establish connections with servers +# that are explicitly configured to track another network. +# +# Network identifiers are usually unsigned integers in the range 0 to +# 4294967295 inclusive. The server also maps the following well-known +# names to the corresponding numerical identifier: +# +# main -> 0 +# testnet -> 1 +# +# If this value is not specified the server is not explicitly configured +# to track a particular network. +# +# #------------------------------------------------------------------------------- # # 4. HTTPS Client diff --git a/src/ripple/overlay/Message.h b/src/ripple/overlay/Message.h index 498f70ca8..e186272b3 100644 --- a/src/ripple/overlay/Message.h +++ b/src/ripple/overlay/Message.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -47,26 +48,9 @@ namespace ripple { class Message : public std::enable_shared_from_this { public: - using pointer = std::shared_ptr; - -public: - /** Number of bytes in a message header. - - A message will not be processed unless a full header is received and - no messages smaller than this will be serialized. - */ - static std::size_t constexpr kHeaderBytes = 6; - - /** The largest size that a message can be. - - Sending a message whose size exceeds this may result in the connection - being dropped. A larger message size may be supported in the future or - negotiated as part of a protocol upgrade. - */ - static std::size_t constexpr kMaxMessageSize = 64 * 1024 * 1024; - Message (::google::protobuf::Message const& message, int type); +public: /** Retrieve the packed message data. */ std::vector const& getBuffer () const @@ -81,92 +65,8 @@ public: return mCategory; } - /** Determine bytewise equality. */ - bool operator == (Message const& other) const; - - /** Calculate the length of a packed message. */ - /** @{ */ - static unsigned getLength (std::vector const& buf); - - template - static - std::enable_if_t::value, std::size_t> - size (FwdIter first, FwdIter last) - { - if (std::distance(first, last) < - Message::kHeaderBytes) - return 0; - std::size_t n; - n = std::size_t{*first++} << 24; - n += std::size_t{*first++} << 16; - n += std::size_t{*first++} << 8; - n += std::size_t{*first}; - return n; - } - - template - static - std::size_t - size (BufferSequence const& buffers) - { - return size(buffers_begin(buffers), - buffers_end(buffers)); - } - /** @} */ - - /** Determine the type of a packed message. */ - /** @{ */ - static int getType (std::vector const& buf); - - template - static - std::enable_if_t::value, int> - type (FwdIter first, FwdIter last) - { - if (std::distance(first, last) < - Message::kHeaderBytes) - return 0; - return (int{*std::next(first, 4)} << 8) | - *std::next(first, 5); - } - - template - static - int - type (BufferSequence const& buffers) - { - return type(buffers_begin(buffers), - buffers_end(buffers)); - } - /** @} */ - private: - template - static - boost::asio::buffers_iterator - buffers_begin (BufferSequence const& buffers) - { - return boost::asio::buffers_iterator< - BufferSequence, Value>::begin (buffers); - } - - template - static - boost::asio::buffers_iterator - buffers_end (BufferSequence const& buffers) - { - return boost::asio::buffers_iterator< - BufferSequence, Value>::end (buffers); - } - - // Encodes the size and type into a header at the beginning of buf - // - void encodeHeader (unsigned size, int type); - std::vector mBuffer; - std::size_t mCategory; }; diff --git a/src/ripple/overlay/Overlay.h b/src/ripple/overlay/Overlay.h index af7821363..9d755110d 100644 --- a/src/ripple/overlay/Overlay.h +++ b/src/ripple/overlay/Overlay.h @@ -32,6 +32,7 @@ #include #include #include +#include #include namespace boost { namespace asio { namespace ssl { class context; } } } @@ -71,6 +72,7 @@ public: beast::IP::Address public_ip; int ipLimit = 0; std::uint32_t crawlOptions = 0; + boost::optional networkID; }; using PeerSequence = std::vector >; diff --git a/src/ripple/overlay/Peer.h b/src/ripple/overlay/Peer.h index b8864c431..583cb04f3 100644 --- a/src/ripple/overlay/Peer.h +++ b/src/ripple/overlay/Peer.h @@ -56,7 +56,7 @@ public: virtual void - send (Message::pointer const& m) = 0; + send (std::shared_ptr const& m) = 0; virtual beast::IP::Endpoint @@ -105,7 +105,6 @@ public: virtual bool hasShard (std::uint32_t shardIndex) const = 0; virtual bool hasTxSet (uint256 const& hash) const = 0; virtual void cycleStatus () = 0; - virtual bool supportsVersion (int version) = 0; virtual bool hasRange (std::uint32_t uMin, std::uint32_t uMax) = 0; }; diff --git a/src/ripple/overlay/README.md b/src/ripple/overlay/README.md index b929d2ee8..ca22f1e8a 100644 --- a/src/ripple/overlay/README.md +++ b/src/ripple/overlay/README.md @@ -2,17 +2,18 @@ ## Introduction -The _Ripple payment network_ consists of a collection of _peers_ running -**rippled**. Each peer maintains multiple outgoing connections and optional -incoming connections to other peers. These connections are made over both -the public Internet and private local area networks. This network defines a -fully connected directed graph of nodes where vertices are instances of rippled -and edges are persistent TCP/IP connections. Peers send and receive messages to -other connected peers. This peer to peer network, layered on top of the public -and private Internet, forms an [_overlay network_][overlay_network]. The -contents of the messages and the behavior of peers in response to the messages, -plus the information exchanged during the handshaking phase of connection -establishment, defines the _Ripple peer protocol_ (_protocol_ in this context). +The _XRP Ledger network_ consists of a collection of _peers_ running +**`rippled`** or other compatible software. Each peer maintains multiple +outgoing connections and optional incoming connections to other peers. +These connections are made over both the public Internet and private local +area networks. This network defines a connected directed graph of nodes +where vertices are instances of `rippled` and edges are persistent TCP/IP +connections. Peers send and receive messages to other connected peers. This +peer to peer network, layered on top of the public and private Internet, +forms an [_overlay network_][overlay_network]. The contents of the messages +and the behavior of peers in response to the messages, plus the information +exchanged during the handshaking phase of connection establishment, defines +the _XRP Ledger peer protocol_ (or _protocol_ in this context). ## Overview @@ -24,71 +25,69 @@ messages are exchanged between peers and serialized using ### Structure Each connection between peers is identified by its connection type, which -affects the behavior of message routing: - -* Leaf - -* Peer - -## Roles - -Depending on the type of connection desired, the peers will modify their -behavior according to certain roles: - -### Leaf or Superpeer - -A peer in the leaf role does not route messages. In the superpeer role, a -peer accepts incoming connections from other leaves and superpeers up to the -configured slot limit. It also routes messages. For a particular connection, -the choice of leaf or superpeer is mutually exclusive. However, a peer can -operate in both the leaf and superpeer role for different connections. One of -the requirements - -### Client Handler - -While not part of the responsibilities of the Overlay module, a peer -operating in the Client Handler role accepts incoming connections from clients -and services them through the JSON-RPC interface. A peer can operate in either -the leaf or superpeer roles while also operating as a client handler. +affects the behavior of message routing. At present, only a single connection +type is supported: **Peer**. ## Handshake To establish a protocol connection, a peer makes an outgoing TLS encrypted -connection to a remote peer, then sends a HTTP request with no message body. -The request uses the [_HTTP/1.1 Upgrade_][upgrade_header] mechanism with some -custom fields to communicate protocol specific information: +connection to a remote peer, then sends an HTTP request with no message body. + +### HTTP + +The HTTP [request](https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html) must: + +- Use HTTP version 1.1. +- Specify a request URI consisting of a single forward slash character ("/") +indicating the server root. Requests using different URIs are reserved for +future protocol implementations. +- Use the [_HTTP/1.1 Upgrade_][upgrade_header] mechanism with additional custom +fields to communicate protocol specific information related to the upgrade. + +HTTP requests which do not conform to this requirements must generate an +appropriate HTTP error and result in the connection being closed. + +Upon receipt of a well-formed HTTP upgrade request, and validation of the +protocol specific parameters, a peer will either send back a HTTP 101 response +and switch to the requested protocol, or a message indicating that the request +failed (e.g. by sending HTTP 400 "Bad Request" or HTTP 503 "Service Unavailable"). + +##### Example HTTP Upgrade Request ``` GET / HTTP/1.1 -User-Agent: rippled-0.27.0 -Local-Address: 192.168.0.101:8421 -Upgrade: RTXP/1.2, RTXP/1.3 +User-Agent: rippled-1.4.0-b1+DEBUG +Upgrade: RTXP/1.2, XRPL/2.0 Connection: Upgrade -Connect-As: Leaf, Peer -Accept-Encoding: identity, zlib, snappy -Public-Key: aBRoQibi2jpDofohooFuzZi9nEzKw9Zdfc4ExVNmuXHaJpSPh8uJ -Session-Signature: 71ED064155FFADFA38782C5E0158CB26 +Connect-As: Peer +Crawl: public +Network-ID: 1 +Network-Time: 619234489 +Public-Key: n94MvLTiHQJjByfGZzvQewTxQP2qjF6shQcuHwCjh5WoiozBrdpX +Session-Signature: MEUCIQCOO8tHOh/tgCSRNe6WwOwmIF6urZ5uSB8l9aAf5q7iRAIgA4aONKBZhpP5RuOuhJP2dP+2UIRioEJcfU4/m4gZdYo= +Remote-IP: 192.0.2.79 +Closed-Ledger: llRZSKqvNieGpPqbFGnm358pmF1aW96SDIUQcnMh6HI= +Previous-Ledger: q4aKbP7sd5wv+EXArwCmQiWZhq9AwBl2p/hCtpGJNsc= ``` -Upon receipt of a well-formed HTTP request the remote peer will send back a -HTTP response indicating the connection status: +##### Example HTTP Upgrade Response (Success) + ``` HTTP/1.1 101 Switching Protocols -Server: rippled-0.27.0 -Remote-Address: 63.104.209.13 -Upgrade: RTXP/1.2 Connection: Upgrade -Connect-As: Leaf -Transfer-Encoding: snappy -Public-Key: aBRoQibi2jpDofohooFuzZi9nEzKw9Zdfc4ExVNmuXHaJpSPh8uJ -Session-Signature: 71ED064155FFADFA38782C5E0158CB26 +Upgrade: RTXP/1.2 +Connect-As: Peer +Server: rippled-1.3.1 +Crawl: public +Public-Key: n9K1ZXXXzzA3dtgKBuQUnZXkhygMRgZbSo3diFNPVHLMsUG5osJM +Session-Signature: MEQCIHMlLGTcGyPvHji7WY2nRM2B0iSBnw9xeDUGW7bPq7IjAiAmy+ofEu+8nOq2eChRTr3wjoKi3EYRqLgzP+q+ORFcig== +Network-Time: 619234797 +Closed-Ledger: h7HL85W9ywkex+G7p42USVeV5kE04CWK+4DVI19Of8I= +Previous-Ledger: EPvIpAD2iavGFyyZYi8REexAXyKGXsi1jMF7OIBY6/Y= ``` -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: +##### Example HTTP Upgrade Response (Failure: no slots available) ``` HTTP/1.1 503 Service Unavailable @@ -101,87 +100,252 @@ Content-Type: application/json "85.127.34.221:51235","50.43.33.236:51235","54.187.138.75:51235"]} ``` -### Fields +#### Standard Fields -* *URL* +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `User-Agent` | :heavy_check_mark: | | - The URL in the request line must be a single forward slash character - ("/"). Requests with any other URL must be rejected. Different URL strings - are reserved for future protocol implementations. +The `User-Agent` field indicates the version of the software that the +peer that is making the HTTP request is using. No semantic meaning is +assigned to the value in this field but it is recommended that implementations +specify the version of the software that is used. -* *HTTP Version* +See [RFC2616 §14.43](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43). - The minimum required HTTP version is 1.1. Requests for HTTP versions - earlier than 1.1 must be rejected. +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Server` | | :heavy_check_mark: | -* `User-Agent` +The `Server` field indicates the version of the software that the +peer that is processing the HTTP request is using. No semantic meaning is +assigned to the value in this field but it is recommended that implementations +specify the version of the software that is used. - Contains information about the software originating the request. - The specification is identical to RFC2616 Section 14.43. +See [RFC2616 §14.38](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.38). -* `Server` +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Connection` | :heavy_check_mark: | :heavy_check_mark: | - Contains information about the software providing the response. The - specification is identical to RFC2616 Section 14.38. +The `Connection` field should have a value of `Upgrade` to indicate that a +request to upgrade the connection is being performed. -* `Remote-Address` (optional) +See [RFC2616 §14.10](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.10). - 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. +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Upgrade` | :heavy_check_mark: | :heavy_check_mark: | -* `Upgrade` +The `Upgrade` field is part of the standard connection upgrade mechanism and +must be present in both requests and responses. It is used to negotiate the +version of the protocol that will be used after the upgrade request completes. - This field must be present and for requests consist of a comma delimited - list of at least one element where each element is of the form "RTXP/" - followed by the dotted major and minor protocol version number. For - responses the value must be a single element matching one of the elements - provided in the corresponding request field. If the server does not - understand any of the requested protocols, the request is rejected. +For requests, it should consist of a comma delimited list of at least one +element, where each element specifies a protocol version that the requesting +server is willing to use. -* `Connection` +For responses, it should a consist of _single element_ matching one of the +elements provided in the corresponding request. If the server does not understand +any of the available protocol versions, the upgrade request should fail with an +appropriate HTTP error code (e.g. by sending an HTTP 400 "Bad Request" response). - This field must be present, containing the value 'Upgrade'. +Protocol versions are string of the form `XRPL/` followed by a dotted major +and minor protocol version number, where the major number is greater than or +equal to 2 and the minor is greater than or equal to 0. The legacy version +`RTXP/1.2` is also supported at this time and is an alias for `XRPL/2.0`. -* `Connect-As` +See [RFC 2616 §14.42](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.42) - For requests the value consists of a comma delimited list of elements - where each element describes a possible connection type. Current connection - types are: - - leaf - - peer +#### Custom Fields - If this field is omitted or the value is the empty string, then 'leaf' is - assumed. +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Connect-As` | :heavy_check_mark: | :heavy_check_mark: | - For responses, the value must consist of exactly one element from the list - of elements specified in the request. If a server does not recognize any - of the connection types it must return a HTTP error response. +The mandatory `Connect-As` field is used to specify that type of connection +that is being requested. -* `Public-Key` +For requests the value consists of a comma delimited list of elements, where +each element describes a possible connection type. Only one connection types +is supported at present: **`peer`**. - This field value must be present, and contain a base 64 encoded value used - as a server public key identifier. +For responses, the value must consist of exactly one element from the list of +elements specified in the request. If a server processing a request does not +recognize any of the connection types, the request should fail with an +appropriate HTTP error code (e.g. by sending an HTTP 400 "Bad Request" response). -* `Session-Signature` - This field must be present. It contains a cryptographic token formed - from the SHA512 hash of the shared data exchanged during SSL handshaking. - For more details see the corresponding source code. +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Remote-IP` | :white_check_mark: | :white_check_mark: | -* `Crawl` (optional) +The optional `Remote-IP` field contains the string representation of the IP +address of the remote end of the connection as seen from the peer that is +sending the field. - If present, and the value is "public" then neighbors will report the IP - address to crawler requests. If absent, neighbor's default behavior is to - not report IP addresses. +By observing values of this field from a sufficient number of different +servers, a peer making outgoing connections can deduce its own IP address. -* _User Defined_ (Unimplemented) - The rippled operator may specify additional, optional fields and values - through the configuration. These headers will be transmitted in the - corresponding request or response messages. +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Local-IP` | :white_check_mark: | :white_check_mark: | + +The optional `Local-IP` field contains the string representation of the IP +address that the peer sending the field believes to be its own. + +Servers receiving this field can detect IP address mismatches, which may +indicate a potential man-in-the-middle attack. + + +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Network-ID` | :white_check_mark: | :white_check_mark: | + +The optional `Network-ID` can be used to identify to which of several +[parallel networks](https://xrpl.org/parallel-networks.html) the server +sending the field is joined. + +The value, if the field is present, is a 32-bit unsigned integer. The +following well-known values are in use: + +- **0**: The "main net" +- **1**: The Ripple-operated [Test Net](https://xrpl.org/xrp-test-net-faucet.html). + +If a server configured to join one network receives a connection request from a +server configured to join another network, the request should fail with an +appropriate HTTP error code (e.g. by sending an HTTP 400 "Bad Request" response). + + +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Network-Time` | :white_check_mark: | :white_check_mark: | + +The optional `Network-Time` field reports the current [time](https://xrpl.org/basic-data-types.html#specifying-time) +according to sender's internal clock. + +Servers should fail a connection if their clocks are not within 20 seconds of +each other with an appropriate HTTP error code (e.g. by sending an HTTP 400 +"Bad Request" response). + +It is highly recommended that servers synchronize their clocks using time +synchronization software. For more on this topic, please visit [ntp.org](http://www.ntp.org/). + + +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Public-Key` | :heavy_check_mark: | :heavy_check_mark: | + +The mandatory `Public-Key` field identifies the sending server's public key, +encoded in base58 using the standard encoding for node public keys. + +See: https://xrpl.org/base58-encodings.html + +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Session-Signature` | :heavy_check_mark: | :heavy_check_mark: | + +The `Session-Signature` field is mandatory and is used to secure the peer link +against certain types of attack. For more details see "Session Signature" below. + +The value is presently encoded using **Base64** encoding, but implementations +should support both **Base64** and **HEX** encoding for this value. + +For more details on this field, please see **Session Signature** below. + +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Crawl` | :white_check_mark: | :white_check_mark: | + +The optional `Crawl` field can be used by a server to indicate whether peers +should include it in crawl reports. + +The field can take two values: +- **`Public`**: The server's IP address and port should be included in crawl +reports. +- **`Private`**: The server's IP address and port should not be included in +crawl reports. _This is the default, if the field is omitted._ + +For more on the Peer Crawler, please visit https://xrpl.org/peer-crawler.html. + + +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Closed-Ledger` | :white_check_mark: | :white_check_mark: | + +If present, identifies the hash of the last ledger that the sending server +considers to be closed. + +The value is presently encoded using **Base64** encoding, but implementations +should support both **Base64** and **HEX** encoding for this value. + +| Field Name | Request | Response | +|--------------------- |:-----------------: |:-----------------: | +| `Previous-Ledger` | :white_check_mark: | :white_check_mark: | + +If present, identifies the hash of the parent ledger that the sending server +considers to be closed. + +The value is presently encoded using **Base64** encoding, but implementations +should support both **Base64** and **HEX** encoding for this value. + +#### Additional Headers + +An implementation or operator may specify additional, optional fields +and values in both requests and responses. + +Implementations should not reject requests because of the presence of fields +that they do not understand. + + +### Session Signature + +Even for SSL/TLS encrypted connections, it is possible for an attacker to mount +relatively inexpensive MITM attacks that can be extremely hard to detect and +may afford the attacker the ability to intelligently tamper with messages +exchanged between the two endpoints. + +This risk can be mitigated if at least one side has a certificate from a certificate +authority trusted by the other endpoint, but having a certificate is not always +possible (or even desirable) in a decentralized and permissionless network. + +Ultimately, the goal is to ensure that two endpoints A and B know that they are +talking directly to each other over a single end-to-end SSL/TLS session instead +of two separate SSL/TLS sessions, with an attacker acting as a proxy. + +The XRP Ledger protocol prevents this attack by leveraging the fact that the two +servers each have a node identity, in the form of **`secp256k1`** keypairs, and +use that to strongly bind the SSL/TLS session to the node identities of each of +the two servers at the end of the SSL/TLS session. + +To do this we "reach into" the SSL/TLS session, and extract the **`finished`** +messages for the local and remote endpoints, and combine them to generate a unique +"fingerprint". By design, this fingerprint should be the same for both SSL/TLS +endpoints. + +That fingerprint, which is never shared over the wire (since each endpoint will +calculate it independently), is then signed by each server using its public +**`secp256k1`** node identity and the signature is transferred over the SSL/TLS +encrypted link during the protocol handshake phase. + +Each side of the link will verify that the provided signature is from the claimed +public key against the session's unique fingerprint. If this signature check fails +then the link **MUST** be dropped. + +If an attacker, Eve, establishes two separate SSL sessions with Alice and Bob, the +fingerprints of the two sessions will be different, and Eve will not be able to +sign the fingerprint of her session with Bob with Alice's private key, or the +fingerprint of her session with Alice with Bob's private key, and so both A and +B will know that an active MITM attack is in progress and will close their +connections. + +If Eve simply proxies the raw bytes, she will be unable to decrypt the data being +transferred between A and B and will not be able to intelligently tamper with the +message stream between Alice and Bob, although she may be still be able to inject +delays or terminate the link. + # Ripple Clustering # diff --git a/src/ripple/overlay/impl/ConnectAttempt.cpp b/src/ripple/overlay/impl/ConnectAttempt.cpp index 157bb68d4..5bea51ff9 100644 --- a/src/ripple/overlay/impl/ConnectAttempt.cpp +++ b/src/ripple/overlay/impl/ConnectAttempt.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -27,7 +28,7 @@ namespace ripple { ConnectAttempt::ConnectAttempt (Application& app, boost::asio::io_service& io_service, endpoint_type const& remote_endpoint, Resource::Consumer usage, beast::asio::ssl_bundle::shared_context const& context, - std::uint32_t id, PeerFinder::Slot::ptr const& slot, + std::uint32_t id, std::shared_ptr const& slot, beast::Journal journal, OverlayImpl& overlay) : Child (overlay) , app_ (app) @@ -89,32 +90,21 @@ ConnectAttempt::close() error_code ec; timer_.cancel(ec); socket_.close(ec); - JLOG(journal_.debug()) << - "Closed"; + JLOG(journal_.debug()) << "Closed"; } } void ConnectAttempt::fail (std::string const& reason) { - assert(strand_.running_in_this_thread()); - if (stream_.next_layer().is_open()) - { - JLOG(journal_.debug()) << - reason; - } + JLOG(journal_.debug()) << reason; close(); } void ConnectAttempt::fail (std::string const& name, error_code ec) { - assert(strand_.running_in_this_thread()); - if (stream_.next_layer().is_open()) - { - JLOG(journal_.debug()) << - name << ": " << ec.message(); - } + JLOG(journal_.debug()) << name << ": " << ec.message(); close(); } @@ -203,19 +193,14 @@ ConnectAttempt::onHandshake (error_code ec) beast::IPAddressConversion::from_asio (local_endpoint))) return fail("Duplicate connection"); - auto sharedValue = makeSharedValue( - stream_.native_handle(), journal_); + auto const sharedValue = makeSharedValue(*ssl_bundle_, journal_); if (! sharedValue) return close(); // makeSharedValue logs - req_ = makeRequest(! overlay_.peerFinder().config().peerPrivate, - remote_endpoint_.address()); - auto const hello = buildHello ( - *sharedValue, - overlay_.setup().public_ip, - beast::IPAddressConversion::from_asio(remote_endpoint_), - app_); - appendHello (req_, hello); + req_ = makeRequest(!overlay_.peerFinder().config().peerPrivate); + + buildHandshake(req_, *sharedValue, overlay_.setup().networkID, + overlay_.setup().public_ip, remote_endpoint_.address(), app_); setTimer(); boost::beast::http::async_write(stream_, req_, @@ -279,17 +264,14 @@ ConnectAttempt::onShutdown (error_code ec) //-------------------------------------------------------------------------- auto -ConnectAttempt::makeRequest (bool crawl, - boost::asio::ip::address const& remote_address) -> - request_type +ConnectAttempt::makeRequest (bool crawl) -> request_type { request_type m; m.method(boost::beast::http::verb::get); m.target("/"); m.version(11); m.insert ("User-Agent", BuildInfo::getFullVersionString()); - m.insert ("Upgrade", "RTXP/1.2"); - //std::string("RTXP/") + to_string (BuildInfo::getCurrentProtocol())); + m.insert ("Upgrade", supportedProtocolVersions()); m.insert ("Connection", "Upgrade"); m.insert ("Connect-As", "Peer"); m.insert ("Crawl", crawl ? "public" : "private"); @@ -305,7 +287,7 @@ ConnectAttempt::processResponse() Json::Reader r; std::string s; s.reserve(boost::asio::buffer_size(response_.body().data())); - for(auto const& buffer : response_.body().data()) + for (auto const& buffer : response_.body().data()) s.append( boost::asio::buffer_cast(buffer), boost::asio::buffer_size(buffer)); @@ -329,64 +311,70 @@ ConnectAttempt::processResponse() eps.push_back(ep); } } - overlay_.peerFinder().onRedirects( - remote_endpoint_, eps); + overlay_.peerFinder().onRedirects(remote_endpoint_, eps); } } } } - if (! OverlayImpl::isPeerUpgrade(response_)) + if (!OverlayImpl::isPeerUpgrade(response_)) { - JLOG(journal_.info()) << - "HTTP Response: " << response_.result() << " " << response_.reason(); + JLOG(journal_.info()) << "Unable to upgrade to peer protocol: " << + response_.result() << " (" << response_.reason() << ")"; return close(); } - auto hello = parseHello (false, response_, journal_); - if(! hello) - return fail("processResponse: Bad TMHello"); + // Just because our peer selected a particular protocol version doesn't + // mean that it's acceptable to us. Check that it is: + boost::optional negotiatedProtocol; - auto sharedValue = makeSharedValue( - ssl_bundle_->stream.native_handle(), journal_); + { + auto const pvs = parseProtocolVersions(response_["Upgrade"]); + + if (pvs.size() == 1 && isProtocolSupported(pvs[0])) + negotiatedProtocol = pvs[0]; + + if (!negotiatedProtocol) + return fail("processResponse: Unable to negotiate protocol version"); + } + + auto const sharedValue = makeSharedValue(*ssl_bundle_, journal_); if(! sharedValue) return close(); // makeSharedValue logs - auto publicKey = verifyHello (*hello, - *sharedValue, - overlay_.setup().public_ip, - beast::IPAddressConversion::from_asio(remote_endpoint_), - journal_, app_); - if(! publicKey) - return close(); // verifyHello logs - JLOG(journal_.info()) << - "Public Key: " << toBase58 ( - TokenType::NodePublic, - *publicKey); - - auto const protocol = - BuildInfo::make_protocol(hello->protoversion()); - JLOG(journal_.info()) << - "Protocol: " << to_string(protocol); - - auto member = app_.cluster().member(*publicKey); - if (member) + try { + auto publicKey = verifyHandshake(response_, *sharedValue, + overlay_.setup().networkID, overlay_.setup().public_ip, + remote_endpoint_.address(), app_); + JLOG(journal_.info()) << - "Cluster name: " << *member; + "Public Key: " << toBase58(TokenType::NodePublic, publicKey); + + JLOG(journal_.debug()) << + "Protocol: " << to_string(*negotiatedProtocol); + + auto const member = app_.cluster().member(publicKey); + if (member) + { + JLOG(journal_.info()) << "Cluster name: " << *member; + } + + auto const result = overlay_.peerFinder().activate (slot_, + publicKey, static_cast(member)); + if (result != PeerFinder::Result::success) + return fail("Outbound slots full"); + + auto const peer = std::make_shared(app_, std::move(ssl_bundle_), + read_buf_.data(), std::move(slot_), std::move(response_), usage_, + publicKey, *negotiatedProtocol, id_, overlay_); + + overlay_.add_active (peer); + } + catch (std::exception const& e) + { + return fail(std::string("Handshake failure (")+ e.what() + ")"); } - - auto const result = overlay_.peerFinder().activate (slot_, - *publicKey, static_cast(member)); - if (result != PeerFinder::Result::success) - return fail("Outbound slots full"); - - auto const peer = std::make_shared(app_, - std::move(ssl_bundle_), read_buf_.data(), - std::move(slot_), std::move(response_), - usage_, *hello, *publicKey, id_, overlay_); - - overlay_.add_active (peer); } } // ripple diff --git a/src/ripple/overlay/impl/ConnectAttempt.h b/src/ripple/overlay/impl/ConnectAttempt.h index 3c63fcf50..c227e15c0 100644 --- a/src/ripple/overlay/impl/ConnectAttempt.h +++ b/src/ripple/overlay/impl/ConnectAttempt.h @@ -20,7 +20,6 @@ #ifndef RIPPLE_OVERLAY_CONNECTATTEMPT_H_INCLUDED #define RIPPLE_OVERLAY_CONNECTATTEMPT_H_INCLUDED -#include #include #include @@ -55,14 +54,14 @@ private: beast::asio::ssl_bundle::stream_type& stream_; boost::beast::multi_buffer read_buf_; response_type response_; - PeerFinder::Slot::ptr slot_; + std::shared_ptr slot_; request_type req_; public: ConnectAttempt (Application& app, boost::asio::io_service& io_service, endpoint_type const& remote_endpoint, Resource::Consumer usage, beast::asio::ssl_bundle::shared_context const& context, - std::uint32_t id, PeerFinder::Slot::ptr const& slot, + std::uint32_t id, std::shared_ptr const& slot, beast::Journal journal, OverlayImpl& overlay); ~ConnectAttempt(); @@ -89,8 +88,7 @@ private: static request_type - makeRequest (bool crawl, - boost::asio::ip::address const& remote_address); + makeRequest (bool crawl); void processResponse(); diff --git a/src/ripple/overlay/impl/Handshake.cpp b/src/ripple/overlay/impl/Handshake.cpp new file mode 100644 index 000000000..15ea5df47 --- /dev/null +++ b/src/ripple/overlay/impl/Handshake.cpp @@ -0,0 +1,287 @@ +//------------------------------------------------------------------------------ +/* + 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 + + +// VFALCO Shouldn't we have to include the OpenSSL +// headers or something for SSL_get_finished? + +namespace ripple { + +/** Hashes the latest finished message from an SSL stream. + + @param ssl the session to get the message from. + @param get a pointer to the function to call to retrieve the finished + message. This can be either: + - `SSL_get_finished` or + - `SSL_get_peer_finished`. + @return `true` if successful, `false` otherwise. + + @note This construct is non-standard. There are potential "standard" + alternatives that should be considered. For a discussion, on + this topic, see https://github.com/openssl/openssl/issues/5509 and + https://github.com/ripple/rippled/issues/2413. +*/ +static +boost::optional> +hashLastMessage (SSL const* ssl, + size_t (*get)(const SSL *, void *, size_t)) +{ + constexpr std::size_t sslMinimumFinishedLength = 12; + + unsigned char buf[1024]; + size_t len = get(ssl, buf, sizeof(buf)); + + if(len < sslMinimumFinishedLength) + return boost::none; + + sha512_hasher h; + + base_uint<512> cookie; + SHA512 (buf, len, cookie.data()); + return cookie; +} + +boost::optional +makeSharedValue (beast::asio::ssl_bundle& ssl, beast::Journal journal) +{ + auto const cookie1 = hashLastMessage( + ssl.stream.native_handle(), SSL_get_finished); + if (!cookie1) + { + JLOG (journal.error()) << "Cookie generation: local setup not complete"; + return boost::none; + } + + auto const cookie2 = hashLastMessage( + ssl.stream.native_handle(), SSL_get_peer_finished); + if (!cookie2) + { + JLOG (journal.error()) << "Cookie generation: peer setup not complete"; + return boost::none; + } + + auto const result = (*cookie1 ^ *cookie2); + + // Both messages hash to the same value and the cookie + // is 0. Don't allow this. + if (result == beast::zero) + { + JLOG(journal.error()) << "Cookie generation: identical finished messages"; + return boost::none; + } + + return sha512Half (Slice (result.data(), result.size())); +} + +void +buildHandshake( + boost::beast::http::fields& h, + ripple::uint256 const& sharedValue, + boost::optional networkID, + beast::IP::Address public_ip, + beast::IP::Address remote_ip, + Application& app) +{ + if (networkID) + { + // The network identifier, if configured, can be used to specify + // what network we intend to connect to and detect if the remote + // end connects to the same network. + h.insert("Network-ID", std::to_string(*networkID)); + } + + h.insert ("Network-Time", + std::to_string(app.timeKeeper().now().time_since_epoch().count())); + + h.insert ("Public-Key", + toBase58(TokenType::NodePublic, app.nodeIdentity().first)); + + { + auto const sig = signDigest(app.nodeIdentity().first, + app.nodeIdentity().second, sharedValue); + h.insert("Session-Signature", + base64_encode(sig.data(), sig.size())); + } + + if (beast::IP::is_public (remote_ip)) + h.insert ("Remote-IP", remote_ip.to_string()); + + if (!public_ip.is_unspecified()) + h.insert ("Local-IP", public_ip.to_string()); + + if (auto const cl = app.getLedgerMaster().getClosedLedger()) + { + // TODO: Use hex for these + h.insert ("Closed-Ledger", base64_encode( + cl->info().hash.begin(), cl->info().hash.size())); + h.insert ("Previous-Ledger", base64_encode( + cl->info().parentHash.begin(), cl->info().parentHash.size())); + } +} + +PublicKey +verifyHandshake( + boost::beast::http::fields const& headers, + ripple::uint256 const& sharedValue, + boost::optional networkID, + beast::IP::Address public_ip, + beast::IP::Address remote, + Application& app) +{ + if (networkID) + { + if (auto const iter = headers.find("Network-ID"); iter != headers.end()) + { + std::uint32_t nid; + + if (!beast::lexicalCastChecked(nid, iter->value().to_string())) + throw std::runtime_error("Invalid peer network identifier"); + + if(nid != *networkID) + throw std::runtime_error("Peer is on a different network"); + } + } + + if (auto const iter = headers.find("Network-Time"); iter != headers.end()) + { + auto const netTime = + [str = iter->value().to_string()]() -> TimeKeeper::time_point + { + TimeKeeper::duration::rep val; + + if (beast::lexicalCastChecked(val, str)) + return TimeKeeper::time_point{TimeKeeper::duration{val}}; + + // It's not an error for the header field to not be present but if + // it is present and it contains junk data, that is an error. + throw std::runtime_error("Invalid peer clock timestamp"); + }(); + + using namespace std::chrono; + + auto const ourTime = app.timeKeeper().now(); + auto const tolerance = 20s; + + // We can't blindly "return a-b;" because TimeKeeper::time_point + // uses an unsigned integer for representing durations, which is + // a problem when trying to subtract time points. + // FIXME: @HowardHinnant, should we migrate to using std::int64_t? + auto calculateOffset = []( + TimeKeeper::time_point a, TimeKeeper::time_point b) + { + if (a > b) + return duration_cast(a - b); + return - duration_cast(b - a); + }; + + auto const offset = calculateOffset(netTime, ourTime); + + if (date::abs(offset) > tolerance) + throw std::runtime_error("Peer clock is too far off"); + } + + PublicKey const publicKey = [&headers] + { + if (auto const iter = headers.find ("Public-Key"); iter != headers.end()) + { + auto pk = parseBase58( + TokenType::NodePublic, iter->value().to_string()); + + if(pk) + { + if (publicKeyType(*pk) != KeyType::secp256k1) + throw std::runtime_error("Unsupported public key type"); + + return *pk; + } + } + + throw std::runtime_error("Bad node public key"); + }(); + + if (publicKey == app.nodeIdentity().first) + throw std::runtime_error("Self connection"); + + // This check gets two birds with one stone: + // + // 1) it verifies that the node we are talking to has access to the + // private key corresponding to the public node identity it claims. + // 2) it verifies that our SSL session is end-to-end with that node + // and not through a proxy that establishes two separate sessions. + { + auto const iter = headers.find("Session-Signature"); + + if (iter == headers.end()) + throw std::runtime_error("No session signature specified"); + + auto sig = base64_decode(iter->value().to_string()); + + if (! verifyDigest(publicKey, sharedValue, makeSlice(sig), false)) + throw std::runtime_error("Failed to verify session"); + } + + if (auto const iter = headers.find ("Local-IP"); iter != headers.end()) + { + boost::system::error_code ec; + auto const local_ip = boost::asio::ip::address::from_string( + iter->value().to_string(), ec); + + if (ec) + throw std::runtime_error("Invalid Local-IP"); + + if (beast::IP::is_public(remote) && remote != local_ip) + throw std::runtime_error("Incorrect Local-IP: " + + remote.to_string() + " instead of " + local_ip.to_string()); + } + + if (auto const iter = headers.find("Remote-IP"); iter != headers.end()) + { + boost::system::error_code ec; + auto const remote_ip = boost::asio::ip::address::from_string( + iter->value().to_string(), ec); + + if (ec) + throw std::runtime_error("Invalid Remote-IP"); + + if (beast::IP::is_public(remote) && !beast::IP::is_unspecified(public_ip)) + { + // We know our public IP and peer reports our connection came + // from some other IP. + if (remote_ip != public_ip) + throw std::runtime_error("Incorrect Remote-IP: " + + public_ip.to_string() + " instead of " + remote_ip.to_string()); + } + } + + return publicKey; +} + +} diff --git a/src/ripple/overlay/impl/Handshake.h b/src/ripple/overlay/impl/Handshake.h new file mode 100644 index 000000000..455780339 --- /dev/null +++ b/src/ripple/overlay/impl/Handshake.h @@ -0,0 +1,78 @@ +//------------------------------------------------------------------------------ +/* + 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_OVERLAY_HANDSHAKE_H_INCLUDED +#define RIPPLE_OVERLAY_HANDSHAKE_H_INCLUDED + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace ripple { + +/** Computes a shared value based on the SSL connection state. + + When there is no man in the middle, both sides will compute the same + value. In the presence of an attacker, the computed values will be + different. + + @param ssl the SSL/TLS connection state. + @return A 256-bit value on success; an unseated optional otherwise. +*/ +boost::optional +makeSharedValue (beast::asio::ssl_bundle& ssl, beast::Journal journal); + +/** Insert fields headers necessary for upgrading the link to the peer protocol. */ +void +buildHandshake( + boost::beast::http::fields& h, + uint256 const& sharedValue, + boost::optional networkID, + beast::IP::Address public_ip, + beast::IP::Address remote_ip, + Application& app); + +/** Validate header fields necessary for upgrading the link to the peer protocol. + + This performs critical security checks that ensure that prevent + MITM attacks on our peer-to-peer links and that the remote peer + has the private keys that correspond to the public identity it + claims. + + @return The public key of the remote peer. + @throw A class derived from std::exception. +*/ +PublicKey +verifyHandshake( + boost::beast::http::fields const& headers, + uint256 const& sharedValue, + boost::optional networkID, + beast::IP::Address public_ip, + beast::IP::Address remote, + Application& app); + +} + +#endif diff --git a/src/ripple/overlay/impl/Message.cpp b/src/ripple/overlay/impl/Message.cpp index c73dee6c1..7e0b81fa9 100644 --- a/src/ripple/overlay/impl/Message.cpp +++ b/src/ripple/overlay/impl/Message.cpp @@ -25,70 +25,28 @@ namespace ripple { Message::Message (::google::protobuf::Message const& message, int type) + : mCategory(TrafficCount::categorize(message, type, false)) { unsigned const messageBytes = message.ByteSize (); - assert (messageBytes != 0); - mBuffer.resize (kHeaderBytes + messageBytes); + /** Number of bytes in a message header. */ + std::size_t constexpr headerBytes = 6; - encodeHeader (messageBytes, type); + mBuffer.resize (headerBytes + messageBytes); + + auto ptr = mBuffer.data(); + + *ptr++ = static_cast((messageBytes >> 24) & 0xFF); + *ptr++ = static_cast((messageBytes >> 16) & 0xFF); + *ptr++ = static_cast((messageBytes >> 8) & 0xFF); + *ptr++ = static_cast(messageBytes & 0xFF); + + *ptr++ = static_cast((type >> 8) & 0xFF); + *ptr++ = static_cast (type & 0xFF); if (messageBytes != 0) - { - message.SerializeToArray (&mBuffer [Message::kHeaderBytes], messageBytes); - } - - mCategory = TrafficCount::categorize(message, type, false); -} - -bool Message::operator== (Message const& other) const -{ - return mBuffer == other.mBuffer; -} - -unsigned Message::getLength (std::vector const& buf) -{ - unsigned result; - - if (buf.size () >= Message::kHeaderBytes) - { - result = buf [0]; - result <<= 8; - result |= buf [1]; - result <<= 8; - result |= buf [2]; - result <<= 8; - result |= buf [3]; - } - else - { - result = 0; - } - - return result; -} - -int Message::getType (std::vector const& buf) -{ - if (buf.size () < Message::kHeaderBytes) - return 0; - - int ret = buf[4]; - ret <<= 8; - ret |= buf[5]; - return ret; -} - -void Message::encodeHeader (unsigned size, int type) -{ - assert (mBuffer.size () >= Message::kHeaderBytes); - mBuffer[0] = static_cast ((size >> 24) & 0xFF); - mBuffer[1] = static_cast ((size >> 16) & 0xFF); - mBuffer[2] = static_cast ((size >> 8) & 0xFF); - mBuffer[3] = static_cast (size & 0xFF); - mBuffer[4] = static_cast ((type >> 8) & 0xFF); - mBuffer[5] = static_cast (type & 0xFF); + message.SerializeToArray(ptr, messageBytes); } } diff --git a/src/ripple/overlay/impl/OverlayImpl.cpp b/src/ripple/overlay/impl/OverlayImpl.cpp index 0d7c65753..a0c3b74fd 100644 --- a/src/ripple/overlay/impl/OverlayImpl.cpp +++ b/src/ripple/overlay/impl/OverlayImpl.cpp @@ -238,88 +238,84 @@ OverlayImpl::onHandoff (std::unique_ptr && ssl_bundle, } } - auto hello = parseHello (true, request, journal); - if(! hello) + auto const negotiatedVersion = negotiateProtocolVersion(request["Upgrade"]); + if (!negotiatedVersion) { m_peerFinder->on_closed(slot); handoff.moved = false; handoff.response = makeErrorResponse (slot, request, - remote_endpoint.address(), - "Unable to parse HELLO message"); + remote_endpoint.address(), "Unable to agree on a protocol version"); handoff.keep_alive = false; return handoff; } - auto sharedValue = makeSharedValue( - ssl_bundle->stream.native_handle(), journal); + auto const sharedValue = makeSharedValue(*ssl_bundle, journal); if(! sharedValue) { m_peerFinder->on_closed(slot); handoff.moved = false; handoff.response = makeErrorResponse (slot, request, - remote_endpoint.address(), - "Incorrect security cookie (possible MITM detected)"); + remote_endpoint.address(), "Incorrect security cookie"); handoff.keep_alive = false; return handoff; } - auto publicKey = verifyHello (*hello, - *sharedValue, - setup_.public_ip, - beast::IPAddressConversion::from_asio( - remote_endpoint), journal, app_); - if(! publicKey) + try { + auto publicKey = verifyHandshake(request, *sharedValue, + setup_.networkID, setup_.public_ip, remote_endpoint.address(), app_); + + { + // The node gets a reserved slot if it is in our cluster + // or if it has a reservation. + bool const reserved = + static_cast(app_.cluster().member(publicKey)) + || app_.peerReservations().contains(publicKey); + auto const result = m_peerFinder->activate(slot, publicKey, reserved); + if (result != PeerFinder::Result::success) + { + m_peerFinder->on_closed(slot); + JLOG(journal.debug()) << "Peer " << remote_endpoint << " redirected, slots full"; + handoff.moved = false; + handoff.response = makeRedirectResponse(slot, request, + remote_endpoint.address()); + handoff.keep_alive = false; + return handoff; + } + } + + auto const peer = std::make_shared(app_, id, slot, + std::move(request), publicKey, *negotiatedVersion, consumer, + std::move(ssl_bundle), *this); + { + // As we are not on the strand, run() must be called + // while holding the lock, otherwise new I/O can be + // queued after a call to stop(). + std::lock_guard lock (mutex_); + { + auto const result = m_peers.emplace (peer->slot(), peer); + assert (result.second); + (void) result.second; + } + list_.emplace(peer.get(), peer); + + peer->run(); + } + handoff.moved = true; + return handoff; + } + catch (std::exception const& e) + { + JLOG(journal.debug()) << "Peer " << remote_endpoint << + " fails handshake (" << e.what() << ")"; + m_peerFinder->on_closed(slot); handoff.moved = false; handoff.response = makeErrorResponse (slot, request, - remote_endpoint.address(), - "Unable to verify HELLO message"); + remote_endpoint.address(), e.what()); handoff.keep_alive = false; return handoff; } - - { - // The node gets a reserved slot if it is in our cluster - // or if it has a reservation. - bool const reserved { - static_cast(app_.cluster().member(*publicKey)) - || app_.peerReservations().contains(*publicKey) - }; - auto const result = m_peerFinder->activate(slot, *publicKey, reserved); - if (result != PeerFinder::Result::success) - { - m_peerFinder->on_closed(slot); - JLOG(journal.debug()) - << "Peer " << remote_endpoint << " redirected, slots full"; - handoff.moved = false; - handoff.response = makeRedirectResponse( - slot, request, remote_endpoint.address()); - handoff.keep_alive = beast::rfc2616::is_keep_alive(request); - return handoff; - } - } - - auto const peer = std::make_shared(app_, id, - remote_endpoint, slot, std::move(request), *hello, - *publicKey, consumer, std::move(ssl_bundle), *this); - { - // As we are not on the strand, run() must be called - // while holding the lock, otherwise new I/O can be - // queued after a call to stop(). - std::lock_guard lock (mutex_); - { - auto const result = - m_peers.emplace (peer->slot(), peer); - assert (result.second); - (void) result.second; - } - list_.emplace(peer.get(), peer); - - peer->run(); - } - handoff.moved = true; - return handoff; } //------------------------------------------------------------------------------ @@ -329,11 +325,8 @@ OverlayImpl::isPeerUpgrade(http_request_type const& request) { if (! is_upgrade(request)) return false; - auto const versions = parse_ProtocolVersions( - request["Upgrade"]); - if (versions.size() == 0) - return false; - return true; + auto const versions = parseProtocolVersions(request["Upgrade"]); + return !versions.empty(); } std::string @@ -345,7 +338,7 @@ OverlayImpl::makePrefix (std::uint32_t id) } std::shared_ptr -OverlayImpl::makeRedirectResponse (PeerFinder::Slot::ptr const& slot, +OverlayImpl::makeRedirectResponse (std::shared_ptr const& slot, http_request_type const& request, address_type remote_address) { boost::beast::http::response msg; @@ -367,18 +360,18 @@ OverlayImpl::makeRedirectResponse (PeerFinder::Slot::ptr const& slot, } std::shared_ptr -OverlayImpl::makeErrorResponse (PeerFinder::Slot::ptr const& slot, +OverlayImpl::makeErrorResponse (std::shared_ptr const& slot, http_request_type const& request, address_type remote_address, std::string text) { - boost::beast::http::response msg; + boost::beast::http::response msg; msg.version(request.version()); msg.result(boost::beast::http::status::bad_request); + msg.reason("Bad Request (" + text + ")"); msg.insert("Server", BuildInfo::getFullVersionString()); msg.insert("Remote-Address", remote_address.to_string()); msg.insert(boost::beast::http::field::connection, "close"); - msg.body() = text; msg.prepare_payload(); return std::make_shared(msg); } @@ -454,7 +447,7 @@ OverlayImpl::add_active (std::shared_ptr const& peer) } void -OverlayImpl::remove (PeerFinder::Slot::ptr const& slot) +OverlayImpl::remove (std::shared_ptr const& slot) { std::lock_guard lock (mutex_); auto const iter = m_peers.find (slot); @@ -1275,6 +1268,7 @@ setup_Overlay (BasicConfig const& config) Throw("Configured public IP is invalid"); } } + { auto const& section = config.section("crawl"); auto const& values = section.values(); @@ -1322,6 +1316,28 @@ setup_Overlay (BasicConfig const& config) } } + try + { + auto id = config.legacy("network_id"); + + if (!id.empty()) + { + if (id == "main") + id = "0"; + + if (id == "testnet") + id = "1"; + + setup.networkID = beast::lexicalCastThrow(id); + } + } + catch (...) + { + Throw( + "Configured [network_id] section is invalid: " + "must be a number or one of the strings 'main' or 'testnet'"); + } + return setup; } diff --git a/src/ripple/overlay/impl/OverlayImpl.h b/src/ripple/overlay/impl/OverlayImpl.h index e599f569e..951b66710 100644 --- a/src/ripple/overlay/impl/OverlayImpl.h +++ b/src/ripple/overlay/impl/OverlayImpl.h @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -111,7 +112,7 @@ private: Resource::Manager& m_resourceManager; std::unique_ptr m_peerFinder; TrafficCount m_traffic; - hash_map , std::weak_ptr > m_peers; hash_map> ids_; Resolver& m_resolver; @@ -128,6 +129,8 @@ private: // Peer IDs expecting to receive a last link notification std::set csIDs_; + boost::optional networkID_; + //-------------------------------------------------------------------------- public: @@ -220,7 +223,7 @@ public: add_active (std::shared_ptr const& peer); void - remove (PeerFinder::Slot::ptr const& slot); + remove (std::shared_ptr const& slot); /** Called when a peer has connected successfully This is called after the peer handshake has been completed and during @@ -281,13 +284,7 @@ public: { if (! is_upgrade(response)) return false; - if(response.result() != boost::beast::http::status::switching_protocols) - return false; - auto const versions = parse_ProtocolVersions( - response["Upgrade"]); - if (versions.size() == 0) - return false; - return true; + return response.result() == boost::beast::http::status::switching_protocols; } template @@ -375,11 +372,11 @@ public: private: std::shared_ptr - makeRedirectResponse (PeerFinder::Slot::ptr const& slot, + makeRedirectResponse (std::shared_ptr const& slot, http_request_type const& request, address_type remote_address); std::shared_ptr - makeErrorResponse (PeerFinder::Slot::ptr const& slot, + makeErrorResponse (std::shared_ptr const& slot, http_request_type const& request, address_type remote_address, std::string msg); diff --git a/src/ripple/overlay/impl/PeerImp.cpp b/src/ripple/overlay/impl/PeerImp.cpp index 6ddcf9866..cda2c80e6 100644 --- a/src/ripple/overlay/impl/PeerImp.cpp +++ b/src/ripple/overlay/impl/PeerImp.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -53,10 +54,10 @@ using namespace std::chrono_literals; namespace ripple { -PeerImp::PeerImp (Application& app, id_t id, endpoint_type remote_endpoint, - PeerFinder::Slot::ptr const& slot, http_request_type&& request, - protocol::TMHello const& hello, PublicKey const& publicKey, - Resource::Consumer consumer, +PeerImp::PeerImp (Application& app, id_t id, + std::shared_ptr const& slot, http_request_type&& request, + PublicKey const& publicKey, + ProtocolVersion protocol, Resource::Consumer consumer, std::unique_ptr&& ssl_bundle, OverlayImpl& overlay) : Child (overlay) @@ -71,16 +72,15 @@ PeerImp::PeerImp (Application& app, id_t id, endpoint_type remote_endpoint, , stream_ (ssl_bundle_->stream) , strand_ (socket_.get_executor()) , timer_ (beast::create_waitable_timer(socket_)) - , remote_address_ ( - beast::IPAddressConversion::from_asio(remote_endpoint)) + , remote_address_ (slot->remote_endpoint()) , overlay_ (overlay) , m_inbound (true) + , protocol_ (protocol) , state_ (State::active) , sanity_ (Sanity::unknown) , insaneTime_ (clock_type::now()) , publicKey_(publicKey) , creationTime_ (clock_type::now()) - , hello_(hello) , usage_(consumer) , fee_ (Resource::feeLightPeer) , slot_ (slot) @@ -116,8 +116,51 @@ void PeerImp::run() { if(! strand_.running_in_this_thread()) - return post(strand_, std::bind ( - &PeerImp::run, shared_from_this())); + return post(strand_, std::bind(&PeerImp::run, shared_from_this())); + + // We need to decipher + auto parseLedgerHash = [](std::string const& value) -> boost::optional + { + uint256 ret; + if (ret.SetHexExact(value)) + return { ret }; + + auto const s = base64_decode(value); + if (s.size() != uint256::size()) + return boost::none; + return uint256{ s }; + }; + + boost::optional closed; + boost::optional previous; + + if (auto const iter = headers_.find("Closed-Ledger"); iter != headers_.end()) + { + closed = parseLedgerHash(iter->value().to_string()); + + if (!closed) + fail("Malformed handshake data (1)"); + } + + if (auto const iter = headers_.find("Previous-Ledger"); iter != headers_.end()) + { + previous = parseLedgerHash(iter->value().to_string()); + + if (!previous) + fail("Malformed handshake data (2)"); + } + + if (previous && !closed) + fail("Malformed handshake data (3)"); + + { + std::lock_guard sl(recentLock_); + if (closed) + closedLedgerHash_ = *closed; + if (previous) + previousLedgerHash_ = *previous; + } + if (m_inbound) { doAccept(); @@ -127,27 +170,6 @@ PeerImp::run() assert (state_ == State::active); // XXX Set timer: connection is in grace period to be useful. // XXX Set timer: connection idle (idle may vary depending on connection type.) - if (hello_.has_ledgerclosed() && - stringIsUint256Sized (hello_.ledgerclosed())) - { - // Operations on closedLedgerHash_ and previousLedgerHash_ must be - // guarded by recentLock_. - std::lock_guard sl(recentLock_); - - closedLedgerHash_ = hello_.ledgerclosed(); - - if (hello_.has_ledgerprevious() && - stringIsUint256Sized (hello_.ledgerprevious())) - { - previousLedgerHash_ = hello_.ledgerprevious(); - - addLedger (previousLedgerHash_, sl); - } - else - { - previousLedgerHash_.zero(); - } - } doProtocolStart(); } @@ -186,7 +208,7 @@ PeerImp::stop() //------------------------------------------------------------------------------ void -PeerImp::send (Message::pointer const& m) +PeerImp::send (std::shared_ptr const& m) { if (! strand_.running_in_this_thread()) return post(strand_, std::bind(&PeerImp::send, shared_from_this(), m)); @@ -266,10 +288,9 @@ PeerImp::cluster() const std::string PeerImp::getVersion() const { - if (hello_.has_fullversion ()) - return hello_.fullversion (); - - return std::string (); + if (m_inbound) + return headers_["User-Agent"].to_string(); + return headers_["Server"].to_string(); } Json::Value @@ -295,17 +316,14 @@ PeerImp::json() ret[jss::load] = usage_.balance (); - if (hello_.has_fullversion ()) - ret[jss::version] = hello_.fullversion (); - - if (hello_.has_protoversion ()) { - auto protocol = BuildInfo::make_protocol (hello_.protoversion ()); - - if (protocol != BuildInfo::getCurrentProtocol()) - ret[jss::protocol] = to_string (protocol); + auto const version = getVersion(); + if (!version.empty()) + ret[jss::version] = version; } + ret[jss::protocol] = to_string (protocol_); + { std::lock_guard sl (recentLock_); if (latency_) @@ -444,12 +462,6 @@ PeerImp::cycleStatus () closedLedgerHash_.zero (); } -bool -PeerImp::supportsVersion (int version) -{ - return hello_.has_protoversion () && (hello_.protoversion () >= version); -} - bool PeerImp::hasRange (std::uint32_t uMin, std::uint32_t uMax) { @@ -682,12 +694,11 @@ PeerImp::onShutdown(error_code ec) void PeerImp::doAccept() { assert(read_buffer_.size() == 0); -// assert(request_.upgrade); JLOG(journal_.debug()) << "doAccept: " << remote_address_; - auto sharedValue = makeSharedValue( - ssl_bundle_->stream.native_handle(), journal_); + auto const sharedValue = makeSharedValue(*ssl_bundle_, journal_); + // This shouldn't fail since we already computed // the shared value successfully in OverlayImpl if(! sharedValue) @@ -697,14 +708,13 @@ void PeerImp::doAccept() boost::beast::ostream(write_buffer_) << makeResponse( ! overlay_.peerFinder().config().peerPrivate, - request_, remote_address_, *sharedValue); + request_, remote_address_.address(), *sharedValue); + + JLOG(journal_.info()) << "Protocol: " << + to_string(protocol_); + JLOG(journal_.info()) << "Public Key: " << + toBase58 (TokenType::NodePublic, publicKey_); - auto const protocol = BuildInfo::make_protocol(hello_.protoversion()); - JLOG(journal_.info()) << "Protocol: " << to_string(protocol); - JLOG(journal_.info()) << - "Public Key: " << toBase58 ( - TokenType::NodePublic, - publicKey_); if (auto member = app_.cluster().member(publicKey_)) { { @@ -718,26 +728,6 @@ void PeerImp::doAccept() // XXX Set timer: connection is in grace period to be useful. // XXX Set timer: connection idle (idle may vary depending on connection type.) - if (hello_.has_ledgerclosed() && - stringIsUint256Sized (hello_.ledgerclosed())) - { - // Operations on closedLedgerHash_ and previousLedgerHash_ must be - // guarded by recentLock_. - std::lock_guard sl(recentLock_); - - closedLedgerHash_ = hello_.ledgerclosed(); - - if (hello_.has_ledgerprevious() && - stringIsUint256Sized (hello_.ledgerprevious())) - { - previousLedgerHash_ = hello_.ledgerprevious(); - addLedger (previousLedgerHash_, sl); - } - else - { - previousLedgerHash_.zero(); - } - } onWriteResponse(error_code(), 0); } @@ -745,20 +735,21 @@ void PeerImp::doAccept() http_response_type PeerImp::makeResponse (bool crawl, http_request_type const& req, - beast::IP::Endpoint remote, + beast::IP::Address remote_ip, uint256 const& sharedValue) { http_response_type resp; resp.result(boost::beast::http::status::switching_protocols); resp.version(req.version()); resp.insert("Connection", "Upgrade"); - resp.insert("Upgrade", "RTXP/1.2"); + resp.insert("Upgrade", to_string(protocol_)); resp.insert("Connect-As", "Peer"); resp.insert("Server", BuildInfo::getFullVersionString()); resp.insert("Crawl", crawl ? "public" : "private"); - protocol::TMHello hello = buildHello(sharedValue, - overlay_.setup().public_ip, remote, app_); - appendHello(resp, hello); + + buildHandshake(resp, sharedValue, overlay_.setup().networkID, + overlay_.setup().public_ip, remote_ip, app_); + return resp; } @@ -940,15 +931,13 @@ PeerImp::onWriteMessage (error_code ec, std::size_t bytes_transferred) // //------------------------------------------------------------------------------ -PeerImp::error_code +void PeerImp::onMessageUnknown (std::uint16_t type) { - error_code ec; // TODO - return ec; } -PeerImp::error_code +void PeerImp::onMessageBegin (std::uint16_t type, std::shared_ptr <::google::protobuf::Message> const& m, std::size_t size) @@ -958,7 +947,6 @@ PeerImp::onMessageBegin (std::uint16_t type, fee_ = Resource::feeLightPeer; overlay_.reportTraffic (TrafficCount::categorize (*m, type, true), true, static_cast(size)); - return error_code{}; } void @@ -969,12 +957,6 @@ PeerImp::onMessageEnd (std::uint16_t, charge (fee_); } -void -PeerImp::onMessage (std::shared_ptr const& m) -{ - fail("Deprecated TMHello"); -} - void PeerImp::onMessage (std::shared_ptr const& m) { @@ -1366,20 +1348,6 @@ PeerImp::onMessage(std::shared_ptr const& m) overlay_.lastLink(id_); } -void -PeerImp::onMessage (std::shared_ptr const& m) -{ - // This message is obsolete due to PeerFinder and - // we no longer provide a response to it. -} - -void -PeerImp::onMessage (std::shared_ptr const& m) -{ - // This message is obsolete due to PeerFinder and - // we no longer process it. -} - void PeerImp::onMessage (std::shared_ptr const& m) { @@ -2701,7 +2669,7 @@ PeerImp::getLedger (std::shared_ptr const& m) } } - Message::pointer oPacket = std::make_shared ( + auto oPacket = std::make_shared ( reply, protocol::mtLEDGER_DATA); send (oPacket); return; @@ -2806,7 +2774,7 @@ PeerImp::getLedger (std::shared_ptr const& m) "Got request for " << packet.nodeids().size() << " nodes at depth " << depth << ", return " << reply.nodes().size() << " nodes"; - Message::pointer oPacket = std::make_shared ( + auto oPacket = std::make_shared ( reply, protocol::mtLEDGER_DATA); send (oPacket); } diff --git a/src/ripple/overlay/impl/PeerImp.h b/src/ripple/overlay/impl/PeerImp.h index 112ec2e89..17eaf6b9d 100644 --- a/src/ripple/overlay/impl/PeerImp.h +++ b/src/ripple/overlay/impl/PeerImp.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -99,9 +100,6 @@ private: using endpoint_type = boost::asio::ip::tcp::endpoint; using waitable_timer = boost::asio::basic_waitable_timer; - // The length of the smallest valid finished message - static const size_t sslMinimumFinishedLength = 12; - Application& app_; id_t const id_; beast::WrappedSink sink_; @@ -124,6 +122,10 @@ private: // OverlayImpl& overlay_; bool const m_inbound; + + // Protocol version to use for this link + ProtocolVersion protocol_; + State state_; // Current state std::atomic sanity_; clock_type::time_point insaneTime_; @@ -179,16 +181,15 @@ private: std::mutex mutable recentLock_; protocol::TMStatusChange last_status_; - protocol::TMHello const hello_; Resource::Consumer usage_; Resource::Charge fee_; - PeerFinder::Slot::ptr const slot_; + std::shared_ptr const slot_; boost::beast::multi_buffer read_buffer_; http_request_type request_; http_response_type response_; boost::beast::http::fields const& headers_; boost::beast::multi_buffer write_buffer_; - std::queue send_queue_; + std::queue> send_queue_; bool gracefulClose_ = false; int large_sendq_ = 0; int no_ping_ = 0; @@ -230,10 +231,10 @@ public: PeerImp& operator= (PeerImp const&) = delete; /** Create an active incoming peer from an established ssl connection. */ - PeerImp (Application& app, id_t id, endpoint_type remote_endpoint, - PeerFinder::Slot::ptr const& slot, http_request_type&& request, - protocol::TMHello const& hello, PublicKey const& publicKey, - Resource::Consumer consumer, + PeerImp (Application& app, id_t id, + std::shared_ptr const& slot, http_request_type&& request, + PublicKey const& publicKey, + ProtocolVersion protocol, Resource::Consumer consumer, std::unique_ptr&& ssl_bundle, OverlayImpl& overlay); @@ -241,11 +242,10 @@ public: // VFALCO legacyPublicKey should be implied by the Slot template PeerImp (Application& app, std::unique_ptr&& ssl_bundle, - Buffers const& buffers, PeerFinder::Slot::ptr&& slot, + Buffers const& buffers, std::shared_ptr&& slot, http_response_type&& response, Resource::Consumer usage, - protocol::TMHello const& hello, - PublicKey const& publicKey, id_t id, - OverlayImpl& overlay); + PublicKey const& publicKey, + ProtocolVersion protocol, id_t id, OverlayImpl& overlay); virtual ~PeerImp(); @@ -256,7 +256,7 @@ public: return p_journal_; } - PeerFinder::Slot::ptr const& + std::shared_ptr const& slot() { return slot_; @@ -275,7 +275,7 @@ public: // void - send (Message::pointer const& m) override; + send (std::shared_ptr const& m) override; /** Send a set of PeerFinder endpoints as a protocol message. */ template const& m, std::size_t size); @@ -477,7 +474,6 @@ public: onMessageEnd (std::uint16_t type, std::shared_ptr <::google::protobuf::Message> const& m); - void onMessage (std::shared_ptr const& m); void onMessage (std::shared_ptr const& m); void onMessage (std::shared_ptr const& m); void onMessage (std::shared_ptr const& m); @@ -485,8 +481,6 @@ public: void onMessage (std::shared_ptr const& m); void onMessage (std::shared_ptr const& m); void onMessage (std::shared_ptr const& m); - void onMessage (std::shared_ptr const& m); - void onMessage (std::shared_ptr const& m); void onMessage (std::shared_ptr const& m); void onMessage (std::shared_ptr const& m); void onMessage (std::shared_ptr const& m); @@ -509,6 +503,8 @@ private: } //-------------------------------------------------------------------------- + void + setLedgerState(); // lockedRecentLock is passed as a reminder to callers that recentLock_ // must be locked. @@ -547,11 +543,10 @@ private: template PeerImp::PeerImp (Application& app, std::unique_ptr&& ssl_bundle, - Buffers const& buffers, PeerFinder::Slot::ptr&& slot, + Buffers const& buffers, std::shared_ptr&& slot, http_response_type&& response, Resource::Consumer usage, - protocol::TMHello const& hello, - PublicKey const& publicKey, id_t id, - OverlayImpl& overlay) + PublicKey const& publicKey, + ProtocolVersion protocol, id_t id, OverlayImpl& overlay) : Child (overlay) , app_ (app) , id_ (id) @@ -567,12 +562,12 @@ PeerImp::PeerImp (Application& app, std::unique_ptr&& s , remote_address_ (slot->remote_endpoint()) , overlay_ (overlay) , m_inbound (false) + , protocol_ (protocol) , state_ (State::active) , sanity_ (Sanity::unknown) , insaneTime_ (clock_type::now()) , publicKey_ (publicKey) , creationTime_ (clock_type::now()) - , hello_ (hello) , usage_ (usage) , fee_ (Resource::feeLightPeer) , slot_ (std::move(slot)) diff --git a/src/ripple/overlay/impl/PeerSet.cpp b/src/ripple/overlay/impl/PeerSet.cpp index 086434f0f..e17a04a6b 100644 --- a/src/ripple/overlay/impl/PeerSet.cpp +++ b/src/ripple/overlay/impl/PeerSet.cpp @@ -125,8 +125,7 @@ void PeerSet::sendRequest (const protocol::TMGetLedger& tmGL) if (mPeers.empty ()) return; - Message::pointer packet ( - std::make_shared (tmGL, protocol::mtGET_LEDGER)); + auto packet = std::make_shared(tmGL, protocol::mtGET_LEDGER); for (auto id : mPeers) { diff --git a/src/ripple/overlay/impl/ProtocolMessage.h b/src/ripple/overlay/impl/ProtocolMessage.h index 129fb295f..6dd047f8c 100644 --- a/src/ripple/overlay/impl/ProtocolMessage.h +++ b/src/ripple/overlay/impl/ProtocolMessage.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_OVERLAY_PROTOCOLMESSAGE_H_INCLUDED #define RIPPLE_OVERLAY_PROTOCOLMESSAGE_H_INCLUDED +#include #include #include #include @@ -41,17 +42,13 @@ protocolMessageName (int type) { switch (type) { - case protocol::mtHELLO: return "hello"; case protocol::mtMANIFESTS: return "manifests"; case protocol::mtPING: return "ping"; - case protocol::mtPROOFOFWORK: return "proof_of_work"; case protocol::mtCLUSTER: return "cluster"; case protocol::mtGET_SHARD_INFO: return "get_shard_info"; case protocol::mtSHARD_INFO: return "shard_info"; case protocol::mtGET_PEER_SHARD_INFO: return "get_peer_shard_info"; case protocol::mtPEER_SHARD_INFO: return "peer_shard_info"; - case protocol::mtGET_PEERS: return "get_peers"; - case protocol::mtPEERS: return "peers"; case protocol::mtENDPOINTS: return "endpoints"; case protocol::mtTRANSACTION: return "tx"; case protocol::mtGET_LEDGER: return "get_ledger"; @@ -63,33 +60,83 @@ protocolMessageName (int type) case protocol::mtGET_OBJECTS: return "get_objects"; default: break; - }; + } return "unknown"; } namespace detail { -template -std::enable_if_t::value, - boost::system::error_code> -invoke (int type, Buffers const& buffers, +struct MessageHeader +{ + /** The size of the message on the wire. + + @note This is the sum of sizes of the header and the payload. + */ + std::uint32_t total_wire_size = 0; + + /** The size of the header associated with this message. */ + std::uint32_t header_size = 0; + + /** The size of the payload on the wire. */ + std::uint32_t payload_wire_size = 0; + + /** The type of the message. */ + std::uint16_t message_type = 0; +}; + +template +boost::optional parseMessageHeader( + BufferSequence const& bufs, + std::size_t size) +{ + auto iter = boost::asio::buffers_iterator::begin(bufs); + + MessageHeader hdr; + + // Version 1 header: uncompressed payload. + // The top six bits of the first byte are 0. + if ((*iter & 0xFC) == 0) + { + hdr.header_size = 6; + + if (size < hdr.header_size) + return {}; + + for (int i = 0; i != 4; ++i) + hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++; + + hdr.total_wire_size = hdr.header_size + hdr.payload_wire_size; + + for (int i = 0; i != 2; ++i) + hdr.message_type = (hdr.message_type << 8) + *iter++; + + return hdr; + } + + return {}; +} + +template ::value>> +bool +invoke ( + MessageHeader const& header, + Buffers const& buffers, Handler& handler) { + auto const m = std::make_shared(); + ZeroCopyInputStream stream(buffers); - stream.Skip(Message::kHeaderBytes); - auto const m (std::make_shared()); + stream.Skip(header.header_size); + if (! m->ParseFromZeroCopyStream(&stream)) - return boost::system::errc::make_error_code( - boost::system::errc::invalid_argument); - auto ec = handler.onMessageBegin (type, m, - Message::kHeaderBytes + Message::size (buffers)); - if (! ec) - { - handler.onMessage (m); - handler.onMessageEnd (type, m); - } - return ec; + return false; + + handler.onMessageBegin (header.message_type, m, header.payload_wire_size); + handler.onMessage (m); + handler.onMessageEnd (header.message_type, m); + + return true; } } @@ -106,82 +153,100 @@ std::pair invokeProtocolMessage (Buffers const& buffers, Handler& handler) { std::pair result = { 0, {} }; - boost::system::error_code& ec = result.second; - auto const bs = boost::asio::buffer_size(buffers); + auto const size = boost::asio::buffer_size(buffers); - // If we don't even have enough bytes for the header, there's no point - // in doing any work. - if (bs < Message::kHeaderBytes) + if (size == 0) return result; - if (bs > Message::kMaxMessageSize) + auto header = detail::parseMessageHeader(buffers, size); + + // If we can't parse the header then it may be that we don't have enough + // bytes yet, or because the message was cut off. + if (!header) + return result; + + // We implement a maximum size for protocol messages. Sending a message + // whose size exceeds this may result in the connection being dropped. A + // larger message size may be supported in the future or negotiated as + // part of a protocol upgrade. + if (header->payload_wire_size > megabytes(64)) { result.second = make_error_code(boost::system::errc::message_size); return result; } - auto const size = Message::kHeaderBytes + Message::size(buffers); - - if (bs < size) + // We don't have the whole message yet. This isn't an error but we have + // nothing to do. + if (header->total_wire_size > size) return result; - auto const type = Message::type(buffers); + bool success; - switch (type) + switch (header->message_type) { - case protocol::mtHELLO: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtMANIFESTS: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtPING: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtCLUSTER: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtGET_SHARD_INFO: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtSHARD_INFO: ec = detail::invoke(type, buffers, handler); break; - case protocol::mtGET_PEER_SHARD_INFO: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtPEER_SHARD_INFO: ec = detail::invoke(type, buffers, handler); break; - case protocol::mtGET_PEERS: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtPEERS: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtENDPOINTS: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtTRANSACTION: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtGET_LEDGER: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtLEDGER_DATA: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtPROPOSE_LEDGER: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtSTATUS_CHANGE: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtHAVE_SET: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtVALIDATION: ec = detail::invoke (type, buffers, handler); break; - case protocol::mtGET_OBJECTS: ec = detail::invoke (type, buffers, handler); break; + case protocol::mtMANIFESTS: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtPING: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtCLUSTER: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtGET_SHARD_INFO: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtSHARD_INFO: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtGET_PEER_SHARD_INFO: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtPEER_SHARD_INFO: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtENDPOINTS: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtTRANSACTION: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtGET_LEDGER: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtLEDGER_DATA: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtPROPOSE_LEDGER: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtSTATUS_CHANGE: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtHAVE_SET: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtVALIDATION: + success = detail::invoke(*header, buffers, handler); + break; + case protocol::mtGET_OBJECTS: + success = detail::invoke(*header, buffers, handler); + break; default: - ec = handler.onMessageUnknown (type); + handler.onMessageUnknown (header->message_type); + success = true; break; } - if (! ec) - result.first = size; + + result.first = header->total_wire_size; + + if (!success) + result.second = make_error_code(boost::system::errc::bad_message); return result; } -/** Write a protocol message to a streambuf. */ -template -void -write (Streambuf& streambuf, - ::google::protobuf::Message const& m, int type, - std::size_t blockBytes) -{ - auto const size = m.ByteSize(); - std::array v; - v[0] = static_cast((size >> 24) & 0xFF); - v[1] = static_cast((size >> 16) & 0xFF); - v[2] = static_cast((size >> 8) & 0xFF); - v[3] = static_cast( size & 0xFF); - v[4] = static_cast((type >> 8) & 0xFF); - v[5] = static_cast( type & 0xFF); - streambuf.commit(boost::asio::buffer_copy( - streambuf.prepare(Message::kHeaderBytes), - boost::asio::buffer(v))); - ZeroCopyOutputStream stream ( - streambuf, blockBytes); - m.SerializeToZeroCopyStream(&stream); -} - } // ripple #endif diff --git a/src/ripple/overlay/impl/ProtocolVersion.cpp b/src/ripple/overlay/impl/ProtocolVersion.cpp new file mode 100644 index 000000000..bebcf4f1e --- /dev/null +++ b/src/ripple/overlay/impl/ProtocolVersion.cpp @@ -0,0 +1,184 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2019 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 + +namespace ripple { + +/** The list of protocol versions we speak and we prefer to use. + + @note The list must be sorted in strictly ascending order (and so + it may not contain any duplicates!) +*/ +constexpr +ProtocolVersion const supportedProtocolList[] +{ + { 1, 2 }, + { 2, 0 } +}; + +// This ugly construct ensures that supportedProtocolList is sorted in strictly +// ascending order and doesn't contain any duplicates. +// FIXME: With C++20 we can use std::is_sorted with an appropriate comparator +static_assert( + []() constexpr -> bool + { + auto const len = std::distance( + std::begin(supportedProtocolList), + std::end(supportedProtocolList)); + + // There should be at least one protocol we're willing to speak. + if (len == 0) + return false; + + // A list with only one entry is, by definition, sorted so we don't + // need to check it. + if (len != 1) + { + for (auto i = 0; i != len - 1; ++i) + { + if (supportedProtocolList[i] >= supportedProtocolList[i+1]) + return false; + } + } + + return true; + }(), "The list of supported protocols isn't properly sorted."); + + +std::string +to_string(ProtocolVersion const& p) +{ + // The legacy protocol uses a different name. This can be removed when we + // migrate away from it and require 2.0 or later. + if (p == ProtocolVersion{ 1, 2 }) + return "RTXP/1.2"; + + return "XRPL/" + std::to_string(p.first) + "." + std::to_string(p.second); +} + +std::vector +parseProtocolVersions(boost::beast::string_view const& value) +{ + static boost::regex re( + "^" // start of line + "XRPL/" // The string "XRPL/" + "([2-9]|(?:[1-9][0-9]+))" // a number (greater than 2 with no leading zeroes) + "\\." // a period + "(0|(?:[1-9][0-9]*))" // a number (no leading zeroes unless exactly zero) + "$" // The end of the string + , boost::regex_constants::optimize); + + std::vector result; + + for (auto const& s : beast::rfc2616::split_commas(value)) + { + if (s == "RTXP/1.2") + { + result.push_back(make_protocol(1, 2)); + continue; + } + + boost::smatch m; + + if (boost::regex_match(s, m, re)) + { + std::uint16_t major; + std::uint16_t minor; + if (!beast::lexicalCastChecked(major, std::string(m[1]))) + continue; + + if (!beast::lexicalCastChecked(minor, std::string(m[2]))) + continue; + + auto const proto = make_protocol(major, minor); + + // This is an extra sanity check: we check that the protocol we just + // decoded corresponds to the token we were parsing. + if (to_string(proto) == s) + result.push_back(make_protocol(major, minor)); + } + } + + // We guarantee that the returned list is sorted and contains no duplicates: + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), result.end()); + + return result; +} + +boost::optional +negotiateProtocolVersion(boost::beast::string_view const& versions) +{ + auto const them = parseProtocolVersions(versions); + + boost::optional result; + + // The protocol version we want to negotiate is the largest item in the + // intersection of the versions supported by us and the peer. Since the + // output of std::set_intersection is sorted, that item is always going + // to be the last one. So we get a little clever and avoid the need for + // a container: + std::function pickVersion = + [&result](ProtocolVersion const& v) + { + result = v; + }; + + std::set_intersection( + std::begin(them), std::end(them), + std::begin(supportedProtocolList), std::end(supportedProtocolList), + boost::make_function_output_iterator(pickVersion)); + + return result; +} + +std::string const& +supportedProtocolVersions() +{ + static std::string const supported = []() + { + std::string ret; + for (auto const& v : supportedProtocolList) + { + if (!ret.empty()) + ret += ", "; + ret += to_string(v); + } + + return ret; + }(); + + return supported; +} + +bool +isProtocolSupported(ProtocolVersion const& v) +{ + return std::end(supportedProtocolList) != std::find( + std::begin(supportedProtocolList), std::end(supportedProtocolList), v); +} + +} diff --git a/src/ripple/overlay/impl/ProtocolVersion.h b/src/ripple/overlay/impl/ProtocolVersion.h new file mode 100644 index 000000000..6e26d2c27 --- /dev/null +++ b/src/ripple/overlay/impl/ProtocolVersion.h @@ -0,0 +1,79 @@ +//------------------------------------------------------------------------------ +/* + 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_OVERLAY_PROTOCOLVERSION_H_INCLUDED +#define RIPPLE_OVERLAY_PROTOCOLVERSION_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +namespace ripple { + +/** Represents a particular version of the peer-to-peer protocol. + + The protocol is represented as two pairs of 16-bit integers; a major + and a minor. + * */ +using ProtocolVersion = std::pair; + +inline +ProtocolVersion +make_protocol(std::uint16_t major, std::uint16_t minor) +{ + return { major, minor }; +} + +/** Print a protocol version a human-readable string. */ +std::string +to_string(ProtocolVersion const& p); + +/** Parse a set of protocol versions. + + Given a comma-separated string, extract and return all those that look + like valid protocol versions (i.e. RTXP/1.2 and XRPL/2.0 and later). Any + strings that are not parseable as valid protocol strings are excluded + from the result set. + + @return A list of all apparently valid protocol versions. + + @note The returned list of protocol versions is guaranteed to contain + no duplicates and will be sorted in ascending protocol order. +*/ +std::vector +parseProtocolVersions(boost::beast::string_view const& s); + +/** Given a list of supported protocol versions, choose the one we prefer. */ +boost::optional +negotiateProtocolVersion(boost::beast::string_view const& versions); + +/** The list of all the protocol versions we support. */ +std::string const& +supportedProtocolVersions(); + +/** Determine whether we support a specific protocol version. */ +bool +isProtocolSupported(ProtocolVersion const& v); + +} + +#endif diff --git a/src/ripple/overlay/impl/TMHello.cpp b/src/ripple/overlay/impl/TMHello.cpp deleted file mode 100644 index 45a3d7c39..000000000 --- a/src/ripple/overlay/impl/TMHello.cpp +++ /dev/null @@ -1,462 +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 -#include -#include -#include -#include -#include -#include - -// VFALCO Shouldn't we have to include the OpenSSL -// headers or something for SSL_get_finished? - -namespace ripple { - -/** Hashes the latest finished message from an SSL stream - @param sslSession the session to get the message from. - @param hash the buffer into which the hash of the retrieved - message will be saved. The buffer MUST be at least - 64 bytes long. - @param getMessage a pointer to the function to call to retrieve the - finished message. This be either: - `SSL_get_finished` or - `SSL_get_peer_finished`. - @return `true` if successful, `false` otherwise. -*/ -static -boost::optional> -hashLastMessage (SSL const* ssl, - size_t (*get)(const SSL *, void *buf, size_t)) -{ - enum - { - sslMinimumFinishedLength = 12 - }; - unsigned char buf[1024]; - size_t len = get (ssl, buf, sizeof (buf)); - if(len < sslMinimumFinishedLength) - return boost::none; - base_uint<512> cookie; - SHA512 (buf, len, cookie.data()); - return cookie; -} - -boost::optional -makeSharedValue (SSL* ssl, beast::Journal journal) -{ - auto const cookie1 = hashLastMessage(ssl, SSL_get_finished); - if (!cookie1) - { - JLOG (journal.error()) << "Cookie generation: local setup not complete"; - return boost::none; - } - - auto const cookie2 = hashLastMessage(ssl, SSL_get_peer_finished); - if (!cookie2) - { - JLOG (journal.error()) << "Cookie generation: peer setup not complete"; - return boost::none; - } - - auto const result = (*cookie1 ^ *cookie2); - - // Both messages hash to the same value and the cookie - // is 0. Don't allow this. - if (result == beast::zero) - { - JLOG(journal.error()) << "Cookie generation: identical finished messages"; - return boost::none; - } - - return sha512Half (Slice (result.data(), result.size())); -} - -protocol::TMHello -buildHello ( - uint256 const& sharedValue, - beast::IP::Address public_ip, - beast::IP::Endpoint remote, - Application& app) -{ - protocol::TMHello h; - - auto const sig = signDigest ( - app.nodeIdentity().first, - app.nodeIdentity().second, - sharedValue); - - h.set_protoversion (to_packed (BuildInfo::getCurrentProtocol())); - h.set_protoversionmin (to_packed (BuildInfo::getMinimumProtocol())); - h.set_fullversion (BuildInfo::getFullVersionString ()); - h.set_nettime (app.timeKeeper().now().time_since_epoch().count()); - h.set_nodepublic ( - toBase58 ( - TokenType::NodePublic, - app.nodeIdentity().first)); - h.set_nodeproof (sig.data(), sig.size()); - h.set_testnet (false); - - if (beast::IP::is_public (remote)) - { - // Connection is to a public IP - h.set_remote_ip_str (remote.to_string()); - if (! public_ip.is_unspecified()) - h.set_local_ip_str (public_ip.to_string()); - } - - // We always advertise ourselves as private in the HELLO message. This - // suppresses the old peer advertising code and allows PeerFinder to - // take over the functionality. - h.set_nodeprivate (true); - - auto const closedLedger = app.getLedgerMaster().getClosedLedger(); - - assert(! closedLedger->open()); - // VFALCO There should ALWAYS be a closed ledger - if (closedLedger) - { - uint256 hash = closedLedger->info().hash; - h.set_ledgerclosed (hash.begin (), hash.size ()); - hash = closedLedger->info().parentHash; - h.set_ledgerprevious (hash.begin (), hash.size ()); - } - - return h; -} - -void -appendHello (boost::beast::http::fields& h, - protocol::TMHello const& hello) -{ - //h.append ("Protocol-Versions",... - - h.insert ("Public-Key", hello.nodepublic()); - - h.insert ("Session-Signature", base64_encode ( - hello.nodeproof())); - - if (hello.has_nettime()) - h.insert ("Network-Time", std::to_string (hello.nettime())); - - if (hello.has_ledgerindex()) - h.insert ("Ledger", std::to_string (hello.ledgerindex())); - - if (hello.has_ledgerclosed()) - h.insert ("Closed-Ledger", base64_encode ( - hello.ledgerclosed())); - - if (hello.has_ledgerprevious()) - h.insert ("Previous-Ledger", base64_encode ( - hello.ledgerprevious())); - - if (hello.has_local_ip_str()) - h.insert ("Local-IP", hello.local_ip_str()); - - if (hello.has_remote_ip()) - h.insert ("Remote-IP", hello.remote_ip_str()); -} - -std::vector -parse_ProtocolVersions(boost::beast::string_view const& value) -{ - static boost::regex re ( - "^" // start of line - "RTXP/" // the string "RTXP/" - "([1-9][0-9]*)" // a number (non-zero and with no leading zeroes) - "\\." // a period - "(0|[1-9][0-9]*)" // a number (no leading zeroes unless exactly zero) - "$" // The end of the string - , boost::regex_constants::optimize); - - auto const list = beast::rfc2616::split_commas(value); - std::vector result; - for (auto const& s : list) - { - boost::smatch m; - if (! boost::regex_match (s, m, re)) - continue; - int major; - int minor; - if (! beast::lexicalCastChecked ( - major, std::string (m[1]))) - continue; - if (! beast::lexicalCastChecked ( - minor, std::string (m[2]))) - continue; - result.push_back (std::make_pair (major, minor)); - } - std::sort(result.begin(), result.end()); - result.erase(std::unique (result.begin(), result.end()), result.end()); - return result; -} - -boost::optional -parseHello (bool request, boost::beast::http::fields const& h, beast::Journal journal) -{ - // protocol version in TMHello is obsolete, - // it is supplanted by the values in the headers. - protocol::TMHello hello; - - { - // Required - auto const iter = h.find ("Upgrade"); - if (iter == h.end()) - return boost::none; - auto const versions = parse_ProtocolVersions(iter->value().to_string()); - if (versions.empty()) - return boost::none; - hello.set_protoversion( - (safe_cast(versions.back().first) << 16) | - (safe_cast(versions.back().second))); - hello.set_protoversionmin( - (safe_cast(versions.front().first) << 16) | - (safe_cast(versions.front().second))); - } - - { - // Required - auto const iter = h.find ("Public-Key"); - if (iter == h.end()) - return boost::none; - auto const pk = parseBase58( - TokenType::NodePublic, iter->value().to_string()); - if (!pk) - return boost::none; - hello.set_nodepublic (iter->value().to_string()); - } - - { - // Required - auto const iter = h.find ("Session-Signature"); - if (iter == h.end()) - return boost::none; - // TODO Security Review - hello.set_nodeproof (base64_decode (iter->value().to_string())); - } - - { - auto const iter = h.find (request ? - "User-Agent" : "Server"); - if (iter != h.end()) - hello.set_fullversion (iter->value().to_string()); - } - - { - auto const iter = h.find ("Network-Time"); - if (iter != h.end()) - { - std::uint64_t nettime; - if (! beast::lexicalCastChecked(nettime, iter->value().to_string())) - return boost::none; - hello.set_nettime (nettime); - } - } - - { - auto const iter = h.find ("Ledger"); - if (iter != h.end()) - { - LedgerIndex ledgerIndex; - if (! beast::lexicalCastChecked(ledgerIndex, iter->value().to_string())) - return boost::none; - hello.set_ledgerindex (ledgerIndex); - } - } - - { - auto const iter = h.find ("Closed-Ledger"); - if (iter != h.end()) - hello.set_ledgerclosed (base64_decode (iter->value().to_string())); - } - - { - auto const iter = h.find ("Previous-Ledger"); - if (iter != h.end()) - hello.set_ledgerprevious (base64_decode (iter->value().to_string())); - } - - { - auto const iter = h.find ("Local-IP"); - if (iter != h.end()) - { - boost::system::error_code ec; - auto address = - beast::IP::Address::from_string (iter->value().to_string(), ec); - if (ec) - { - JLOG(journal.warn()) << "invalid Local-IP: " - << iter->value().to_string(); - return boost::none; - } - hello.set_local_ip_str(address.to_string()); - } - } - - { - auto const iter = h.find ("Remote-IP"); - if (iter != h.end()) - { - boost::system::error_code ec; - auto address = - beast::IP::Address::from_string (iter->value().to_string(), ec); - if (ec) - { - JLOG(journal.warn()) << "invalid Remote-IP: " - << iter->value().to_string(); - return boost::none; - } - hello.set_remote_ip_str(address.to_string()); - } - } - - return hello; -} - -boost::optional -verifyHello (protocol::TMHello const& h, - uint256 const& sharedValue, - beast::IP::Address public_ip, - beast::IP::Endpoint remote, - beast::Journal journal, - Application& app) -{ - if (h.has_nettime ()) - { - auto const ourTime = app.timeKeeper().now().time_since_epoch().count(); - auto const minTime = ourTime - clockToleranceDeltaSeconds; - auto const maxTime = ourTime + clockToleranceDeltaSeconds; - - if (h.nettime () > maxTime) - { - JLOG(journal.info()) << - "Clock for is off by +" << h.nettime() - ourTime; - return boost::none; - } - - if (h.nettime () < minTime) - { - JLOG(journal.info()) << - "Clock is off by -" << ourTime - h.nettime(); - return boost::none; - } - - JLOG(journal.trace()) << - "Connect: time offset " << - safe_cast(ourTime) - h.nettime(); - } - - if (h.protoversionmin () > to_packed (BuildInfo::getCurrentProtocol())) - { - JLOG(journal.info()) << - "Hello: Disconnect: Protocol mismatch [" << - "Peer expects " << to_string ( - BuildInfo::make_protocol(h.protoversion())) << - " and we run " << to_string ( - BuildInfo::getCurrentProtocol()) << "]"; - return boost::none; - } - - auto const publicKey = parseBase58( - TokenType::NodePublic, h.nodepublic()); - - if (! publicKey) - { - JLOG(journal.info()) << - "Hello: Disconnect: Bad node public key."; - return boost::none; - } - - if (publicKeyType(*publicKey) != KeyType::secp256k1) - { - JLOG(journal.info()) << - "Hello: Disconnect: Unsupported public key type."; - return boost::none; - } - - if (*publicKey == app.nodeIdentity().first) - { - JLOG(journal.info()) << - "Hello: Disconnect: Self connection."; - return boost::none; - } - - if (! verifyDigest (*publicKey, sharedValue, - makeSlice (h.nodeproof()), false)) - { - // Unable to verify they have private key for claimed public key. - JLOG(journal.info()) << - "Hello: Disconnect: Failed to verify session."; - return boost::none; - } - - if (h.has_local_ip_str () && - is_public (remote)) - { - boost::system::error_code ec; - auto local_ip = - beast::IP::Address::from_string (h.local_ip_str(), ec); - if (ec) - { - JLOG(journal.warn()) << "invalid local-ip: " << h.local_ip_str(); - return boost::none; - } - - if (remote.address() != local_ip) - { - // Remote asked us to confirm connection is from correct IP - JLOG(journal.info()) << - "Hello: Disconnect: Peer IP is " << remote.address().to_string() - << " not " << local_ip.to_string(); - return boost::none; - } - } - - if (h.has_remote_ip_str () && - is_public (remote) && - (! beast::IP::is_unspecified(public_ip))) - { - boost::system::error_code ec; - auto remote_ip = - beast::IP::Address::from_string (h.remote_ip_str(), ec); - if (ec) - { - JLOG(journal.warn()) << "invalid remote-ip: " << h.remote_ip_str(); - return boost::none; - } - - if (remote_ip != public_ip) - { - // We know our public IP and peer reports connection from some - // other IP - JLOG(journal.info()) << - "Hello: Disconnect: Our IP is " << public_ip.to_string() - << " not " << remote_ip.to_string(); - return boost::none; - } - } - - return publicKey; -} - -} diff --git a/src/ripple/overlay/impl/TMHello.h b/src/ripple/overlay/impl/TMHello.h deleted file mode 100644 index ae0afee74..000000000 --- a/src/ripple/overlay/impl/TMHello.h +++ /dev/null @@ -1,89 +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_OVERLAY_TMHELLO_H_INCLUDED -#define RIPPLE_OVERLAY_TMHELLO_H_INCLUDED - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace ripple { - -enum -{ - // The clock drift we allow a remote peer to have - clockToleranceDeltaSeconds = 20 -}; - -/** Computes a shared value based on the SSL connection state. - When there is no man in the middle, both sides will compute the same - value. In the presence of an attacker, the computed values will be - different. - If the shared value generation fails, the link MUST be dropped. - @return A pair. Second will be false if shared value generation failed. -*/ -boost::optional -makeSharedValue (SSL* ssl, beast::Journal journal); - -/** Build a TMHello protocol message. */ -protocol::TMHello -buildHello (uint256 const& sharedValue, - beast::IP::Address public_ip, - beast::IP::Endpoint remote, Application& app); - -/** Insert HTTP headers based on the TMHello protocol message. */ -void -appendHello (boost::beast::http::fields& h, protocol::TMHello const& hello); - -/** Parse HTTP headers into TMHello protocol message. - @return A protocol message on success; an empty optional - if the parsing failed. -*/ -boost::optional -parseHello (bool request, boost::beast::http::fields const& h, beast::Journal journal); - -/** Validate and store the public key in the TMHello. - This includes signature verification on the shared value. - @return The remote end public key on success; an empty - optional if the check failed. -*/ -boost::optional -verifyHello (protocol::TMHello const& h, uint256 const& sharedValue, - beast::IP::Address public_ip, - beast::IP::Endpoint remote, - beast::Journal journal, Application& app); - -/** Parse a set of protocol versions. - The returned list contains no duplicates and is sorted ascending. - Any strings that are not parseable as RTXP protocol strings are - excluded from the result set. -*/ -std::vector -parse_ProtocolVersions(boost::beast::string_view const& s); - -} - -#endif diff --git a/src/ripple/overlay/impl/TrafficCount.cpp b/src/ripple/overlay/impl/TrafficCount.cpp index abb4789db..8e0a69f06 100644 --- a/src/ripple/overlay/impl/TrafficCount.cpp +++ b/src/ripple/overlay/impl/TrafficCount.cpp @@ -25,9 +25,7 @@ TrafficCount::category TrafficCount::categorize ( ::google::protobuf::Message const& message, int type, bool inbound) { - if ((type == protocol::mtHELLO) || - (type == protocol::mtPING) || - (type == protocol::mtSTATUS_CHANGE)) + if ((type == protocol::mtPING) || (type == protocol::mtSTATUS_CHANGE)) return TrafficCount::category::base; if (type == protocol::mtCLUSTER) @@ -36,9 +34,7 @@ TrafficCount::category TrafficCount::categorize ( if (type == protocol::mtMANIFESTS) return TrafficCount::category::manifests; - if ((type == protocol::mtENDPOINTS) || - (type == protocol::mtPEERS) || - (type == protocol::mtGET_PEERS)) + if (type == protocol::mtENDPOINTS) return TrafficCount::category::overlay; if ((type == protocol::mtGET_SHARD_INFO) || diff --git a/src/ripple/overlay/predicates.h b/src/ripple/overlay/predicates.h index 9824609a2..ee88b6a35 100644 --- a/src/ripple/overlay/predicates.h +++ b/src/ripple/overlay/predicates.h @@ -32,9 +32,9 @@ struct send_always { using return_type = void; - Message::pointer const& msg; + std::shared_ptr const& msg; - send_always(Message::pointer const& m) + send_always(std::shared_ptr const& m) : msg(m) { } @@ -52,10 +52,10 @@ struct send_if_pred { using return_type = void; - Message::pointer const& msg; + std::shared_ptr const& msg; Predicate const& predicate; - send_if_pred(Message::pointer const& m, Predicate const& p) + send_if_pred(std::shared_ptr const& m, Predicate const& p) : msg(m), predicate(p) { } @@ -69,7 +69,7 @@ struct send_if_pred /** Helper function to aid in type deduction */ template send_if_pred send_if ( - Message::pointer const& m, + std::shared_ptr const& m, Predicate const &f) { return send_if_pred(m, f); @@ -83,10 +83,10 @@ struct send_if_not_pred { using return_type = void; - Message::pointer const& msg; + std::shared_ptr const& msg; Predicate const& predicate; - send_if_not_pred(Message::pointer const& m, Predicate const& p) + send_if_not_pred(std::shared_ptr const& m, Predicate const& p) : msg(m), predicate(p) { } @@ -100,7 +100,7 @@ struct send_if_not_pred /** Helper function to aid in type deduction */ template send_if_not_pred send_if_not ( - Message::pointer const& m, + std::shared_ptr const& m, Predicate const &f) { return send_if_not_pred(m, f); diff --git a/src/ripple/peerfinder/PeerfinderManager.h b/src/ripple/peerfinder/PeerfinderManager.h index 6c7150d20..4d651f724 100644 --- a/src/ripple/peerfinder/PeerfinderManager.h +++ b/src/ripple/peerfinder/PeerfinderManager.h @@ -174,7 +174,7 @@ public: If nullptr is returned, then the slot could not be assigned. Usually this is because of a detected self-connection. */ - virtual Slot::ptr new_inbound_slot ( + virtual std::shared_ptr new_inbound_slot ( beast::IP::Endpoint const& local_endpoint, beast::IP::Endpoint const& remote_endpoint) = 0; @@ -182,21 +182,21 @@ public: If nullptr is returned, then the slot could not be assigned. Usually this is because of a duplicate connection. */ - virtual Slot::ptr new_outbound_slot ( + virtual std::shared_ptr new_outbound_slot ( beast::IP::Endpoint const& remote_endpoint) = 0; /** Called when mtENDPOINTS is received. */ - virtual void on_endpoints (Slot::ptr const& slot, + virtual void on_endpoints (std::shared_ptr const& slot, Endpoints const& endpoints) = 0; /** Called when the slot is closed. This always happens when the socket is closed, unless the socket was canceled. */ - virtual void on_closed (Slot::ptr const& slot) = 0; + virtual void on_closed (std::shared_ptr const& slot) = 0; /** Called when an outbound connection is deemed to have failed */ - virtual void on_failure (Slot::ptr const& slot) = 0; + virtual void on_failure (std::shared_ptr const& slot) = 0; /** Called when we received redirect IPs from a busy peer. */ virtual @@ -215,19 +215,19 @@ public: */ virtual bool - onConnected (Slot::ptr const& slot, + onConnected (std::shared_ptr const& slot, beast::IP::Endpoint const& local_endpoint) = 0; /** Request an active slot type. */ virtual Result - activate (Slot::ptr const& slot, + activate (std::shared_ptr const& slot, PublicKey const& key, bool reserved) = 0; /** Returns a set of endpoints suitable for redirection. */ virtual std::vector - redirect (Slot::ptr const& slot) = 0; + redirect (std::shared_ptr const& slot) = 0; /** Return a set of addresses we should connect to. */ virtual @@ -235,7 +235,7 @@ public: autoconnect() = 0; virtual - std::vector>> + std::vector, std::vector>> buildEndpointsForPeers() = 0; /** Perform periodic activity. diff --git a/src/ripple/peerfinder/impl/Logic.h b/src/ripple/peerfinder/impl/Logic.h index d1b70f0f7..931789e02 100644 --- a/src/ripple/peerfinder/impl/Logic.h +++ b/src/ripple/peerfinder/impl/Logic.h @@ -573,10 +573,10 @@ public: return none; } - std::vector>> + std::vector, std::vector>> buildEndpointsForPeers() { - std::vector>> result; + std::vector, std::vector>> result; std::lock_guard _(lock_); diff --git a/src/ripple/peerfinder/impl/PeerfinderManager.cpp b/src/ripple/peerfinder/impl/PeerfinderManager.cpp index 2a0cf623b..6f4a660f6 100644 --- a/src/ripple/peerfinder/impl/PeerfinderManager.cpp +++ b/src/ripple/peerfinder/impl/PeerfinderManager.cpp @@ -119,7 +119,7 @@ public: //-------------------------------------------------------------------------- - Slot::ptr + std::shared_ptr new_inbound_slot ( beast::IP::Endpoint const& local_endpoint, beast::IP::Endpoint const& remote_endpoint) override @@ -127,14 +127,14 @@ public: return m_logic.new_inbound_slot (local_endpoint, remote_endpoint); } - Slot::ptr + std::shared_ptr new_outbound_slot (beast::IP::Endpoint const& remote_endpoint) override { return m_logic.new_outbound_slot (remote_endpoint); } void - on_endpoints (Slot::ptr const& slot, + on_endpoints (std::shared_ptr const& slot, Endpoints const& endpoints) override { SlotImp::ptr impl (std::dynamic_pointer_cast (slot)); @@ -142,14 +142,14 @@ public: } void - on_closed (Slot::ptr const& slot) override + on_closed (std::shared_ptr const& slot) override { SlotImp::ptr impl (std::dynamic_pointer_cast (slot)); m_logic.on_closed (impl); } void - on_failure (Slot::ptr const& slot) override + on_failure (std::shared_ptr const& slot) override { SlotImp::ptr impl (std::dynamic_pointer_cast (slot)); m_logic.on_failure (impl); @@ -165,7 +165,7 @@ public: //-------------------------------------------------------------------------- bool - onConnected (Slot::ptr const& slot, + onConnected (std::shared_ptr const& slot, beast::IP::Endpoint const& local_endpoint) override { SlotImp::ptr impl (std::dynamic_pointer_cast (slot)); @@ -173,14 +173,15 @@ public: } Result - activate (Slot::ptr const& slot, PublicKey const& key, bool reserved) override + activate (std::shared_ptr const& slot, + PublicKey const& key, bool reserved) override { SlotImp::ptr impl (std::dynamic_pointer_cast (slot)); return m_logic.activate (impl, key, reserved); } std::vector - redirect (Slot::ptr const& slot) override + redirect (std::shared_ptr const& slot) override { SlotImp::ptr impl (std::dynamic_pointer_cast (slot)); return m_logic.redirect (impl); @@ -198,7 +199,7 @@ public: m_logic.once_per_second(); } - std::vector>> + std::vector, std::vector>> buildEndpointsForPeers() override { return m_logic.buildEndpointsForPeers(); diff --git a/src/ripple/proto/ripple.proto b/src/ripple/proto/ripple.proto index ee4dafe2b..a82d62870 100644 --- a/src/ripple/proto/ripple.proto +++ b/src/ripple/proto/ripple.proto @@ -1,15 +1,14 @@ syntax = "proto2"; package protocol; +// Unused numbers in the list below may have been used previously. Please don't +// reassign them for reuse unless you are 100% certain that there won't be a +// conflict. Even if you're sure, it's probably best to assign a new type. enum MessageType { - mtHELLO = 1; mtMANIFESTS = 2; mtPING = 3; - mtPROOFOFWORK = 4; mtCLUSTER = 5; - mtGET_PEERS = 12; - mtPEERS = 13; mtENDPOINTS = 15; mtTRANSACTION = 30; mtGET_LEDGER = 31; @@ -23,14 +22,6 @@ enum MessageType mtSHARD_INFO = 51; mtGET_PEER_SHARD_INFO = 52; mtPEER_SHARD_INFO = 53; - - // = 10; - // = 11; - // = 14; - // = 20; - // = 21; - // = 22; - // = 40; } // token, iterations, target, challenge = issue demand for proof of work @@ -56,56 +47,6 @@ message TMManifests //------------------------------------------------------------------------------ -/* Requests or responds to a proof of work. - Unimplemented and unused currently. -*/ -message TMProofWork -{ - required string token = 1; - optional uint32 iterations = 2; - optional bytes target = 3; - optional bytes challenge = 4; - optional bytes response = 5; - - enum PowResult - { - powrOK = 0; - powrREUSED = 1; - powrEXPIRED = 2; // You took too long solving - powrTOOEASY = 3; // Difficulty went way up, sorry - powrINVALID = 4; - powrDISCONNECT = 5; // We are disconnecting - } - optional PowResult result = 6; -} - -//------------------------------------------------------------------------------ - -// Sent on connect -message TMHello -{ - // VFALCO NOTE The major and minor parts of the version number are - // encoded in the high and low order 16 bits of the uint32. - // - required uint32 protoVersion = 1; - required uint32 protoVersionMin = 2; - required bytes nodePublic = 3; - required bytes nodeProof = 4; - optional string fullVersion = 5; - optional uint64 netTime = 6; - optional uint32 ipv4Port = 7; // NOT USED - optional uint32 ledgerIndex = 8; - optional bytes ledgerClosed = 9; // our last closed ledger - optional bytes ledgerPrevious = 10; // the ledger before the last closed ledger - optional bool nodePrivate = 11; // Request to not forward IP. - optional TMProofWork proofOfWork = 12; // request/provide proof of work - optional bool testNet = 13; // Running as testnet. - optional uint32 local_ip = 14; // NOT USED -- our public IP - optional uint32 remote_ip = 15; // NOT USED -- IP we see connection from - optional string local_ip_str = 16; // our public IP - optional string remote_ip_str = 17; // IP we see connection from -} - // The status of a node in our cluster message TMClusterNode { @@ -265,11 +206,6 @@ message TMValidation optional uint32 hops = 3; // Number of hops traveled } -message TMGetPeers -{ - required uint32 doWeNeedThis = 1; // yes since you are asserting that the packet size isn't 0 in Message -} - message TMIPv4Endpoint { required uint32 ipv4 = 1; @@ -280,12 +216,6 @@ message TMIPv4Endpoint required uint32 ipv4Port = 2; } -// this message is obsolete/no longer procesed -message TMPeers -{ - repeated TMIPv4Endpoint nodes = 1; -} - // An Endpoint describes a network peer that can accept incoming connections message TMEndpoint { diff --git a/src/ripple/protocol/BuildInfo.h b/src/ripple/protocol/BuildInfo.h index 5fdc8aa17..505f0cdc9 100644 --- a/src/ripple/protocol/BuildInfo.h +++ b/src/ripple/protocol/BuildInfo.h @@ -25,8 +25,6 @@ namespace ripple { -/** Describes a Ripple/RTXP protocol version. */ -using ProtocolVersion = std::pair; /** Versioning information for this build. */ // VFALCO The namespace is deprecated @@ -46,25 +44,8 @@ getVersionString(); std::string const& getFullVersionString(); -/** Construct a protocol version from a packed 32-bit protocol identifier */ -ProtocolVersion -make_protocol (std::uint32_t version); - -/** The protocol version we speak and prefer. */ -ProtocolVersion const& -getCurrentProtocol(); - -/** The oldest protocol version we will accept. */ -ProtocolVersion const& getMinimumProtocol (); - } // BuildInfo (DEPRECATED) -std::string -to_string (ProtocolVersion const& p); - -std::uint32_t -to_packed (ProtocolVersion const& p); - } // ripple #endif diff --git a/src/ripple/crypto/KeyType.h b/src/ripple/protocol/KeyType.h similarity index 95% rename from src/ripple/crypto/KeyType.h rename to src/ripple/protocol/KeyType.h index 32195c764..cb96f4a7a 100644 --- a/src/ripple/crypto/KeyType.h +++ b/src/ripple/protocol/KeyType.h @@ -17,8 +17,8 @@ */ //============================================================================== -#ifndef RIPPLE_CRYPTO_KEYTYPE_H_INCLUDED -#define RIPPLE_CRYPTO_KEYTYPE_H_INCLUDED +#ifndef RIPPLE_PROTOCOL_KEYTYPE_H_INCLUDED +#define RIPPLE_PROTOCOL_KEYTYPE_H_INCLUDED #include #include diff --git a/src/ripple/protocol/PublicKey.h b/src/ripple/protocol/PublicKey.h index d2e485081..e4662decd 100644 --- a/src/ripple/protocol/PublicKey.h +++ b/src/ripple/protocol/PublicKey.h @@ -21,7 +21,7 @@ #define RIPPLE_PROTOCOL_PUBLICKEY_H_INCLUDED #include -#include // move to protocol/ +#include #include #include #include diff --git a/src/ripple/protocol/SecretKey.h b/src/ripple/protocol/SecretKey.h index b371ac65a..7565564ba 100644 --- a/src/ripple/protocol/SecretKey.h +++ b/src/ripple/protocol/SecretKey.h @@ -22,7 +22,7 @@ #include #include -#include // move to protocol/ +#include #include #include #include diff --git a/src/ripple/protocol/impl/BuildInfo.cpp b/src/ripple/protocol/impl/BuildInfo.cpp index 8657dc015..36ab5fd9c 100644 --- a/src/ripple/protocol/impl/BuildInfo.cpp +++ b/src/ripple/protocol/impl/BuildInfo.cpp @@ -33,7 +33,7 @@ namespace BuildInfo { char const* const versionString = "1.4.0" #if defined(DEBUG) || defined(SANITIZER) - "+" + "+" #ifdef DEBUG "DEBUG" #ifdef SANITIZER @@ -49,46 +49,9 @@ char const* const versionString = "1.4.0" //-------------------------------------------------------------------------- ; -ProtocolVersion const& -getCurrentProtocol () -{ - static ProtocolVersion currentProtocol ( - //-------------------------------------------------------------------------- - // - // The protocol version we speak and prefer (edit this if necessary) - // - 1, // major - 2 // minor - // - //-------------------------------------------------------------------------- - ); - - return currentProtocol; -} - -ProtocolVersion const& -getMinimumProtocol () -{ - static ProtocolVersion minimumProtocol ( - - //-------------------------------------------------------------------------- - // - // The oldest protocol version we will accept. (edit this if necessary) - // - 1, // major - 2 // minor - // - //-------------------------------------------------------------------------- - ); - - return minimumProtocol; -} - -// // // Don't touch anything below this line // -//------------------------------------------------------------------------------ std::string const& getVersionString () @@ -110,26 +73,6 @@ std::string const& getFullVersionString () return value; } -ProtocolVersion -make_protocol (std::uint32_t version) -{ - return ProtocolVersion( - static_cast ((version >> 16) & 0xffff), - static_cast (version & 0xffff)); -} - -} - -std::string -to_string (ProtocolVersion const& p) -{ - return std::to_string (p.first) + "." + std::to_string (p.second); -} - -std::uint32_t -to_packed (ProtocolVersion const& p) -{ - return (static_cast (p.first) << 16) + p.second; -} +} // BuildInfo } // ripple diff --git a/src/ripple/rpc/handlers/WalletPropose.cpp b/src/ripple/rpc/handlers/WalletPropose.cpp index b4561516f..ab1692f31 100644 --- a/src/ripple/rpc/handlers/WalletPropose.cpp +++ b/src/ripple/rpc/handlers/WalletPropose.cpp @@ -18,10 +18,10 @@ //============================================================================== #include -#include #include #include #include +#include #include #include #include diff --git a/src/ripple/rpc/impl/ServerHandlerImp.cpp b/src/ripple/rpc/impl/ServerHandlerImp.cpp index c764bf7fa..4212b1901 100644 --- a/src/ripple/rpc/impl/ServerHandlerImp.cpp +++ b/src/ripple/rpc/impl/ServerHandlerImp.cpp @@ -284,8 +284,7 @@ ServerHandlerImp::onRequest (Session& session) } // Check user/password authorization - if (! authorized ( - session.port(), build_map(session.request()))) + if (! authorized (session.port(), build_map(session.request()))) { HTTPReply (403, "Forbidden", makeOutput (session), app_.journal ("RPC")); session.close (true); diff --git a/src/ripple/unity/overlay2.cpp b/src/ripple/unity/overlay2.cpp index 81d07cd94..271c37c5d 100644 --- a/src/ripple/unity/overlay2.cpp +++ b/src/ripple/unity/overlay2.cpp @@ -18,10 +18,11 @@ //============================================================================== +#include #include #include #include -#include +#include #include #if DOXYGEN diff --git a/src/test/jtx/Account.h b/src/test/jtx/Account.h index f8ddf34c7..2e9e0b3aa 100644 --- a/src/test/jtx/Account.h +++ b/src/test/jtx/Account.h @@ -20,9 +20,9 @@ #ifndef RIPPLE_TEST_JTX_ACCOUNT_H_INCLUDED #define RIPPLE_TEST_JTX_ACCOUNT_H_INCLUDED +#include #include #include -#include #include #include #include diff --git a/src/test/overlay/ProtocolVersion_test.cpp b/src/test/overlay/ProtocolVersion_test.cpp new file mode 100644 index 000000000..cf55a76a4 --- /dev/null +++ b/src/test/overlay/ProtocolVersion_test.cpp @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2019 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 + +namespace ripple { + +class ProtocolVersion_test : public beast::unit_test::suite +{ +private: + template + static + std::string + join (FwdIt first, FwdIt last, char const* sep = ",") + { + std::string result; + if (first == last) + return result; + result = to_string(*first++); + while(first != last) + result += sep + to_string(*first++); + return result; + } + + void + check(std::string const& s, std::string const& answer) + { + auto const result = parseProtocolVersions(s); + BEAST_EXPECT(join(result.begin(), result.end()) == answer); + } + +public: + void + run() override + { + testcase("Convert protocol version to string"); + BEAST_EXPECT(to_string( make_protocol(1,2)) == "RTXP/1.2"); + BEAST_EXPECT(to_string( make_protocol(1,3)) == "XRPL/1.3"); + BEAST_EXPECT(to_string( make_protocol(2,0)) == "XRPL/2.0"); + BEAST_EXPECT(to_string( make_protocol(2,1)) == "XRPL/2.1"); + BEAST_EXPECT(to_string(make_protocol(10,10)) == "XRPL/10.10"); + + { + testcase("Convert strings to protocol versions"); + + // Empty string + check( + "", + ""); + check( + "RTXP/1.1,RTXP/1.3,XRPL/2.1,RTXP/1.2,XRPL/2.0", + "RTXP/1.2,XRPL/2.0,XRPL/2.1"); + check( + "RTXP/0.9,RTXP/1.01,XRPL/0.3,XRPL/2.01,XRPL/19.04,Oscar/123,NIKB", + ""); + check( + "RTXP/1.2,XRPL/2.0,RTXP/1.2,XRPL/2.0,XRPL/19.4,XRPL/7.89,XRPL/A.1,XRPL/2.01", + "RTXP/1.2,XRPL/2.0,XRPL/7.89,XRPL/19.4"); + check( + "XRPL/2.0,XRPL/3.0,XRPL/4,XRPL/,XRPL,OPT XRPL/2.2,XRPL/5.67", + "XRPL/2.0,XRPL/3.0,XRPL/5.67"); + } + + { + testcase("Protocol version negotiation"); + + BEAST_EXPECT(negotiateProtocolVersion("RTXP/1.2") == make_protocol(1,2)); + BEAST_EXPECT(negotiateProtocolVersion("RTXP/1.2, XRPL/2.0") == make_protocol(2,0)); + BEAST_EXPECT(negotiateProtocolVersion("XRPL/2.0") == make_protocol(2,0)); + BEAST_EXPECT(negotiateProtocolVersion("RTXP/1.2, XRPL/2.0, XRPL/999.999") == make_protocol(2,0)); + BEAST_EXPECT(negotiateProtocolVersion("XRPL/999.999, WebSocket/1.0") == boost::none); + BEAST_EXPECT(negotiateProtocolVersion("") == boost::none); + } + } +}; + +BEAST_DEFINE_TESTSUITE(ProtocolVersion,overlay,ripple); + +} + diff --git a/src/test/overlay/TMHello_test.cpp b/src/test/overlay/TMHello_test.cpp deleted file mode 100644 index 979231d63..000000000 --- a/src/test/overlay/TMHello_test.cpp +++ /dev/null @@ -1,70 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright 2014 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 - -namespace ripple { - -class TMHello_test : public beast::unit_test::suite -{ -private: - template - static - std::string - join (FwdIt first, FwdIt last, char c = ',') - { - std::string result; - if (first == last) - return result; - result = to_string(*first++); - while(first != last) - result += "," + to_string(*first++); - return result; - } - - void - check(std::string const& s, std::string const& answer) - { - auto const result = parse_ProtocolVersions(s); - BEAST_EXPECT(join(result.begin(), result.end()) == answer); - } - -public: - void - test_protocolVersions() - { - check("", ""); - check("RTXP/1.0", "1.0"); - check("RTXP/1.0, Websocket/1.0", "1.0"); - check("RTXP/1.0, RTXP/1.0", "1.0"); - check("RTXP/1.0, RTXP/1.1", "1.0,1.1"); - check("RTXP/1.1, RTXP/1.0", "1.0,1.1"); - } - - void - run() override - { - test_protocolVersions(); - } -}; - -BEAST_DEFINE_TESTSUITE(TMHello,overlay,ripple); - -} diff --git a/src/test/protocol/BuildInfo_test.cpp b/src/test/protocol/BuildInfo_test.cpp deleted file mode 100644 index 40840627f..000000000 --- a/src/test/protocol/BuildInfo_test.cpp +++ /dev/null @@ -1,106 +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 BuildInfo_test : public beast::unit_test::suite -{ -public: - ProtocolVersion - from_version (std::uint16_t major, std::uint16_t minor) - { - return ProtocolVersion (major, minor); - } - - void testValues () - { - testcase ("comparison"); - - BEAST_EXPECT(from_version (1,2) == from_version (1,2)); - BEAST_EXPECT(from_version (3,4) >= from_version (3,4)); - BEAST_EXPECT(from_version (5,6) <= from_version (5,6)); - BEAST_EXPECT(from_version (7,8) > from_version (6,7)); - BEAST_EXPECT(from_version (7,8) < from_version (8,9)); - BEAST_EXPECT(from_version (65535,0) < from_version (65535,65535)); - BEAST_EXPECT(from_version (65535,65535) >= from_version (65535,65535)); - } - - void testStringVersion () - { - testcase ("string version"); - - for (std::uint16_t major = 0; major < 8; major++) - { - for (std::uint16_t minor = 0; minor < 8; minor++) - { - BEAST_EXPECT(to_string (from_version (major, minor)) == - std::to_string (major) + "." + std::to_string (minor)); - } - } - } - - void testVersionPacking () - { - testcase ("version packing"); - - BEAST_EXPECT(to_packed (from_version (0, 0)) == 0); - BEAST_EXPECT(to_packed (from_version (0, 1)) == 1); - BEAST_EXPECT(to_packed (from_version (0, 255)) == 255); - BEAST_EXPECT(to_packed (from_version (0, 65535)) == 65535); - - BEAST_EXPECT(to_packed (from_version (1, 0)) == 65536); - BEAST_EXPECT(to_packed (from_version (1, 1)) == 65537); - BEAST_EXPECT(to_packed (from_version (1, 255)) == 65791); - BEAST_EXPECT(to_packed (from_version (1, 65535)) == 131071); - - BEAST_EXPECT(to_packed (from_version (255, 0)) == 16711680); - BEAST_EXPECT(to_packed (from_version (255, 1)) == 16711681); - BEAST_EXPECT(to_packed (from_version (255, 255)) == 16711935); - BEAST_EXPECT(to_packed (from_version (255, 65535)) == 16777215); - - BEAST_EXPECT(to_packed (from_version (65535, 0)) == 4294901760); - BEAST_EXPECT(to_packed (from_version (65535, 1)) == 4294901761); - BEAST_EXPECT(to_packed (from_version (65535, 255)) == 4294902015); - BEAST_EXPECT(to_packed (from_version (65535, 65535)) == 4294967295); - } - - void run () override - { - testValues (); - testStringVersion (); - testVersionPacking (); - - auto const current_protocol = BuildInfo::getCurrentProtocol (); - auto const minimum_protocol = BuildInfo::getMinimumProtocol (); - - BEAST_EXPECT(current_protocol >= minimum_protocol); - - log << - " Ripple Version: " << BuildInfo::getVersionString() << '\n' << - " Protocol Version: " << to_string (current_protocol) << std::endl; - } -}; - -BEAST_DEFINE_TESTSUITE(BuildInfo,ripple_data,ripple); - -} // ripple diff --git a/src/test/unity/overlay_test_unity.cpp b/src/test/unity/overlay_test_unity.cpp index 7b9cd76ac..42881ee48 100644 --- a/src/test/unity/overlay_test_unity.cpp +++ b/src/test/unity/overlay_test_unity.cpp @@ -18,6 +18,6 @@ */ //============================================================================== +#include #include #include -#include \ No newline at end of file diff --git a/src/test/unity/protocol_test_unity.cpp b/src/test/unity/protocol_test_unity.cpp index ab1aecfc1..016770c11 100644 --- a/src/test/unity/protocol_test_unity.cpp +++ b/src/test/unity/protocol_test_unity.cpp @@ -18,7 +18,6 @@ */ //============================================================================== -#include #include #include #include