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/beast/core/LexicalCast.h>
21 #include <ripple/beast/rfc2616.h>
22 #include <ripple/overlay/impl/ProtocolVersion.h>
23 #include <boost/iterator/function_output_iterator.hpp>
24 #include <boost/regex.hpp>
25 #include <algorithm>
26 #include <functional>
27 
28 namespace ripple {
29 
36 // clang-format off
37 constexpr ProtocolVersion const supportedProtocolList[]
38 {
39  {2, 1},
40  {2, 2}
41 };
42 // clang-format on
43 
44 // This ugly construct ensures that supportedProtocolList is sorted in strictly
45 // ascending order and doesn't contain any duplicates.
46 // FIXME: With C++20 we can use std::is_sorted with an appropriate comparator
47 static_assert(
48  []() constexpr->bool {
49  auto const len = std::distance(
50  std::begin(supportedProtocolList), std::end(supportedProtocolList));
51 
52  // There should be at least one protocol we're willing to speak.
53  if (len == 0)
54  return false;
55 
56  // A list with only one entry is, by definition, sorted so we don't
57  // need to check it.
58  if (len != 1)
59  {
60  for (auto i = 0; i != len - 1; ++i)
61  {
62  if (supportedProtocolList[i] >= supportedProtocolList[i + 1])
63  return false;
64  }
65  }
66 
67  return true;
68  }(),
69  "The list of supported protocols isn't properly sorted.");
70 
72 to_string(ProtocolVersion const& p)
73 {
74  return "XRPL/" + std::to_string(p.first) + "." + std::to_string(p.second);
75 }
76 
78 parseProtocolVersions(boost::beast::string_view const& value)
79 {
80  static boost::regex re(
81  "^" // start of line
82  "XRPL/" // The string "XRPL/"
83  "([2-9]|(?:[1-9][0-9]+))" // a number (greater than 2 with no leading
84  // zeroes)
85  "\\." // a period
86  "(0|(?:[1-9][0-9]*))" // a number (no leading zeroes unless exactly
87  // zero)
88  "$" // The end of the string
89  ,
90  boost::regex_constants::optimize);
91 
93 
94  for (auto const& s : beast::rfc2616::split_commas(value))
95  {
96  boost::smatch m;
97 
98  if (boost::regex_match(s, m, re))
99  {
100  std::uint16_t major;
101  std::uint16_t minor;
102  if (!beast::lexicalCastChecked(major, std::string(m[1])))
103  continue;
104 
105  if (!beast::lexicalCastChecked(minor, std::string(m[2])))
106  continue;
107 
108  auto const proto = make_protocol(major, minor);
109 
110  // This is an extra sanity check: we check that the protocol we just
111  // decoded corresponds to the token we were parsing.
112  if (to_string(proto) == s)
113  result.push_back(make_protocol(major, minor));
114  }
115  }
116 
117  // We guarantee that the returned list is sorted and contains no duplicates:
118  std::sort(result.begin(), result.end());
119  result.erase(std::unique(result.begin(), result.end()), result.end());
120 
121  return result;
122 }
123 
126 {
128 
129  // The protocol version we want to negotiate is the largest item in the
130  // intersection of the versions supported by us and the peer. Since the
131  // output of std::set_intersection is sorted, that item is always going
132  // to be the last one. So we get a little clever and avoid the need for
133  // a container:
134  std::function<void(ProtocolVersion const&)> pickVersion =
135  [&result](ProtocolVersion const& v) { result = v; };
136 
138  std::begin(versions),
139  std::end(versions),
142  boost::make_function_output_iterator(pickVersion));
143 
144  return result;
145 }
146 
148 negotiateProtocolVersion(boost::beast::string_view const& versions)
149 {
150  auto const them = parseProtocolVersions(versions);
151 
152  return negotiateProtocolVersion(them);
153 }
154 
155 std::string const&
157 {
158  static std::string const supported = []() {
159  std::string ret;
160  for (auto const& v : supportedProtocolList)
161  {
162  if (!ret.empty())
163  ret += ", ";
164  ret += to_string(v);
165  }
166 
167  return ret;
168  }();
169 
170  return supported;
171 }
172 
173 bool
175 {
177  std::find(
180  v);
181 }
182 
183 } // namespace ripple
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:40
ripple::parseProtocolVersions
std::vector< ProtocolVersion > parseProtocolVersions(boost::beast::string_view const &value)
Parse a set of protocol versions.
Definition: ProtocolVersion.cpp:78
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
std::sort
T sort(T... args)
algorithm
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:38
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:199
ripple::supportedProtocolVersions
std::string const & supportedProtocolVersions()
The list of all the protocol versions we support.
Definition: ProtocolVersion.cpp:156
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:266
std::vector::begin
T begin(T... args)
ripple::isProtocolSupported
bool isProtocolSupported(ProtocolVersion const &v)
Determine whether we support a specific protocol version.
Definition: ProtocolVersion.cpp:174
std::string::empty
T empty(T... args)
std::unique
T unique(T... args)
std::optional
std::vector::end
T end(T... args)
std::set_intersection
T set_intersection(T... args)
ripple::negotiateProtocolVersion
std::optional< ProtocolVersion > negotiateProtocolVersion(std::vector< ProtocolVersion > const &versions)
Given a list of supported protocol versions, choose the one we prefer.
Definition: ProtocolVersion.cpp:125