mirror of
				https://github.com/Xahau/xahaud.git
				synced 2025-11-04 10:45:50 +00:00 
			
		
		
		
	Improve protocol-level handshaking protocol:
This commit restructures the HTTP based protocol negotiation that `rippled` executes and introduces support for negotiation of compression for peer links which, if implemented, should result in significant bandwidth savings for some server roles. This commit also introduces the new `[network_id]` configuration option that administrators can use to specify which network the server is part of and intends to join. This makes it possible for servers from different networks to drop the link early. The changeset also improves the log messages generated when negotiation of a peer link upgrade fails. In the past, no useful information would be logged, making it more difficult for admins to troubleshoot errors. This commit also fixes RIPD-237 and RIPD-451
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@
 | 
			
		||||
#include <boost/asio/buffer.hpp>
 | 
			
		||||
#include <boost/asio/buffers_iterator.hpp>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
#include <memory>
 | 
			
		||||
@@ -47,26 +48,9 @@ namespace ripple {
 | 
			
		||||
class Message : public std::enable_shared_from_this <Message>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    using pointer = std::shared_ptr<Message>;
 | 
			
		||||
 | 
			
		||||
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 <uint8_t> 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 <uint8_t> const& buf);
 | 
			
		||||
 | 
			
		||||
    template <class FwdIter>
 | 
			
		||||
    static
 | 
			
		||||
    std::enable_if_t<std::is_same<typename
 | 
			
		||||
        FwdIter::value_type, std::uint8_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 <class BufferSequence>
 | 
			
		||||
    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 <uint8_t> const& buf);
 | 
			
		||||
 | 
			
		||||
    template <class FwdIter>
 | 
			
		||||
    static
 | 
			
		||||
    std::enable_if_t<std::is_same<typename
 | 
			
		||||
        FwdIter::value_type, std::uint8_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 <class BufferSequence>
 | 
			
		||||
    static
 | 
			
		||||
    int
 | 
			
		||||
    type (BufferSequence const& buffers)
 | 
			
		||||
    {
 | 
			
		||||
        return type(buffers_begin(buffers),
 | 
			
		||||
            buffers_end(buffers));
 | 
			
		||||
    }
 | 
			
		||||
    /** @} */
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    template <class BufferSequence, class Value = std::uint8_t>
 | 
			
		||||
    static
 | 
			
		||||
    boost::asio::buffers_iterator<BufferSequence, Value>
 | 
			
		||||
    buffers_begin (BufferSequence const& buffers)
 | 
			
		||||
    {
 | 
			
		||||
        return boost::asio::buffers_iterator<
 | 
			
		||||
            BufferSequence, Value>::begin (buffers);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <class BufferSequence, class Value = std::uint8_t>
 | 
			
		||||
    static
 | 
			
		||||
    boost::asio::buffers_iterator<BufferSequence, Value>
 | 
			
		||||
    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 <uint8_t> mBuffer;
 | 
			
		||||
 | 
			
		||||
    std::size_t mCategory;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#include <boost/asio/buffer.hpp>
 | 
			
		||||
#include <boost/asio/ip/tcp.hpp>
 | 
			
		||||
#include <boost/optional.hpp>
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
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<std::uint32_t> networkID;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    using PeerSequence = std::vector <std::shared_ptr<Peer>>;
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ public:
 | 
			
		||||
 | 
			
		||||
    virtual
 | 
			
		||||
    void
 | 
			
		||||
    send (Message::pointer const& m) = 0;
 | 
			
		||||
    send (std::shared_ptr<Message> 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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 #
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@
 | 
			
		||||
 | 
			
		||||
#include <ripple/overlay/impl/ConnectAttempt.h>
 | 
			
		||||
#include <ripple/overlay/impl/PeerImp.h>
 | 
			
		||||
#include <ripple/overlay/impl/ProtocolVersion.h>
 | 
			
		||||
#include <ripple/overlay/Cluster.h>
 | 
			
		||||
#include <ripple/json/json_reader.h>
 | 
			
		||||
 | 
			
		||||
@@ -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<PeerFinder::Slot> 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<char const*>(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<ProtocolVersion> 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<bool>(member));
 | 
			
		||||
        if (result != PeerFinder::Result::success)
 | 
			
		||||
            return fail("Outbound slots full");
 | 
			
		||||
 | 
			
		||||
        auto const peer = std::make_shared<PeerImp>(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<bool>(member));
 | 
			
		||||
    if (result != PeerFinder::Result::success)
 | 
			
		||||
        return fail("Outbound slots full");
 | 
			
		||||
 | 
			
		||||
    auto const peer = std::make_shared<PeerImp>(app_,
 | 
			
		||||
        std::move(ssl_bundle_), read_buf_.data(),
 | 
			
		||||
            std::move(slot_), std::move(response_),
 | 
			
		||||
                usage_, *hello, *publicKey, id_, overlay_);
 | 
			
		||||
 | 
			
		||||
    overlay_.add_active (peer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // ripple
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,6 @@
 | 
			
		||||
#ifndef RIPPLE_OVERLAY_CONNECTATTEMPT_H_INCLUDED
 | 
			
		||||
#define RIPPLE_OVERLAY_CONNECTATTEMPT_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <ripple/protocol/messages.h>
 | 
			
		||||
#include <ripple/overlay/impl/OverlayImpl.h>
 | 
			
		||||
#include <ripple/overlay/impl/Tuning.h>
 | 
			
		||||
 | 
			
		||||
@@ -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<PeerFinder::Slot> 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<PeerFinder::Slot> 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();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										287
									
								
								src/ripple/overlay/impl/Handshake.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								src/ripple/overlay/impl/Handshake.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -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 <ripple/overlay/impl/Handshake.h>
 | 
			
		||||
#include <ripple/app/ledger/LedgerMaster.h>
 | 
			
		||||
#include <ripple/app/main/Application.h>
 | 
			
		||||
#include <ripple/basics/base64.h>
 | 
			
		||||
#include <ripple/basics/safe_cast.h>
 | 
			
		||||
#include <ripple/beast/rfc2616.h>
 | 
			
		||||
#include <ripple/beast/core/LexicalCast.h>
 | 
			
		||||
#include <ripple/protocol/digest.h>
 | 
			
		||||
#include <boost/regex.hpp>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// 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<base_uint<512>>
 | 
			
		||||
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<uint256>
 | 
			
		||||
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<std::uint32_t> 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<std::uint32_t> 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<std::chrono::seconds>(a - b);
 | 
			
		||||
            return - duration_cast<std::chrono::seconds>(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<PublicKey>(
 | 
			
		||||
                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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								src/ripple/overlay/impl/Handshake.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/ripple/overlay/impl/Handshake.h
									
									
									
									
									
										Normal file
									
								
							@@ -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 <ripple/app/main/Application.h>
 | 
			
		||||
#include <ripple/beast/asio/ssl_bundle.h>
 | 
			
		||||
#include <ripple/beast/utility/Journal.h>
 | 
			
		||||
#include <ripple/protocol/BuildInfo.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/beast/http/fields.hpp>
 | 
			
		||||
#include <boost/asio/ssl.hpp>
 | 
			
		||||
#include <boost/optional.hpp>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
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<uint256>
 | 
			
		||||
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<std::uint32_t> 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<std::uint32_t> networkID,
 | 
			
		||||
    beast::IP::Address public_ip,
 | 
			
		||||
    beast::IP::Address remote,
 | 
			
		||||
    Application& app);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -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<std::uint8_t>((messageBytes >> 24) & 0xFF);
 | 
			
		||||
    *ptr++ = static_cast<std::uint8_t>((messageBytes >> 16) & 0xFF);
 | 
			
		||||
    *ptr++ = static_cast<std::uint8_t>((messageBytes >> 8) & 0xFF);
 | 
			
		||||
    *ptr++ = static_cast<std::uint8_t>(messageBytes & 0xFF);
 | 
			
		||||
 | 
			
		||||
    *ptr++ = static_cast<std::uint8_t>((type >> 8) & 0xFF);
 | 
			
		||||
    *ptr++ = static_cast<std::uint8_t> (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 <uint8_t> 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<uint8_t> 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<std::uint8_t> ((size >> 24) & 0xFF);
 | 
			
		||||
    mBuffer[1] = static_cast<std::uint8_t> ((size >> 16) & 0xFF);
 | 
			
		||||
    mBuffer[2] = static_cast<std::uint8_t> ((size >> 8) & 0xFF);
 | 
			
		||||
    mBuffer[3] = static_cast<std::uint8_t> (size & 0xFF);
 | 
			
		||||
    mBuffer[4] = static_cast<std::uint8_t> ((type >> 8) & 0xFF);
 | 
			
		||||
    mBuffer[5] = static_cast<std::uint8_t> (type & 0xFF);
 | 
			
		||||
        message.SerializeToArray(ptr, messageBytes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -238,88 +238,84 @@ OverlayImpl::onHandoff (std::unique_ptr <beast::asio::ssl_bundle>&& 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<bool>(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<PeerImp>(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 <decltype(mutex_)> 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<bool>(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<PeerImp>(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<Writer>
 | 
			
		||||
OverlayImpl::makeRedirectResponse (PeerFinder::Slot::ptr const& slot,
 | 
			
		||||
OverlayImpl::makeRedirectResponse (std::shared_ptr<PeerFinder::Slot> const& slot,
 | 
			
		||||
    http_request_type const& request, address_type remote_address)
 | 
			
		||||
{
 | 
			
		||||
    boost::beast::http::response<json_body> msg;
 | 
			
		||||
@@ -367,18 +360,18 @@ OverlayImpl::makeRedirectResponse (PeerFinder::Slot::ptr const& slot,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::shared_ptr<Writer>
 | 
			
		||||
OverlayImpl::makeErrorResponse (PeerFinder::Slot::ptr const& slot,
 | 
			
		||||
OverlayImpl::makeErrorResponse (std::shared_ptr<PeerFinder::Slot> const& slot,
 | 
			
		||||
    http_request_type const& request,
 | 
			
		||||
    address_type remote_address,
 | 
			
		||||
    std::string text)
 | 
			
		||||
{
 | 
			
		||||
    boost::beast::http::response<boost::beast::http::string_body> msg;
 | 
			
		||||
    boost::beast::http::response<boost::beast::http::empty_body> 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<SimpleWriter>(msg);
 | 
			
		||||
}
 | 
			
		||||
@@ -454,7 +447,7 @@ OverlayImpl::add_active (std::shared_ptr<PeerImp> const& peer)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
OverlayImpl::remove (PeerFinder::Slot::ptr const& slot)
 | 
			
		||||
OverlayImpl::remove (std::shared_ptr<PeerFinder::Slot> const& slot)
 | 
			
		||||
{
 | 
			
		||||
    std::lock_guard lock (mutex_);
 | 
			
		||||
    auto const iter = m_peers.find (slot);
 | 
			
		||||
@@ -1275,6 +1268,7 @@ setup_Overlay (BasicConfig const& config)
 | 
			
		||||
                Throw<std::runtime_error>("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<std::uint32_t>(id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    catch (...)
 | 
			
		||||
    {
 | 
			
		||||
        Throw<std::runtime_error>(
 | 
			
		||||
            "Configured [network_id] section is invalid: "
 | 
			
		||||
            "must be a number or one of the strings 'main' or 'testnet'");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return setup;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@
 | 
			
		||||
#include <ripple/basics/Resolver.h>
 | 
			
		||||
#include <ripple/basics/chrono.h>
 | 
			
		||||
#include <ripple/basics/UnorderedContainers.h>
 | 
			
		||||
#include <ripple/overlay/impl/TMHello.h>
 | 
			
		||||
#include <ripple/overlay/impl/Handshake.h>
 | 
			
		||||
#include <ripple/peerfinder/PeerfinderManager.h>
 | 
			
		||||
#include <ripple/resource/ResourceManager.h>
 | 
			
		||||
#include <boost/asio/ip/tcp.hpp>
 | 
			
		||||
@@ -37,6 +37,7 @@
 | 
			
		||||
#include <boost/asio/strand.hpp>
 | 
			
		||||
#include <boost/asio/basic_waitable_timer.hpp>
 | 
			
		||||
#include <boost/container/flat_map.hpp>
 | 
			
		||||
#include <boost/optional.hpp>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
@@ -111,7 +112,7 @@ private:
 | 
			
		||||
    Resource::Manager& m_resourceManager;
 | 
			
		||||
    std::unique_ptr <PeerFinder::Manager> m_peerFinder;
 | 
			
		||||
    TrafficCount m_traffic;
 | 
			
		||||
    hash_map <PeerFinder::Slot::ptr,
 | 
			
		||||
    hash_map <std::shared_ptr<PeerFinder::Slot>,
 | 
			
		||||
        std::weak_ptr <PeerImp>> m_peers;
 | 
			
		||||
    hash_map<Peer::id_t, std::weak_ptr<PeerImp>> ids_;
 | 
			
		||||
    Resolver& m_resolver;
 | 
			
		||||
@@ -128,6 +129,8 @@ private:
 | 
			
		||||
    // Peer IDs expecting to receive a last link notification
 | 
			
		||||
    std::set<std::uint32_t> csIDs_;
 | 
			
		||||
 | 
			
		||||
    boost::optional<std::uint32_t> networkID_;
 | 
			
		||||
 | 
			
		||||
    //--------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@@ -220,7 +223,7 @@ public:
 | 
			
		||||
    add_active (std::shared_ptr<PeerImp> const& peer);
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    remove (PeerFinder::Slot::ptr const& slot);
 | 
			
		||||
    remove (std::shared_ptr<PeerFinder::Slot> 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<class Fields>
 | 
			
		||||
@@ -375,11 +372,11 @@ public:
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::shared_ptr<Writer>
 | 
			
		||||
    makeRedirectResponse (PeerFinder::Slot::ptr const& slot,
 | 
			
		||||
    makeRedirectResponse (std::shared_ptr<PeerFinder::Slot> const& slot,
 | 
			
		||||
        http_request_type const& request, address_type remote_address);
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<Writer>
 | 
			
		||||
    makeErrorResponse (PeerFinder::Slot::ptr const& slot,
 | 
			
		||||
    makeErrorResponse (std::shared_ptr<PeerFinder::Slot> const& slot,
 | 
			
		||||
        http_request_type const& request, address_type remote_address,
 | 
			
		||||
        std::string msg);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@
 | 
			
		||||
#include <ripple/app/misc/Transaction.h>
 | 
			
		||||
#include <ripple/app/misc/ValidatorList.h>
 | 
			
		||||
#include <ripple/app/tx/apply.h>
 | 
			
		||||
#include <ripple/basics/base64.h>
 | 
			
		||||
#include <ripple/basics/random.h>
 | 
			
		||||
#include <ripple/basics/safe_cast.h>
 | 
			
		||||
#include <ripple/basics/UptimeClock.h>
 | 
			
		||||
@@ -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<PeerFinder::Slot> const& slot, http_request_type&& request,
 | 
			
		||||
        PublicKey const& publicKey,
 | 
			
		||||
            ProtocolVersion protocol, Resource::Consumer consumer,
 | 
			
		||||
                std::unique_ptr<beast::asio::ssl_bundle>&& 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<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>
 | 
			
		||||
    {
 | 
			
		||||
        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<uint256> closed;
 | 
			
		||||
    boost::optional<uint256> 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<std::mutex> 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<Message> 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<int>(size));
 | 
			
		||||
    return error_code{};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -969,12 +957,6 @@ PeerImp::onMessageEnd (std::uint16_t,
 | 
			
		||||
    charge (fee_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
PeerImp::onMessage (std::shared_ptr <protocol::TMHello> const& m)
 | 
			
		||||
{
 | 
			
		||||
    fail("Deprecated TMHello");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
PeerImp::onMessage (std::shared_ptr<protocol::TMManifests> const& m)
 | 
			
		||||
{
 | 
			
		||||
@@ -1366,20 +1348,6 @@ PeerImp::onMessage(std::shared_ptr <protocol::TMPeerShardInfo> const& m)
 | 
			
		||||
        overlay_.lastLink(id_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
PeerImp::onMessage (std::shared_ptr <protocol::TMGetPeers> const& m)
 | 
			
		||||
{
 | 
			
		||||
    // This message is obsolete due to PeerFinder and
 | 
			
		||||
    // we no longer provide a response to it.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
PeerImp::onMessage (std::shared_ptr <protocol::TMPeers> const& m)
 | 
			
		||||
{
 | 
			
		||||
    // This message is obsolete due to PeerFinder and
 | 
			
		||||
    // we no longer process it.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
PeerImp::onMessage (std::shared_ptr <protocol::TMEndpoints> const& m)
 | 
			
		||||
{
 | 
			
		||||
@@ -2701,7 +2669,7 @@ PeerImp::getLedger (std::shared_ptr<protocol::TMGetLedger> const& m)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Message::pointer oPacket = std::make_shared<Message> (
 | 
			
		||||
            auto oPacket = std::make_shared<Message> (
 | 
			
		||||
                reply, protocol::mtLEDGER_DATA);
 | 
			
		||||
            send (oPacket);
 | 
			
		||||
            return;
 | 
			
		||||
@@ -2806,7 +2774,7 @@ PeerImp::getLedger (std::shared_ptr<protocol::TMGetLedger> const& m)
 | 
			
		||||
        "Got request for " << packet.nodeids().size() << " nodes at depth " <<
 | 
			
		||||
        depth << ", return " << reply.nodes().size() << " nodes";
 | 
			
		||||
 | 
			
		||||
    Message::pointer oPacket = std::make_shared<Message> (
 | 
			
		||||
    auto oPacket = std::make_shared<Message> (
 | 
			
		||||
        reply, protocol::mtLEDGER_DATA);
 | 
			
		||||
    send (oPacket);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@
 | 
			
		||||
#include <ripple/beast/asio/waitable_timer.h>
 | 
			
		||||
#include <ripple/beast/utility/WrappedSink.h>
 | 
			
		||||
#include <ripple/overlay/impl/ProtocolMessage.h>
 | 
			
		||||
#include <ripple/overlay/impl/ProtocolVersion.h>
 | 
			
		||||
#include <ripple/overlay/impl/OverlayImpl.h>
 | 
			
		||||
#include <ripple/peerfinder/PeerfinderManager.h>
 | 
			
		||||
#include <ripple/protocol/Protocol.h>
 | 
			
		||||
@@ -99,9 +100,6 @@ private:
 | 
			
		||||
    using endpoint_type = boost::asio::ip::tcp::endpoint;
 | 
			
		||||
    using waitable_timer = boost::asio::basic_waitable_timer<std::chrono::steady_clock>;
 | 
			
		||||
 | 
			
		||||
    // 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> 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<PeerFinder::Slot> 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<Message::pointer> send_queue_;
 | 
			
		||||
    std::queue<std::shared_ptr<Message>> 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<PeerFinder::Slot> const& slot, http_request_type&& request,
 | 
			
		||||
            PublicKey const& publicKey,
 | 
			
		||||
                ProtocolVersion protocol, Resource::Consumer consumer,
 | 
			
		||||
                    std::unique_ptr<beast::asio::ssl_bundle>&& ssl_bundle,
 | 
			
		||||
                        OverlayImpl& overlay);
 | 
			
		||||
 | 
			
		||||
@@ -241,11 +242,10 @@ public:
 | 
			
		||||
    // VFALCO legacyPublicKey should be implied by the Slot
 | 
			
		||||
    template <class Buffers>
 | 
			
		||||
    PeerImp (Application& app, std::unique_ptr<beast::asio::ssl_bundle>&& ssl_bundle,
 | 
			
		||||
        Buffers const& buffers, PeerFinder::Slot::ptr&& slot,
 | 
			
		||||
        Buffers const& buffers, std::shared_ptr<PeerFinder::Slot>&& 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<PeerFinder::Slot> const&
 | 
			
		||||
    slot()
 | 
			
		||||
    {
 | 
			
		||||
        return slot_;
 | 
			
		||||
@@ -275,7 +275,7 @@ public:
 | 
			
		||||
    //
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    send (Message::pointer const& m) override;
 | 
			
		||||
    send (std::shared_ptr<Message> const& m) override;
 | 
			
		||||
 | 
			
		||||
    /** Send a set of PeerFinder endpoints as a protocol message. */
 | 
			
		||||
    template <class FwdIt, class = typename std::enable_if_t<std::is_same<
 | 
			
		||||
@@ -367,9 +367,6 @@ public:
 | 
			
		||||
    void
 | 
			
		||||
    cycleStatus () override;
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
    supportsVersion (int version) override;
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
    hasRange (std::uint32_t uMin, std::uint32_t uMax) override;
 | 
			
		||||
 | 
			
		||||
@@ -424,7 +421,7 @@ private:
 | 
			
		||||
 | 
			
		||||
    http_response_type
 | 
			
		||||
    makeResponse (bool crawl, http_request_type const& req,
 | 
			
		||||
        beast::IP::Endpoint remoteAddress,
 | 
			
		||||
        beast::IP::Address remote_ip,
 | 
			
		||||
        uint256 const& sharedValue);
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
@@ -465,10 +462,10 @@ public:
 | 
			
		||||
            boost::system::errc::invalid_argument);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    error_code
 | 
			
		||||
    void
 | 
			
		||||
    onMessageUnknown (std::uint16_t type);
 | 
			
		||||
 | 
			
		||||
    error_code
 | 
			
		||||
    void
 | 
			
		||||
    onMessageBegin (std::uint16_t type,
 | 
			
		||||
        std::shared_ptr <::google::protobuf::Message> 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 <protocol::TMHello> const& m);
 | 
			
		||||
    void onMessage (std::shared_ptr <protocol::TMManifests> const& m);
 | 
			
		||||
    void onMessage (std::shared_ptr <protocol::TMPing> const& m);
 | 
			
		||||
    void onMessage (std::shared_ptr <protocol::TMCluster> const& m);
 | 
			
		||||
@@ -485,8 +481,6 @@ public:
 | 
			
		||||
    void onMessage (std::shared_ptr <protocol::TMShardInfo> const& m);
 | 
			
		||||
    void onMessage (std::shared_ptr <protocol::TMGetPeerShardInfo> const& m);
 | 
			
		||||
    void onMessage (std::shared_ptr <protocol::TMPeerShardInfo> const& m);
 | 
			
		||||
    void onMessage (std::shared_ptr <protocol::TMGetPeers> const& m);
 | 
			
		||||
    void onMessage (std::shared_ptr <protocol::TMPeers> const& m);
 | 
			
		||||
    void onMessage (std::shared_ptr <protocol::TMEndpoints> const& m);
 | 
			
		||||
    void onMessage (std::shared_ptr <protocol::TMTransaction> const& m);
 | 
			
		||||
    void onMessage (std::shared_ptr <protocol::TMGetLedger> 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 <class Buffers>
 | 
			
		||||
PeerImp::PeerImp (Application& app, std::unique_ptr<beast::asio::ssl_bundle>&& ssl_bundle,
 | 
			
		||||
    Buffers const& buffers, PeerFinder::Slot::ptr&& slot,
 | 
			
		||||
    Buffers const& buffers, std::shared_ptr<PeerFinder::Slot>&& 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<beast::asio::ssl_bundle>&& 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))
 | 
			
		||||
 
 | 
			
		||||
@@ -125,8 +125,7 @@ void PeerSet::sendRequest (const protocol::TMGetLedger& tmGL)
 | 
			
		||||
    if (mPeers.empty ())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    Message::pointer packet (
 | 
			
		||||
        std::make_shared<Message> (tmGL, protocol::mtGET_LEDGER));
 | 
			
		||||
    auto packet = std::make_shared<Message>(tmGL, protocol::mtGET_LEDGER);
 | 
			
		||||
 | 
			
		||||
    for (auto id : mPeers)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
#ifndef RIPPLE_OVERLAY_PROTOCOLMESSAGE_H_INCLUDED
 | 
			
		||||
#define RIPPLE_OVERLAY_PROTOCOLMESSAGE_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/ByteUtilities.h>
 | 
			
		||||
#include <ripple/protocol/messages.h>
 | 
			
		||||
#include <ripple/overlay/Message.h>
 | 
			
		||||
#include <ripple/overlay/impl/ZeroCopyStream.h>
 | 
			
		||||
@@ -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 <class T, class Buffers, class Handler>
 | 
			
		||||
std::enable_if_t<std::is_base_of<
 | 
			
		||||
    ::google::protobuf::Message, 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 <class BufferSequence>
 | 
			
		||||
boost::optional<MessageHeader> parseMessageHeader(
 | 
			
		||||
    BufferSequence const& bufs,
 | 
			
		||||
    std::size_t size)
 | 
			
		||||
{
 | 
			
		||||
    auto iter = boost::asio::buffers_iterator<BufferSequence, std::uint8_t>::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 <class T, class Buffers, class Handler,
 | 
			
		||||
    class = std::enable_if_t<std::is_base_of<::google::protobuf::Message, T>::value>>
 | 
			
		||||
bool
 | 
			
		||||
invoke (
 | 
			
		||||
    MessageHeader const& header,
 | 
			
		||||
    Buffers const& buffers,
 | 
			
		||||
    Handler& handler)
 | 
			
		||||
{
 | 
			
		||||
    auto const m = std::make_shared<T>();
 | 
			
		||||
 | 
			
		||||
    ZeroCopyInputStream<Buffers> stream(buffers);
 | 
			
		||||
    stream.Skip(Message::kHeaderBytes);
 | 
			
		||||
    auto const m (std::make_shared<T>());
 | 
			
		||||
    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 <std::size_t, boost::system::error_code>
 | 
			
		||||
invokeProtocolMessage (Buffers const& buffers, Handler& handler)
 | 
			
		||||
{
 | 
			
		||||
    std::pair<std::size_t,boost::system::error_code> 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<protocol::TMHello> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtMANIFESTS:             ec = detail::invoke<protocol::TMManifests> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtPING:                  ec = detail::invoke<protocol::TMPing> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtCLUSTER:               ec = detail::invoke<protocol::TMCluster> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtGET_SHARD_INFO:        ec = detail::invoke<protocol::TMGetShardInfo> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtSHARD_INFO:            ec = detail::invoke<protocol::TMShardInfo>(type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtGET_PEER_SHARD_INFO:   ec = detail::invoke<protocol::TMGetPeerShardInfo> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtPEER_SHARD_INFO:       ec = detail::invoke<protocol::TMPeerShardInfo>(type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtGET_PEERS:             ec = detail::invoke<protocol::TMGetPeers> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtPEERS:                 ec = detail::invoke<protocol::TMPeers> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtENDPOINTS:             ec = detail::invoke<protocol::TMEndpoints> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtTRANSACTION:           ec = detail::invoke<protocol::TMTransaction> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtGET_LEDGER:            ec = detail::invoke<protocol::TMGetLedger> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtLEDGER_DATA:           ec = detail::invoke<protocol::TMLedgerData> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtPROPOSE_LEDGER:        ec = detail::invoke<protocol::TMProposeSet> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtSTATUS_CHANGE:         ec = detail::invoke<protocol::TMStatusChange> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtHAVE_SET:              ec = detail::invoke<protocol::TMHaveTransactionSet> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtVALIDATION:            ec = detail::invoke<protocol::TMValidation> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtGET_OBJECTS:           ec = detail::invoke<protocol::TMGetObjectByHash> (type, buffers, handler); break;
 | 
			
		||||
    case protocol::mtMANIFESTS:
 | 
			
		||||
        success = detail::invoke<protocol::TMManifests>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtPING:
 | 
			
		||||
        success = detail::invoke<protocol::TMPing>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtCLUSTER:
 | 
			
		||||
        success = detail::invoke<protocol::TMCluster>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtGET_SHARD_INFO:
 | 
			
		||||
        success = detail::invoke<protocol::TMGetShardInfo>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtSHARD_INFO:
 | 
			
		||||
        success = detail::invoke<protocol::TMShardInfo>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtGET_PEER_SHARD_INFO:
 | 
			
		||||
        success = detail::invoke<protocol::TMGetPeerShardInfo>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtPEER_SHARD_INFO:
 | 
			
		||||
        success = detail::invoke<protocol::TMPeerShardInfo>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtENDPOINTS:
 | 
			
		||||
        success = detail::invoke<protocol::TMEndpoints>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtTRANSACTION:
 | 
			
		||||
        success = detail::invoke<protocol::TMTransaction>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtGET_LEDGER:
 | 
			
		||||
        success = detail::invoke<protocol::TMGetLedger>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtLEDGER_DATA:
 | 
			
		||||
        success = detail::invoke<protocol::TMLedgerData>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtPROPOSE_LEDGER:
 | 
			
		||||
        success = detail::invoke<protocol::TMProposeSet>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtSTATUS_CHANGE:
 | 
			
		||||
        success = detail::invoke<protocol::TMStatusChange>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtHAVE_SET:
 | 
			
		||||
        success = detail::invoke<protocol::TMHaveTransactionSet>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtVALIDATION:
 | 
			
		||||
        success = detail::invoke<protocol::TMValidation>(*header, buffers, handler);
 | 
			
		||||
        break;
 | 
			
		||||
    case protocol::mtGET_OBJECTS:
 | 
			
		||||
        success = detail::invoke<protocol::TMGetObjectByHash>(*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 <class Streambuf>
 | 
			
		||||
void
 | 
			
		||||
write (Streambuf& streambuf,
 | 
			
		||||
    ::google::protobuf::Message const& m, int type,
 | 
			
		||||
        std::size_t blockBytes)
 | 
			
		||||
{
 | 
			
		||||
    auto const size = m.ByteSize();
 | 
			
		||||
    std::array<std::uint8_t, 6> v;
 | 
			
		||||
    v[0] = static_cast<std::uint8_t>((size >> 24) & 0xFF);
 | 
			
		||||
    v[1] = static_cast<std::uint8_t>((size >> 16) & 0xFF);
 | 
			
		||||
    v[2] = static_cast<std::uint8_t>((size >>  8) & 0xFF);
 | 
			
		||||
    v[3] = static_cast<std::uint8_t>( size        & 0xFF);
 | 
			
		||||
    v[4] = static_cast<std::uint8_t>((type >>  8) & 0xFF);
 | 
			
		||||
    v[5] = static_cast<std::uint8_t>( type        & 0xFF);
 | 
			
		||||
    streambuf.commit(boost::asio::buffer_copy(
 | 
			
		||||
        streambuf.prepare(Message::kHeaderBytes),
 | 
			
		||||
            boost::asio::buffer(v)));
 | 
			
		||||
    ZeroCopyOutputStream<Streambuf> stream (
 | 
			
		||||
        streambuf, blockBytes);
 | 
			
		||||
    m.SerializeToZeroCopyStream(&stream);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // ripple
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										184
									
								
								src/ripple/overlay/impl/ProtocolVersion.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								src/ripple/overlay/impl/ProtocolVersion.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -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 <ripple/overlay/impl/ProtocolVersion.h>
 | 
			
		||||
#include <ripple/beast/core/LexicalCast.h>
 | 
			
		||||
#include <ripple/beast/rfc2616.h>
 | 
			
		||||
#include <boost/function_output_iterator.hpp>
 | 
			
		||||
#include <boost/regex.hpp>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
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<ProtocolVersion>
 | 
			
		||||
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<ProtocolVersion> 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<ProtocolVersion>
 | 
			
		||||
negotiateProtocolVersion(boost::beast::string_view const& versions)
 | 
			
		||||
{
 | 
			
		||||
    auto const them = parseProtocolVersions(versions);
 | 
			
		||||
 | 
			
		||||
    boost::optional<ProtocolVersion> 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<void(ProtocolVersion const&)> 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								src/ripple/overlay/impl/ProtocolVersion.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/ripple/overlay/impl/ProtocolVersion.h
									
									
									
									
									
										Normal file
									
								
							@@ -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 <boost/beast/core/string.hpp>
 | 
			
		||||
#include <boost/optional.hpp>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
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<std::uint16_t, std::uint16_t>;
 | 
			
		||||
 | 
			
		||||
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<ProtocolVersion>
 | 
			
		||||
parseProtocolVersions(boost::beast::string_view const& s);
 | 
			
		||||
 | 
			
		||||
/** Given a list of supported protocol versions, choose the one we prefer. */
 | 
			
		||||
boost::optional<ProtocolVersion>
 | 
			
		||||
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
 | 
			
		||||
@@ -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 <ripple/overlay/impl/TMHello.h>
 | 
			
		||||
#include <ripple/app/ledger/LedgerMaster.h>
 | 
			
		||||
#include <ripple/app/main/Application.h>
 | 
			
		||||
#include <ripple/basics/base64.h>
 | 
			
		||||
#include <ripple/basics/safe_cast.h>
 | 
			
		||||
#include <ripple/beast/rfc2616.h>
 | 
			
		||||
#include <ripple/beast/core/LexicalCast.h>
 | 
			
		||||
#include <ripple/protocol/digest.h>
 | 
			
		||||
#include <boost/regex.hpp>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
// 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<base_uint<512>>
 | 
			
		||||
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<uint256>
 | 
			
		||||
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<ProtocolVersion>
 | 
			
		||||
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<ProtocolVersion> 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<protocol::TMHello>
 | 
			
		||||
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<std::uint32_t>(versions.back().first) << 16) |
 | 
			
		||||
            (safe_cast<std::uint32_t>(versions.back().second)));
 | 
			
		||||
        hello.set_protoversionmin(
 | 
			
		||||
            (safe_cast<std::uint32_t>(versions.front().first) << 16) |
 | 
			
		||||
            (safe_cast<std::uint32_t>(versions.front().second)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        // Required
 | 
			
		||||
        auto const iter = h.find ("Public-Key");
 | 
			
		||||
        if (iter == h.end())
 | 
			
		||||
            return boost::none;
 | 
			
		||||
        auto const pk = parseBase58<PublicKey>(
 | 
			
		||||
            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<PublicKey>
 | 
			
		||||
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<std::int64_t>(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<PublicKey>(
 | 
			
		||||
        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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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 <ripple/protocol/messages.h>
 | 
			
		||||
#include <ripple/app/main/Application.h>
 | 
			
		||||
#include <ripple/beast/utility/Journal.h>
 | 
			
		||||
#include <ripple/protocol/BuildInfo.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/beast/http/message.hpp>
 | 
			
		||||
#include <boost/asio/ssl.hpp>
 | 
			
		||||
#include <boost/optional.hpp>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
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<uint256>
 | 
			
		||||
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<protocol::TMHello>
 | 
			
		||||
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<PublicKey>
 | 
			
		||||
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<ProtocolVersion>
 | 
			
		||||
parse_ProtocolVersions(boost::beast::string_view const& s);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -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) ||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,9 +32,9 @@ struct send_always
 | 
			
		||||
{
 | 
			
		||||
    using return_type = void;
 | 
			
		||||
 | 
			
		||||
    Message::pointer const& msg;
 | 
			
		||||
    std::shared_ptr<Message> const& msg;
 | 
			
		||||
 | 
			
		||||
    send_always(Message::pointer const& m)
 | 
			
		||||
    send_always(std::shared_ptr<Message> const& m)
 | 
			
		||||
        : msg(m)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
@@ -52,10 +52,10 @@ struct send_if_pred
 | 
			
		||||
{
 | 
			
		||||
    using return_type = void;
 | 
			
		||||
 | 
			
		||||
    Message::pointer const& msg;
 | 
			
		||||
    std::shared_ptr<Message> const& msg;
 | 
			
		||||
    Predicate const& predicate;
 | 
			
		||||
 | 
			
		||||
    send_if_pred(Message::pointer const& m, Predicate const& p)
 | 
			
		||||
    send_if_pred(std::shared_ptr<Message> 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 <typename Predicate>
 | 
			
		||||
send_if_pred<Predicate> send_if (
 | 
			
		||||
    Message::pointer const& m,
 | 
			
		||||
    std::shared_ptr<Message> const& m,
 | 
			
		||||
        Predicate const &f)
 | 
			
		||||
{
 | 
			
		||||
    return send_if_pred<Predicate>(m, f);
 | 
			
		||||
@@ -83,10 +83,10 @@ struct send_if_not_pred
 | 
			
		||||
{
 | 
			
		||||
    using return_type = void;
 | 
			
		||||
 | 
			
		||||
    Message::pointer const& msg;
 | 
			
		||||
    std::shared_ptr<Message> const& msg;
 | 
			
		||||
    Predicate const& predicate;
 | 
			
		||||
 | 
			
		||||
    send_if_not_pred(Message::pointer const& m, Predicate const& p)
 | 
			
		||||
    send_if_not_pred(std::shared_ptr<Message> 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 <typename Predicate>
 | 
			
		||||
send_if_not_pred<Predicate> send_if_not (
 | 
			
		||||
    Message::pointer const& m,
 | 
			
		||||
    std::shared_ptr<Message> const& m,
 | 
			
		||||
        Predicate const &f)
 | 
			
		||||
{
 | 
			
		||||
    return send_if_not_pred<Predicate>(m, f);
 | 
			
		||||
 
 | 
			
		||||
@@ -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<Slot> 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<Slot> 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<Slot> 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<Slot> 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<Slot> 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<Slot> 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<Slot> const& slot,
 | 
			
		||||
        PublicKey const& key, bool reserved) = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns a set of endpoints suitable for redirection. */
 | 
			
		||||
    virtual
 | 
			
		||||
    std::vector <Endpoint>
 | 
			
		||||
    redirect (Slot::ptr const& slot) = 0;
 | 
			
		||||
    redirect (std::shared_ptr<Slot> const& slot) = 0;
 | 
			
		||||
 | 
			
		||||
    /** Return a set of addresses we should connect to. */
 | 
			
		||||
    virtual
 | 
			
		||||
@@ -235,7 +235,7 @@ public:
 | 
			
		||||
    autoconnect() = 0;
 | 
			
		||||
 | 
			
		||||
    virtual
 | 
			
		||||
    std::vector<std::pair<Slot::ptr, std::vector<Endpoint>>>
 | 
			
		||||
    std::vector<std::pair<std::shared_ptr<Slot>, std::vector<Endpoint>>>
 | 
			
		||||
    buildEndpointsForPeers() = 0;
 | 
			
		||||
 | 
			
		||||
    /** Perform periodic activity.
 | 
			
		||||
 
 | 
			
		||||
@@ -573,10 +573,10 @@ public:
 | 
			
		||||
        return none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<std::pair<Slot::ptr, std::vector<Endpoint>>>
 | 
			
		||||
    std::vector<std::pair<std::shared_ptr<Slot>, std::vector<Endpoint>>>
 | 
			
		||||
    buildEndpointsForPeers()
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<std::pair<Slot::ptr, std::vector<Endpoint>>> result;
 | 
			
		||||
        std::vector<std::pair<std::shared_ptr<Slot>, std::vector<Endpoint>>> result;
 | 
			
		||||
 | 
			
		||||
        std::lock_guard _(lock_);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -119,7 +119,7 @@ public:
 | 
			
		||||
 | 
			
		||||
    //--------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    Slot::ptr
 | 
			
		||||
    std::shared_ptr<Slot>
 | 
			
		||||
    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<Slot>
 | 
			
		||||
    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<Slot> const& slot,
 | 
			
		||||
        Endpoints const& endpoints)  override
 | 
			
		||||
    {
 | 
			
		||||
        SlotImp::ptr impl (std::dynamic_pointer_cast <SlotImp> (slot));
 | 
			
		||||
@@ -142,14 +142,14 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    on_closed (Slot::ptr const& slot)  override
 | 
			
		||||
    on_closed (std::shared_ptr<Slot> const& slot)  override
 | 
			
		||||
    {
 | 
			
		||||
        SlotImp::ptr impl (std::dynamic_pointer_cast <SlotImp> (slot));
 | 
			
		||||
        m_logic.on_closed (impl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    on_failure (Slot::ptr const& slot)  override
 | 
			
		||||
    on_failure (std::shared_ptr<Slot> const& slot)  override
 | 
			
		||||
    {
 | 
			
		||||
        SlotImp::ptr impl (std::dynamic_pointer_cast <SlotImp> (slot));
 | 
			
		||||
        m_logic.on_failure (impl);
 | 
			
		||||
@@ -165,7 +165,7 @@ public:
 | 
			
		||||
    //--------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
    onConnected (Slot::ptr const& slot,
 | 
			
		||||
    onConnected (std::shared_ptr<Slot> const& slot,
 | 
			
		||||
        beast::IP::Endpoint const& local_endpoint) override
 | 
			
		||||
    {
 | 
			
		||||
        SlotImp::ptr impl (std::dynamic_pointer_cast <SlotImp> (slot));
 | 
			
		||||
@@ -173,14 +173,15 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Result
 | 
			
		||||
    activate (Slot::ptr const& slot, PublicKey const& key, bool reserved) override
 | 
			
		||||
    activate (std::shared_ptr<Slot> const& slot,
 | 
			
		||||
        PublicKey const& key, bool reserved) override
 | 
			
		||||
    {
 | 
			
		||||
        SlotImp::ptr impl (std::dynamic_pointer_cast <SlotImp> (slot));
 | 
			
		||||
        return m_logic.activate (impl, key, reserved);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector <Endpoint>
 | 
			
		||||
    redirect (Slot::ptr const& slot) override
 | 
			
		||||
    redirect (std::shared_ptr<Slot> const& slot) override
 | 
			
		||||
    {
 | 
			
		||||
        SlotImp::ptr impl (std::dynamic_pointer_cast <SlotImp> (slot));
 | 
			
		||||
        return m_logic.redirect (impl);
 | 
			
		||||
@@ -198,7 +199,7 @@ public:
 | 
			
		||||
        m_logic.once_per_second();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<std::pair<Slot::ptr, std::vector<Endpoint>>>
 | 
			
		||||
    std::vector<std::pair<std::shared_ptr<Slot>, std::vector<Endpoint>>>
 | 
			
		||||
    buildEndpointsForPeers() override
 | 
			
		||||
    {
 | 
			
		||||
        return m_logic.buildEndpointsForPeers();
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
    // <available>          = 10;
 | 
			
		||||
    // <available>          = 11;
 | 
			
		||||
    // <available>          = 14;
 | 
			
		||||
    // <available>          = 20;
 | 
			
		||||
    // <available>          = 21;
 | 
			
		||||
    // <available>          = 22;
 | 
			
		||||
    // <available>          = 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
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,6 @@
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
/** Describes a Ripple/RTXP protocol version. */
 | 
			
		||||
using ProtocolVersion = std::pair<std::uint16_t, std::uint16_t>;
 | 
			
		||||
 | 
			
		||||
/** 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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 <string>
 | 
			
		||||
#include <boost/optional.hpp>
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
#define RIPPLE_PROTOCOL_PUBLICKEY_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/Slice.h>
 | 
			
		||||
#include <ripple/crypto/KeyType.h> // move to protocol/
 | 
			
		||||
#include <ripple/protocol/KeyType.h>
 | 
			
		||||
#include <ripple/protocol/STExchange.h>
 | 
			
		||||
#include <ripple/protocol/tokens.h>
 | 
			
		||||
#include <ripple/protocol/UintTypes.h>
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/Buffer.h>
 | 
			
		||||
#include <ripple/basics/Slice.h>
 | 
			
		||||
#include <ripple/crypto/KeyType.h> // move to protocol/
 | 
			
		||||
#include <ripple/protocol/KeyType.h>
 | 
			
		||||
#include <ripple/protocol/PublicKey.h>
 | 
			
		||||
#include <ripple/protocol/Seed.h>
 | 
			
		||||
#include <ripple/protocol/tokens.h>
 | 
			
		||||
 
 | 
			
		||||
@@ -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<std::uint16_t> ((version >> 16) & 0xffff),
 | 
			
		||||
        static_cast<std::uint16_t> (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<std::uint32_t> (p.first) << 16) + p.second;
 | 
			
		||||
}
 | 
			
		||||
} // BuildInfo
 | 
			
		||||
 | 
			
		||||
} // ripple
 | 
			
		||||
 
 | 
			
		||||
@@ -18,10 +18,10 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/strHex.h>
 | 
			
		||||
#include <ripple/crypto/KeyType.h>
 | 
			
		||||
#include <ripple/net/RPCErr.h>
 | 
			
		||||
#include <ripple/protocol/ErrorCodes.h>
 | 
			
		||||
#include <ripple/protocol/jss.h>
 | 
			
		||||
#include <ripple/protocol/KeyType.h>
 | 
			
		||||
#include <ripple/protocol/PublicKey.h>
 | 
			
		||||
#include <ripple/protocol/SecretKey.h>
 | 
			
		||||
#include <ripple/protocol/Seed.h>
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -18,10 +18,11 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <ripple/overlay/impl/Handshake.cpp>
 | 
			
		||||
#include <ripple/overlay/impl/PeerImp.cpp>
 | 
			
		||||
#include <ripple/overlay/impl/PeerReservationTable.cpp>
 | 
			
		||||
#include <ripple/overlay/impl/PeerSet.cpp>
 | 
			
		||||
#include <ripple/overlay/impl/TMHello.cpp>
 | 
			
		||||
#include <ripple/overlay/impl/ProtocolVersion.cpp>
 | 
			
		||||
#include <ripple/overlay/impl/TrafficCount.cpp>
 | 
			
		||||
 | 
			
		||||
#if DOXYGEN
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,9 @@
 | 
			
		||||
#ifndef RIPPLE_TEST_JTX_ACCOUNT_H_INCLUDED
 | 
			
		||||
#define RIPPLE_TEST_JTX_ACCOUNT_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <ripple/protocol/KeyType.h>
 | 
			
		||||
#include <ripple/protocol/SecretKey.h>
 | 
			
		||||
#include <ripple/protocol/UintTypes.h>
 | 
			
		||||
#include <ripple/crypto/KeyType.h>
 | 
			
		||||
#include <ripple/beast/hash/uhash.h>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <string>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										97
									
								
								src/test/overlay/ProtocolVersion_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/test/overlay/ProtocolVersion_test.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -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 <ripple/overlay/impl/ProtocolVersion.h>
 | 
			
		||||
#include <ripple/beast/unit_test.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
class ProtocolVersion_test : public beast::unit_test::suite
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    template <class FwdIt>
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -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 <ripple/overlay/impl/TMHello.h>
 | 
			
		||||
#include <ripple/beast/unit_test.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
class TMHello_test : public beast::unit_test::suite
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    template <class FwdIt>
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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 <ripple/protocol/BuildInfo.h>
 | 
			
		||||
#include <ripple/beast/core/SemanticVersion.h>
 | 
			
		||||
#include <ripple/beast/unit_test.h>
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
@@ -18,6 +18,6 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <test/overlay/ProtocolVersion_test.cpp>
 | 
			
		||||
#include <test/overlay/cluster_test.cpp>
 | 
			
		||||
#include <test/overlay/short_read_test.cpp>
 | 
			
		||||
#include <test/overlay/TMHello_test.cpp>
 | 
			
		||||
@@ -18,7 +18,6 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <test/protocol/BuildInfo_test.cpp>
 | 
			
		||||
#include <test/protocol/digest_test.cpp>
 | 
			
		||||
#include <test/protocol/InnerObjectFormats_test.cpp>
 | 
			
		||||
#include <test/protocol/IOUAmount_test.cpp>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user