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