rippled
ProtocolVersion.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2019 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/overlay/impl/ProtocolVersion.h>
21 #include <ripple/beast/core/LexicalCast.h>
22 #include <ripple/beast/rfc2616.h>
23 #include <boost/function_output_iterator.hpp>
24 #include <boost/regex.hpp>
25 #include <algorithm>
26 #include <functional>
27 
28 namespace ripple {
29 
35 constexpr
37 {
38  { 1, 2 },
39  { 2, 0 },
40  { 2, 1 }
41 };
42 
43 // This ugly construct ensures that supportedProtocolList is sorted in strictly
44 // ascending order and doesn't contain any duplicates.
45 // FIXME: With C++20 we can use std::is_sorted with an appropriate comparator
46 static_assert(
47  []() constexpr -> bool
48  {
49  auto const len = std::distance(
52 
53  // There should be at least one protocol we're willing to speak.
54  if (len == 0)
55  return false;
56 
57  // A list with only one entry is, by definition, sorted so we don't
58  // need to check it.
59  if (len != 1)
60  {
61  for (auto i = 0; i != len - 1; ++i)
62  {
64  return false;
65  }
66  }
67 
68  return true;
69  }(), "The list of supported protocols isn't properly sorted.");
70 
71 
74 {
75  // The legacy protocol uses a different name. This can be removed when we
76  // migrate away from it and require 2.0 or later.
77  if (p == ProtocolVersion{ 1, 2 })
78  return "RTXP/1.2";
79 
80  return "XRPL/" + std::to_string(p.first) + "." + std::to_string(p.second);
81 }
82 
84 parseProtocolVersions(boost::beast::string_view const& value)
85 {
86  static boost::regex re(
87  "^" // start of line
88  "XRPL/" // The string "XRPL/"
89  "([2-9]|(?:[1-9][0-9]+))" // a number (greater than 2 with no leading zeroes)
90  "\\." // a period
91  "(0|(?:[1-9][0-9]*))" // a number (no leading zeroes unless exactly zero)
92  "$" // The end of the string
93  , boost::regex_constants::optimize);
94 
96 
97  for (auto const& s : beast::rfc2616::split_commas(value))
98  {
99  if (s == "RTXP/1.2")
100  {
101  result.push_back(make_protocol(1, 2));
102  continue;
103  }
104 
105  boost::smatch m;
106 
107  if (boost::regex_match(s, m, re))
108  {
109  std::uint16_t major;
110  std::uint16_t minor;
111  if (!beast::lexicalCastChecked(major, std::string(m[1])))
112  continue;
113 
114  if (!beast::lexicalCastChecked(minor, std::string(m[2])))
115  continue;
116 
117  auto const proto = make_protocol(major, minor);
118 
119  // This is an extra sanity check: we check that the protocol we just
120  // decoded corresponds to the token we were parsing.
121  if (to_string(proto) == s)
122  result.push_back(make_protocol(major, minor));
123  }
124  }
125 
126  // We guarantee that the returned list is sorted and contains no duplicates:
127  std::sort(result.begin(), result.end());
128  result.erase(std::unique(result.begin(), result.end()), result.end());
129 
130  return result;
131 }
132 
133 boost::optional<ProtocolVersion>
135 {
136  boost::optional<ProtocolVersion> result;
137 
138  // The protocol version we want to negotiate is the largest item in the
139  // intersection of the versions supported by us and the peer. Since the
140  // output of std::set_intersection is sorted, that item is always going
141  // to be the last one. So we get a little clever and avoid the need for
142  // a container:
143  std::function<void(ProtocolVersion const&)> pickVersion =
144  [&result](ProtocolVersion const& v)
145  {
146  result = v;
147  };
148 
150  std::begin(versions), std::end(versions),
152  boost::make_function_output_iterator(pickVersion));
153 
154  return result;
155 }
156 
157 boost::optional<ProtocolVersion>
158 negotiateProtocolVersion(boost::beast::string_view const& versions)
159 {
160  auto const them = parseProtocolVersions(versions);
161 
162  return negotiateProtocolVersion(them);
163 }
164 
165 std::string const&
167 {
168  static std::string const supported = []()
169  {
170  std::string ret;
171  for (auto const& v : supportedProtocolList)
172  {
173  if (!ret.empty())
174  ret += ", ";
175  ret += to_string(v);
176  }
177 
178  return ret;
179  }();
180 
181  return supported;
182 }
183 
184 bool
186 {
189 }
190 
191 }
std::string
STL class.
functional
std::pair
std::vector
STL class.
std::find
T find(T... args)
ripple::make_protocol
constexpr ProtocolVersion make_protocol(std::uint16_t major, std::uint16_t minor)
Definition: ProtocolVersion.h:42
std::distance
T distance(T... args)
ripple::parseProtocolVersions
std::vector< ProtocolVersion > parseProtocolVersions(boost::beast::string_view const &value)
Parse a set of protocol versions.
Definition: ProtocolVersion.cpp:84
ripple::ProtocolVersion
std::pair< std::uint16_t, std::uint16_t > ProtocolVersion
Represents a particular version of the peer-to-peer protocol.
Definition: ProtocolVersion.h:37
std::function
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
std::sort
T sort(T... args)
algorithm
ripple::negotiateProtocolVersion
boost::optional< ProtocolVersion > negotiateProtocolVersion(std::vector< ProtocolVersion > const &versions)
Given a list of supported protocol versions, choose the one we prefer.
Definition: ProtocolVersion.cpp:134
std::vector::push_back
T push_back(T... args)
ripple::supportedProtocolList
constexpr const ProtocolVersion supportedProtocolList[]
The list of protocol versions we speak and we prefer to use.
Definition: ProtocolVersion.cpp:37
std::to_string
T to_string(T... args)
std::vector::erase
T erase(T... args)
std::uint16_t
beast::rfc2616::split_commas
Result split_commas(FwdIt first, FwdIt last)
Definition: rfc2616.h:282
ripple::supportedProtocolVersions
std::string const & supportedProtocolVersions()
The list of all the protocol versions we support.
Definition: ProtocolVersion.cpp:166
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
beast::lexicalCastChecked
bool lexicalCastChecked(Out &out, In in)
Intelligently convert from one type to another.
Definition: LexicalCast.h:262
std::begin
T begin(T... args)
ripple::isProtocolSupported
bool isProtocolSupported(ProtocolVersion const &v)
Determine whether we support a specific protocol version.
Definition: ProtocolVersion.cpp:185
std::string::empty
T empty(T... args)
std::unique
T unique(T... args)
std::end
T end(T... args)
std::set_intersection
T set_intersection(T... args)