rippled
ProtocolMessage.h
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 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 #ifndef RIPPLE_OVERLAY_PROTOCOLMESSAGE_H_INCLUDED
21 #define RIPPLE_OVERLAY_PROTOCOLMESSAGE_H_INCLUDED
22 
23 #include <ripple/basics/ByteUtilities.h>
24 #include <ripple/overlay/Compression.h>
25 #include <ripple/overlay/Message.h>
26 #include <ripple/overlay/impl/ZeroCopyStream.h>
27 #include <ripple/protocol/messages.h>
28 #include <boost/asio/buffer.hpp>
29 #include <boost/asio/buffers_iterator.hpp>
30 #include <boost/system/error_code.hpp>
31 #include <cassert>
32 #include <cstdint>
33 #include <memory>
34 #include <optional>
35 #include <ripple.pb.h>
36 #include <type_traits>
37 #include <vector>
38 
39 namespace ripple {
40 
41 inline protocol::MessageType
42 protocolMessageType(protocol::TMGetLedger const&)
43 {
44  return protocol::mtGET_LEDGER;
45 }
46 
47 inline protocol::MessageType
48 protocolMessageType(protocol::TMReplayDeltaRequest const&)
49 {
50  return protocol::mtREPLAY_DELTA_REQ;
51 }
52 
53 inline protocol::MessageType
54 protocolMessageType(protocol::TMProofPathRequest const&)
55 {
56  return protocol::mtPROOF_PATH_REQ;
57 }
58 
60 template <class = void>
63 {
64  switch (type)
65  {
66  case protocol::mtMANIFESTS:
67  return "manifests";
68  case protocol::mtPING:
69  return "ping";
70  case protocol::mtCLUSTER:
71  return "cluster";
72  case protocol::mtGET_SHARD_INFO:
73  return "get_shard_info";
74  case protocol::mtSHARD_INFO:
75  return "shard_info";
76  case protocol::mtGET_PEER_SHARD_INFO:
77  return "get_peer_shard_info";
78  case protocol::mtPEER_SHARD_INFO:
79  return "peer_shard_info";
80  case protocol::mtENDPOINTS:
81  return "endpoints";
82  case protocol::mtTRANSACTION:
83  return "tx";
84  case protocol::mtGET_LEDGER:
85  return "get_ledger";
86  case protocol::mtLEDGER_DATA:
87  return "ledger_data";
88  case protocol::mtPROPOSE_LEDGER:
89  return "propose";
90  case protocol::mtSTATUS_CHANGE:
91  return "status";
92  case protocol::mtHAVE_SET:
93  return "have_set";
94  case protocol::mtVALIDATORLIST:
95  return "validator_list";
96  case protocol::mtVALIDATORLISTCOLLECTION:
97  return "validator_list_collection";
98  case protocol::mtVALIDATION:
99  return "validation";
100  case protocol::mtGET_OBJECTS:
101  return "get_objects";
102  case protocol::mtSQUELCH:
103  return "squelch";
104  case protocol::mtPROOF_PATH_REQ:
105  return "proof_path_request";
106  case protocol::mtPROOF_PATH_RESPONSE:
107  return "proof_path_response";
108  case protocol::mtREPLAY_DELTA_REQ:
109  return "replay_delta_request";
110  case protocol::mtREPLAY_DELTA_RESPONSE:
111  return "replay_delta_response";
112  default:
113  break;
114  }
115  return "unknown";
116 }
117 
118 namespace detail {
119 
121 {
127 
130 
133 
136 
139 
145 };
146 
147 template <typename BufferSequence>
148 auto
149 buffersBegin(BufferSequence const& bufs)
150 {
151  return boost::asio::buffers_iterator<BufferSequence, std::uint8_t>::begin(
152  bufs);
153 }
154 
155 template <typename BufferSequence>
156 auto
157 buffersEnd(BufferSequence const& bufs)
158 {
159  return boost::asio::buffers_iterator<BufferSequence, std::uint8_t>::end(
160  bufs);
161 }
162 
172 template <class BufferSequence>
175  boost::system::error_code& ec,
176  BufferSequence const& bufs,
177  std::size_t size)
178 {
179  using namespace ripple::compression;
180 
181  MessageHeader hdr;
182  auto iter = buffersBegin(bufs);
183  assert(iter != buffersEnd(bufs));
184 
185  // Check valid header compressed message:
186  // - 4 bits are the compression algorithm, 1st bit is always set to 1
187  // - 2 bits are always set to 0
188  // - 26 bits are the payload size
189  // - 32 bits are the uncompressed data size
190  if (*iter & 0x80)
191  {
193 
194  // not enough bytes to parse the header
195  if (size < hdr.header_size)
196  {
197  ec = make_error_code(boost::system::errc::success);
198  return std::nullopt;
199  }
200 
201  if (*iter & 0x0C)
202  {
203  ec = make_error_code(boost::system::errc::protocol_error);
204  return std::nullopt;
205  }
206 
207  hdr.algorithm = static_cast<compression::Algorithm>(*iter & 0xF0);
208 
210  {
211  ec = make_error_code(boost::system::errc::protocol_error);
212  return std::nullopt;
213  }
214 
215  for (int i = 0; i != 4; ++i)
216  hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++;
217 
218  // clear the top four bits (the compression bits).
219  hdr.payload_wire_size &= 0x0FFFFFFF;
220 
222 
223  for (int i = 0; i != 2; ++i)
224  hdr.message_type = (hdr.message_type << 8) + *iter++;
225 
226  for (int i = 0; i != 4; ++i)
227  hdr.uncompressed_size = (hdr.uncompressed_size << 8) + *iter++;
228 
229  return hdr;
230  }
231 
232  // Check valid header uncompressed message:
233  // - 6 bits are set to 0
234  // - 26 bits are the payload size
235  if ((*iter & 0xFC) == 0)
236  {
237  hdr.header_size = headerBytes;
238 
239  if (size < hdr.header_size)
240  {
241  ec = make_error_code(boost::system::errc::success);
242  return std::nullopt;
243  }
244 
245  hdr.algorithm = Algorithm::None;
246 
247  for (int i = 0; i != 4; ++i)
248  hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++;
249 
252 
253  for (int i = 0; i != 2; ++i)
254  hdr.message_type = (hdr.message_type << 8) + *iter++;
255 
256  return hdr;
257  }
258 
259  ec = make_error_code(boost::system::errc::no_message);
260  return std::nullopt;
261 }
262 
263 template <
264  class T,
265  class Buffers,
266  class = std::enable_if_t<
269 parseMessageContent(MessageHeader const& header, Buffers const& buffers)
270 {
271  auto const m = std::make_shared<T>();
272 
273  ZeroCopyInputStream<Buffers> stream(buffers);
274  stream.Skip(header.header_size);
275 
277  {
279  payload.resize(header.uncompressed_size);
280 
281  auto const payloadSize = ripple::compression::decompress(
282  stream,
283  header.payload_wire_size,
284  payload.data(),
285  header.uncompressed_size,
286  header.algorithm);
287 
288  if (payloadSize == 0 || !m->ParseFromArray(payload.data(), payloadSize))
289  return {};
290  }
291  else if (!m->ParseFromZeroCopyStream(&stream))
292  return {};
293 
294  return m;
295 }
296 
297 template <
298  class T,
299  class Buffers,
300  class Handler,
301  class = std::enable_if_t<
303 bool
304 invoke(MessageHeader const& header, Buffers const& buffers, Handler& handler)
305 {
306  auto const m = parseMessageContent<T>(header, buffers);
307  if (!m)
308  return false;
309 
310  using namespace ripple::compression;
311  handler.onMessageBegin(
312  header.message_type,
313  m,
314  header.payload_wire_size,
315  header.uncompressed_size,
316  header.algorithm != Algorithm::None);
317  handler.onMessage(m);
318  handler.onMessageEnd(header.message_type, m);
319 
320  return true;
321 }
322 
323 } // namespace detail
324 
337 template <class Buffers, class Handler>
340  Buffers const& buffers,
341  Handler& handler,
342  std::size_t& hint)
343 {
345 
346  auto const size = boost::asio::buffer_size(buffers);
347 
348  if (size == 0)
349  return result;
350 
351  auto header = detail::parseMessageHeader(result.second, buffers, size);
352 
353  // If we can't parse the header then it may be that we don't have enough
354  // bytes yet, or because the message was cut off (if error_code is success).
355  // Otherwise we failed to match the header's marker (error_code is set to
356  // no_message) or the compression algorithm is invalid (error_code is
357  // protocol_error) and signal an error.
358  if (!header)
359  return result;
360 
361  // We implement a maximum size for protocol messages. Sending a message
362  // whose size exceeds this may result in the connection being dropped. A
363  // larger message size may be supported in the future or negotiated as
364  // part of a protocol upgrade.
365  if (header->payload_wire_size > maximiumMessageSize ||
366  header->uncompressed_size > maximiumMessageSize)
367  {
368  result.second = make_error_code(boost::system::errc::message_size);
369  return result;
370  }
371 
372  // We requested uncompressed messages from the peer but received compressed.
373  if (!handler.compressionEnabled() &&
374  header->algorithm != compression::Algorithm::None)
375  {
376  result.second = make_error_code(boost::system::errc::protocol_error);
377  return result;
378  }
379 
380  // We don't have the whole message yet. This isn't an error but we have
381  // nothing to do.
382  if (header->total_wire_size > size)
383  {
384  hint = header->total_wire_size - size;
385  return result;
386  }
387 
388  bool success;
389 
390  switch (header->message_type)
391  {
392  case protocol::mtMANIFESTS:
393  success = detail::invoke<protocol::TMManifests>(
394  *header, buffers, handler);
395  break;
396  case protocol::mtPING:
397  success =
398  detail::invoke<protocol::TMPing>(*header, buffers, handler);
399  break;
400  case protocol::mtCLUSTER:
401  success =
402  detail::invoke<protocol::TMCluster>(*header, buffers, handler);
403  break;
404  case protocol::mtGET_SHARD_INFO:
405  success = detail::invoke<protocol::TMGetShardInfo>(
406  *header, buffers, handler);
407  break;
408  case protocol::mtSHARD_INFO:
409  success = detail::invoke<protocol::TMShardInfo>(
410  *header, buffers, handler);
411  break;
412  case protocol::mtGET_PEER_SHARD_INFO:
413  success = detail::invoke<protocol::TMGetPeerShardInfo>(
414  *header, buffers, handler);
415  break;
416  case protocol::mtPEER_SHARD_INFO:
417  success = detail::invoke<protocol::TMPeerShardInfo>(
418  *header, buffers, handler);
419  break;
420  case protocol::mtENDPOINTS:
421  success = detail::invoke<protocol::TMEndpoints>(
422  *header, buffers, handler);
423  break;
424  case protocol::mtTRANSACTION:
425  success = detail::invoke<protocol::TMTransaction>(
426  *header, buffers, handler);
427  break;
428  case protocol::mtGET_LEDGER:
429  success = detail::invoke<protocol::TMGetLedger>(
430  *header, buffers, handler);
431  break;
432  case protocol::mtLEDGER_DATA:
433  success = detail::invoke<protocol::TMLedgerData>(
434  *header, buffers, handler);
435  break;
436  case protocol::mtPROPOSE_LEDGER:
437  success = detail::invoke<protocol::TMProposeSet>(
438  *header, buffers, handler);
439  break;
440  case protocol::mtSTATUS_CHANGE:
441  success = detail::invoke<protocol::TMStatusChange>(
442  *header, buffers, handler);
443  break;
444  case protocol::mtHAVE_SET:
445  success = detail::invoke<protocol::TMHaveTransactionSet>(
446  *header, buffers, handler);
447  break;
448  case protocol::mtVALIDATION:
449  success = detail::invoke<protocol::TMValidation>(
450  *header, buffers, handler);
451  break;
452  case protocol::mtVALIDATORLIST:
453  success = detail::invoke<protocol::TMValidatorList>(
454  *header, buffers, handler);
455  break;
456  case protocol::mtVALIDATORLISTCOLLECTION:
457  success = detail::invoke<protocol::TMValidatorListCollection>(
458  *header, buffers, handler);
459  break;
460  case protocol::mtGET_OBJECTS:
461  success = detail::invoke<protocol::TMGetObjectByHash>(
462  *header, buffers, handler);
463  break;
464  case protocol::mtSQUELCH:
465  success =
466  detail::invoke<protocol::TMSquelch>(*header, buffers, handler);
467  break;
468  case protocol::mtPROOF_PATH_REQ:
469  success = detail::invoke<protocol::TMProofPathRequest>(
470  *header, buffers, handler);
471  break;
472  case protocol::mtPROOF_PATH_RESPONSE:
473  success = detail::invoke<protocol::TMProofPathResponse>(
474  *header, buffers, handler);
475  break;
476  case protocol::mtREPLAY_DELTA_REQ:
477  success = detail::invoke<protocol::TMReplayDeltaRequest>(
478  *header, buffers, handler);
479  break;
480  case protocol::mtREPLAY_DELTA_RESPONSE:
481  success = detail::invoke<protocol::TMReplayDeltaResponse>(
482  *header, buffers, handler);
483  break;
484  default:
485  handler.onMessageUnknown(header->message_type);
486  success = true;
487  break;
488  }
489 
490  result.first = header->total_wire_size;
491 
492  if (!success)
493  result.second = make_error_code(boost::system::errc::bad_message);
494 
495  return result;
496 }
497 
498 } // namespace ripple
499 
500 #endif
std::vector::resize
T resize(T... args)
ripple::detail::MessageHeader::message_type
std::uint16_t message_type
The type of the message.
Definition: ProtocolMessage.h:138
ripple::detail::parseMessageContent
std::shared_ptr< T > parseMessageContent(MessageHeader const &header, Buffers const &buffers)
Definition: ProtocolMessage.h:269
std::string
STL class.
std::shared_ptr
STL class.
ripple::maximiumMessageSize
constexpr std::size_t maximiumMessageSize
Definition: overlay/Message.h:38
ripple::detail::MessageHeader::payload_wire_size
std::uint32_t payload_wire_size
The size of the payload on the wire.
Definition: ProtocolMessage.h:132
ripple::detail::invoke
bool invoke(MessageHeader const &header, Buffers const &buffers, Handler &handler)
Definition: ProtocolMessage.h:304
std::pair
ripple::detail::MessageHeader::header_size
std::uint32_t header_size
The size of the header associated with this message.
Definition: ProtocolMessage.h:129
vector
ripple::compression::headerBytes
constexpr std::size_t headerBytes
Definition: Compression.h:31
ripple::detail::buffersEnd
auto buffersEnd(BufferSequence const &bufs)
Definition: ProtocolMessage.h:157
ripple::compression::Algorithm::LZ4
@ LZ4
ripple::ZeroCopyInputStream
Implements ZeroCopyInputStream around a buffer sequence.
Definition: ZeroCopyStream.h:35
ripple::detail::buffersBegin
auto buffersBegin(BufferSequence const &bufs)
Definition: ProtocolMessage.h:149
ripple::detail::MessageHeader::algorithm
compression::Algorithm algorithm
Indicates which compression algorithm the payload is compressed with.
Definition: ProtocolMessage.h:144
ripple::protocolMessageName
std::string protocolMessageName(int type)
Returns the name of a protocol message given its type.
Definition: ProtocolMessage.h:62
ripple::detail::MessageHeader::uncompressed_size
std::uint32_t uncompressed_size
Uncompressed message size if the message is compressed.
Definition: ProtocolMessage.h:135
std::enable_if_t
ripple::detail::MessageHeader::total_wire_size
std::uint32_t total_wire_size
The size of the message on the wire.
Definition: ProtocolMessage.h:126
ripple::detail::MessageHeader
Definition: ProtocolMessage.h:120
cstdint
std::uint32_t
ripple::compression
Definition: Compression.h:29
ripple::compression::Algorithm
Algorithm
Definition: Compression.h:36
memory
ripple::protocolMessageType
protocol::MessageType protocolMessageType(protocol::TMGetLedger const &)
Definition: ProtocolMessage.h:42
ripple::compression::decompress
std::size_t decompress(InputStream &in, std::size_t inSize, std::uint8_t *decompressed, std::size_t decompressedSize, Algorithm algorithm=Algorithm::LZ4)
Decompress input stream.
Definition: Compression.h:50
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
cassert
optional
std::size_t
ripple::compression::headerBytesCompressed
constexpr std::size_t headerBytesCompressed
Definition: Compression.h:32
ripple::invokeProtocolMessage
std::pair< std::size_t, boost::system::error_code > invokeProtocolMessage(Buffers const &buffers, Handler &handler, std::size_t &hint)
Calls the handler for up to one protocol message in the passed buffers.
Definition: ProtocolMessage.h:339
std::vector::data
T data(T... args)
type_traits
ripple::detail::parseMessageHeader
std::optional< MessageHeader > parseMessageHeader(boost::system::error_code &ec, BufferSequence const &bufs, std::size_t size)
Parse a message header.
Definition: ProtocolMessage.h:174
std::is_base_of
ripple::compression::Algorithm::None
@ None