1#include <xrpld/overlay/detail/Handshake.h>
3#include <xrpld/app/ledger/LedgerMaster.h>
4#include <xrpld/app/main/Application.h>
5#include <xrpld/overlay/detail/ProtocolVersion.h>
7#include <xrpl/basics/Log.h>
8#include <xrpl/basics/Slice.h>
9#include <xrpl/basics/StringUtilities.h>
10#include <xrpl/basics/base64.h>
11#include <xrpl/basics/base_uint.h>
12#include <xrpl/basics/strHex.h>
13#include <xrpl/beast/core/LexicalCast.h>
14#include <xrpl/beast/net/IPAddress.h>
15#include <xrpl/beast/rfc2616.h>
16#include <xrpl/beast/utility/Journal.h>
17#include <xrpl/beast/utility/Zero.h>
18#include <xrpl/protocol/BuildInfo.h>
19#include <xrpl/protocol/KeyType.h>
20#include <xrpl/protocol/PublicKey.h>
21#include <xrpl/protocol/SecretKey.h>
22#include <xrpl/protocol/digest.h>
23#include <xrpl/protocol/tokens.h>
25#include <boost/asio/ip/address.hpp>
26#include <boost/beast/http/status.hpp>
27#include <boost/beast/http/verb.hpp>
28#include <boost/regex/v5/regex.hpp>
29#include <boost/regex/v5/regex_search.hpp>
30#include <boost/system/detail/error_code.hpp>
32#include <openssl/crypto.h>
33#include <openssl/sha.h>
34#include <openssl/ssl.h>
50std::optional<std::string>
53 auto const header = headers.find(
"X-Protocol-Ctl");
54 if (header == headers.end())
57 boost::regex
const rx(feature +
"=([^;\\s]+)");
59 if (boost::regex_search(allFeatures, match, rx))
66 boost::beast::http::fields
const& headers,
85 bool ledgerReplayEnabled,
86 bool txReduceRelayEnabled,
87 bool vpReduceRelayEnabled)
92 if (ledgerReplayEnabled)
94 if (txReduceRelayEnabled)
96 if (vpReduceRelayEnabled)
105 bool ledgerReplayEnabled,
106 bool txReduceRelayEnabled,
107 bool vpReduceRelayEnabled)
138 static constexpr std::size_t kSslMinimumFinishedLength = 12;
140 unsigned char buf[1024];
141 size_t const len =
get(ssl, buf,
sizeof(buf));
143 if (len < kSslMinimumFinishedLength)
149 SHA512(buf, len, cookie.
data());
156 auto const cookie1 =
hashLastMessage(ssl.native_handle(), SSL_get_finished);
159 JLOG(journal.
error()) <<
"Cookie generation: local setup not complete";
163 auto const cookie2 =
hashLastMessage(ssl.native_handle(), SSL_get_peer_finished);
166 JLOG(journal.
error()) <<
"Cookie generation: peer setup not complete";
170 auto const result = (*cookie1 ^ *cookie2);
174 if (result == beast::kZero)
176 JLOG(journal.
error()) <<
"Cookie generation: identical finished messages";
185 boost::beast::http::fields& h,
207 h.insert(
"Session-Signature",
base64Encode(sig.data(), sig.size()));
216 h.insert(
"Remote-IP", remoteIp.to_string());
218 if (!publicIp.is_unspecified())
219 h.insert(
"Local-IP", publicIp.to_string());
223 h.insert(
"Closed-Ledger",
strHex(cl->header().hash));
224 h.insert(
"Previous-Ledger",
strHex(cl->header().parentHash));
230 boost::beast::http::fields
const& headers,
237 if (
auto const iter = headers.find(
"Server-Domain"); iter != headers.end())
243 if (
auto const iter = headers.find(
"Network-ID"); iter != headers.end())
250 if (networkID && nid != *networkID)
254 if (
auto const iter = headers.find(
"Network-Time"); iter != headers.end())
257 TimeKeeper::duration::rep val = 0;
270 auto const tolerance = 20s;
281 auto const offset = calculateOffset(netTime, ourTime);
283 if (
abs(offset) > tolerance)
288 if (
auto const iter = headers.find(
"Public-Key"); iter != headers.end())
311 auto const iter = headers.find(
"Session-Signature");
313 if (iter == headers.end())
325 if (
auto const iter = headers.find(
"Local-IP"); iter != headers.end())
327 boost::system::error_code ec;
328 auto const localIp = boost::asio::ip::make_address(
std::string_view(iter->value()), ec);
336 "Incorrect Local-IP: " + remote.to_string() +
" instead of " + localIp.to_string());
340 if (
auto const iter = headers.find(
"Remote-IP"); iter != headers.end())
342 boost::system::error_code ec;
343 auto const remoteIp = boost::asio::ip::make_address(
std::string_view(iter->value()), ec);
352 if (remoteIp != publicIp)
355 "Incorrect Remote-IP: " + publicIp.to_string() +
" instead of " +
356 remoteIp.to_string());
368 bool ledgerReplayEnabled,
369 bool txReduceRelayEnabled,
373 m.method(boost::beast::http::verb::get);
378 m.insert(
"Connection",
"Upgrade");
379 m.insert(
"Connect-As",
"Peer");
380 m.insert(
"Crawl", crawlPublic ?
"public" :
"private");
384 comprEnabled, ledgerReplayEnabled, txReduceRelayEnabled, vpReduceRelayEnabled));
400 resp.result(boost::beast::http::status::switching_protocols);
401 resp.version(req.version());
402 resp.insert(
"Connection",
"Upgrade");
404 resp.insert(
"Connect-As",
"Peer");
406 resp.insert(
"Crawl", crawlPublic ?
"public" :
"private");
416 buildHandshake(resp, sharedValue, networkID, publicIp, remoteIp, app);
NetClock::duration duration
NetClock::time_point time_point
A generic endpoint for log messages.
virtual Config & config()=0
virtual std::uint64_t instanceID() const =0
Returns a 64-bit instance identifier, generated at startup.
virtual std::pair< PublicKey, SecretKey > const & nodeIdentity()=0
Integers of any length that is a multiple of 32-bits.
bool vpReduceRelayBaseSquelchEnable
std::shared_ptr< Ledger const > getClosedLedger()
virtual LedgerMaster & getLedgerMaster()=0
virtual TimeKeeper & getTimeKeeper()=0
An immutable linear range of bytes.
time_point now() const override
Returns the current time, using the server's clock.
T duration_cast(T... args)
bool isPublic(Address const &addr)
Returns true if the address is a public routable address.
bool isUnspecified(Address const &addr)
Returns true if the address is unspecified.
boost::asio::ip::address Address
bool tokenInList(boost::string_ref const &value, boost::string_ref const &token)
Returns true if the specified token exists in the list.
bool lexicalCastChecked(Out &out, In in)
Intelligently convert from one type to another.
std::string const & getFullVersionString()
Full server version string.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::string base64Decode(std::string_view data)
static constexpr char kDelimFeature[]
bool featureEnabled(boost::beast::http::fields const &headers, std::string const &feature)
Check if a feature is enabled.
static std::optional< BaseUInt< 512 > > hashLastMessage(SSL const *ssl, size_t(*get)(const SSL *, void *, size_t))
Hashes the latest finished message from an SSL stream.
PublicKey verifyHandshake(boost::beast::http::fields const &headers, xrpl::uint256 const &sharedValue, std::optional< std::uint32_t > networkID, beast::IP::Address publicIp, beast::IP::Address remote, Application &app)
Validate header fields necessary for upgrading the link to the peer protocol.
bool verifyDigest(PublicKey const &publicKey, uint256 const &digest, Slice const &sig, bool mustBeFullyCanonical=true) noexcept
Verify a secp256k1 signature on the digest of a message.
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
std::optional< uint256 > makeSharedValue(stream_type &ssl, beast::Journal journal)
Computes a shared value based on the SSL connection state.
std::optional< AccountID > parseBase58(std::string const &s)
Parse AccountID from checked, base58 string.
bool isProperlyFormedTomlDomain(std::string_view domain)
Determines if the given string looks like a TOML-file hosting domain.
T get(Section const §ion, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
std::string strHex(FwdIt begin, FwdIt end)
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
static constexpr char kFeatureLedgerReplay[]
boost::beast::http::request< boost::beast::http::empty_body > request_type
std::string to_string(BaseUInt< Bits, Tag > const &a)
auto makeRequest(bool crawlPublic, bool comprEnabled, bool ledgerReplayEnabled, bool txReduceRelayEnabled, bool vpReduceRelayEnabled) -> request_type
Make outbound http request.
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
static constexpr char kFeatureTxrr[]
http_response_type makeResponse(bool crawlPublic, http_request_type const &req, beast::IP::Address publicIp, beast::IP::Address remoteIp, uint256 const &sharedValue, std::optional< std::uint32_t > networkID, ProtocolVersion protocol, Application &app)
Make http response.
void buildHandshake(boost::beast::http::fields &h, xrpl::uint256 const &sharedValue, std::optional< std::uint32_t > networkID, beast::IP::Address publicIp, beast::IP::Address remoteIp, Application &app)
Insert fields headers necessary for upgrading the link to the peer protocol.
std::string base64Encode(std::uint8_t const *data, std::size_t len)
std::string makeFeaturesRequestHeader(bool comprEnabled, bool ledgerReplayEnabled, bool txReduceRelayEnabled, bool vpReduceRelayEnabled)
Make request header X-Protocol-Ctl value with supported features.
Buffer signDigest(PublicKey const &pk, SecretKey const &sk, uint256 const &digest)
Generate a signature for a message digest.
OpensslSha512Hasher sha512_hasher
constexpr Number abs(Number x) noexcept
std::string const & supportedProtocolVersions()
The list of all the protocol versions we support.
std::optional< std::string > getFeatureValue(boost::beast::http::fields const &headers, std::string const &feature)
Get feature's header value.
std::pair< std::uint16_t, std::uint16_t > ProtocolVersion
Represents a particular version of the peer-to-peer protocol.
boost::beast::ssl_stream< socket_type > stream_type
boost::beast::http::request< boost::beast::http::dynamic_body > http_request_type
std::string makeFeaturesResponseHeader(http_request_type const &headers, bool comprEnabled, bool ledgerReplayEnabled, bool txReduceRelayEnabled, bool vpReduceRelayEnabled)
Make response header X-Protocol-Ctl value with supported features.
static constexpr char kFeatureCompr[]
static constexpr char kFeatureVprr[]
bool isFeatureValue(boost::beast::http::fields const &headers, std::string const &feature, std::string const &value)
Check if a feature's value is equal to the specified value.
std::enable_if_t< std::is_same_v< T, char >||std::is_same_v< T, unsigned char >, Slice > makeSlice(std::array< T, N > const &a)
boost::beast::http::response< boost::beast::http::dynamic_body > http_response_type