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 <type_traits>
36 #include <vector>
37 
38 namespace ripple {
39 
41 template <class = void>
44 {
45  switch (type)
46  {
47  case protocol::mtMANIFESTS:
48  return "manifests";
49  case protocol::mtPING:
50  return "ping";
51  case protocol::mtCLUSTER:
52  return "cluster";
53  case protocol::mtGET_SHARD_INFO:
54  return "get_shard_info";
55  case protocol::mtSHARD_INFO:
56  return "shard_info";
57  case protocol::mtGET_PEER_SHARD_INFO:
58  return "get_peer_shard_info";
59  case protocol::mtPEER_SHARD_INFO:
60  return "peer_shard_info";
61  case protocol::mtENDPOINTS:
62  return "endpoints";
63  case protocol::mtTRANSACTION:
64  return "tx";
65  case protocol::mtGET_LEDGER:
66  return "get_ledger";
67  case protocol::mtLEDGER_DATA:
68  return "ledger_data";
69  case protocol::mtPROPOSE_LEDGER:
70  return "propose";
71  case protocol::mtSTATUS_CHANGE:
72  return "status";
73  case protocol::mtHAVE_SET:
74  return "have_set";
75  case protocol::mtVALIDATORLIST:
76  return "validator_list";
77  case protocol::mtVALIDATORLISTCOLLECTION:
78  return "validator_list_collection";
79  case protocol::mtVALIDATION:
80  return "validation";
81  case protocol::mtGET_OBJECTS:
82  return "get_objects";
83  case protocol::mtSQUELCH:
84  return "squelch";
85  default:
86  break;
87  }
88  return "unknown";
89 }
90 
91 namespace detail {
92 
94 {
100 
103 
106 
109 
112 
118 };
119 
120 template <typename BufferSequence>
121 auto
122 buffersBegin(BufferSequence const& bufs)
123 {
124  return boost::asio::buffers_iterator<BufferSequence, std::uint8_t>::begin(
125  bufs);
126 }
127 
128 template <typename BufferSequence>
129 auto
130 buffersEnd(BufferSequence const& bufs)
131 {
132  return boost::asio::buffers_iterator<BufferSequence, std::uint8_t>::end(
133  bufs);
134 }
135 
145 template <class BufferSequence>
148  boost::system::error_code& ec,
149  BufferSequence const& bufs,
150  std::size_t size)
151 {
152  using namespace ripple::compression;
153 
154  MessageHeader hdr;
155  auto iter = buffersBegin(bufs);
156  assert(iter != buffersEnd(bufs));
157 
158  // Check valid header compressed message:
159  // - 4 bits are the compression algorithm, 1st bit is always set to 1
160  // - 2 bits are always set to 0
161  // - 26 bits are the payload size
162  // - 32 bits are the uncompressed data size
163  if (*iter & 0x80)
164  {
166 
167  // not enough bytes to parse the header
168  if (size < hdr.header_size)
169  {
170  ec = make_error_code(boost::system::errc::success);
171  return std::nullopt;
172  }
173 
174  if (*iter & 0x0C)
175  {
176  ec = make_error_code(boost::system::errc::protocol_error);
177  return std::nullopt;
178  }
179 
180  hdr.algorithm = static_cast<compression::Algorithm>(*iter & 0xF0);
181 
183  {
184  ec = make_error_code(boost::system::errc::protocol_error);
185  return std::nullopt;
186  }
187 
188  for (int i = 0; i != 4; ++i)
189  hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++;
190 
191  // clear the top four bits (the compression bits).
192  hdr.payload_wire_size &= 0x0FFFFFFF;
193 
195 
196  for (int i = 0; i != 2; ++i)
197  hdr.message_type = (hdr.message_type << 8) + *iter++;
198 
199  for (int i = 0; i != 4; ++i)
200  hdr.uncompressed_size = (hdr.uncompressed_size << 8) + *iter++;
201 
202  return hdr;
203  }
204 
205  // Check valid header uncompressed message:
206  // - 6 bits are set to 0
207  // - 26 bits are the payload size
208  if ((*iter & 0xFC) == 0)
209  {
210  hdr.header_size = headerBytes;
211 
212  if (size < hdr.header_size)
213  {
214  ec = make_error_code(boost::system::errc::success);
215  return std::nullopt;
216  }
217 
218  hdr.algorithm = Algorithm::None;
219 
220  for (int i = 0; i != 4; ++i)
221  hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++;
222 
225 
226  for (int i = 0; i != 2; ++i)
227  hdr.message_type = (hdr.message_type << 8) + *iter++;
228 
229  return hdr;
230  }
231 
232  ec = make_error_code(boost::system::errc::no_message);
233  return std::nullopt;
234 }
235 
236 template <
237  class T,
238  class Buffers,
239  class = std::enable_if_t<
242 parseMessageContent(MessageHeader const& header, Buffers const& buffers)
243 {
244  auto const m = std::make_shared<T>();
245 
246  ZeroCopyInputStream<Buffers> stream(buffers);
247  stream.Skip(header.header_size);
248 
250  {
252  payload.resize(header.uncompressed_size);
253 
254  auto const payloadSize = ripple::compression::decompress(
255  stream,
256  header.payload_wire_size,
257  payload.data(),
258  header.uncompressed_size,
259  header.algorithm);
260 
261  if (payloadSize == 0 || !m->ParseFromArray(payload.data(), payloadSize))
262  return {};
263  }
264  else if (!m->ParseFromZeroCopyStream(&stream))
265  return {};
266 
267  return m;
268 }
269 
270 template <
271  class T,
272  class Buffers,
273  class Handler,
274  class = std::enable_if_t<
276 bool
277 invoke(MessageHeader const& header, Buffers const& buffers, Handler& handler)
278 {
279  auto const m = parseMessageContent<T>(header, buffers);
280  if (!m)
281  return false;
282 
283  using namespace ripple::compression;
284  handler.onMessageBegin(
285  header.message_type,
286  m,
287  header.payload_wire_size,
288  header.uncompressed_size,
289  header.algorithm != Algorithm::None);
290  handler.onMessage(m);
291  handler.onMessageEnd(header.message_type, m);
292 
293  return true;
294 }
295 
296 } // namespace detail
297 
310 template <class Buffers, class Handler>
313  Buffers const& buffers,
314  Handler& handler,
315  std::size_t& hint)
316 {
318 
319  auto const size = boost::asio::buffer_size(buffers);
320 
321  if (size == 0)
322  return result;
323 
324  auto header = detail::parseMessageHeader(result.second, buffers, size);
325 
326  // If we can't parse the header then it may be that we don't have enough
327  // bytes yet, or because the message was cut off (if error_code is success).
328  // Otherwise we failed to match the header's marker (error_code is set to
329  // no_message) or the compression algorithm is invalid (error_code is
330  // protocol_error) and signal an error.
331  if (!header)
332  return result;
333 
334  // We implement a maximum size for protocol messages. Sending a message
335  // whose size exceeds this may result in the connection being dropped. A
336  // larger message size may be supported in the future or negotiated as
337  // part of a protocol upgrade.
338  if (header->payload_wire_size > maximiumMessageSize ||
339  header->uncompressed_size > maximiumMessageSize)
340  {
341  result.second = make_error_code(boost::system::errc::message_size);
342  return result;
343  }
344 
345  // We requested uncompressed messages from the peer but received compressed.
346  if (!handler.compressionEnabled() &&
347  header->algorithm != compression::Algorithm::None)
348  {
349  result.second = make_error_code(boost::system::errc::protocol_error);
350  return result;
351  }
352 
353  // We don't have the whole message yet. This isn't an error but we have
354  // nothing to do.
355  if (header->total_wire_size > size)
356  {
357  hint = header->total_wire_size - size;
358  return result;
359  }
360 
361  bool success;
362 
363  switch (header->message_type)
364  {
365  case protocol::mtMANIFESTS:
366  success = detail::invoke<protocol::TMManifests>(
367  *header, buffers, handler);
368  break;
369  case protocol::mtPING:
370  success =
371  detail::invoke<protocol::TMPing>(*header, buffers, handler);
372  break;
373  case protocol::mtCLUSTER:
374  success =
375  detail::invoke<protocol::TMCluster>(*header, buffers, handler);
376  break;
377  case protocol::mtGET_SHARD_INFO:
378  success = detail::invoke<protocol::TMGetShardInfo>(
379  *header, buffers, handler);
380  break;
381  case protocol::mtSHARD_INFO:
382  success = detail::invoke<protocol::TMShardInfo>(
383  *header, buffers, handler);
384  break;
385  case protocol::mtGET_PEER_SHARD_INFO:
386  success = detail::invoke<protocol::TMGetPeerShardInfo>(
387  *header, buffers, handler);
388  break;
389  case protocol::mtPEER_SHARD_INFO:
390  success = detail::invoke<protocol::TMPeerShardInfo>(
391  *header, buffers, handler);
392  break;
393  case protocol::mtENDPOINTS:
394  success = detail::invoke<protocol::TMEndpoints>(
395  *header, buffers, handler);
396  break;
397  case protocol::mtTRANSACTION:
398  success = detail::invoke<protocol::TMTransaction>(
399  *header, buffers, handler);
400  break;
401  case protocol::mtGET_LEDGER:
402  success = detail::invoke<protocol::TMGetLedger>(
403  *header, buffers, handler);
404  break;
405  case protocol::mtLEDGER_DATA:
406  success = detail::invoke<protocol::TMLedgerData>(
407  *header, buffers, handler);
408  break;
409  case protocol::mtPROPOSE_LEDGER:
410  success = detail::invoke<protocol::TMProposeSet>(
411  *header, buffers, handler);
412  break;
413  case protocol::mtSTATUS_CHANGE:
414  success = detail::invoke<protocol::TMStatusChange>(
415  *header, buffers, handler);
416  break;
417  case protocol::mtHAVE_SET:
418  success = detail::invoke<protocol::TMHaveTransactionSet>(
419  *header, buffers, handler);
420  break;
421  case protocol::mtVALIDATION:
422  success = detail::invoke<protocol::TMValidation>(
423  *header, buffers, handler);
424  break;
425  case protocol::mtVALIDATORLIST:
426  success = detail::invoke<protocol::TMValidatorList>(
427  *header, buffers, handler);
428  break;
429  case protocol::mtVALIDATORLISTCOLLECTION:
430  success = detail::invoke<protocol::TMValidatorListCollection>(
431  *header, buffers, handler);
432  break;
433  case protocol::mtGET_OBJECTS:
434  success = detail::invoke<protocol::TMGetObjectByHash>(
435  *header, buffers, handler);
436  break;
437  case protocol::mtSQUELCH:
438  success =
439  detail::invoke<protocol::TMSquelch>(*header, buffers, handler);
440  break;
441  default:
442  handler.onMessageUnknown(header->message_type);
443  success = true;
444  break;
445  }
446 
447  result.first = header->total_wire_size;
448 
449  if (!success)
450  result.second = make_error_code(boost::system::errc::bad_message);
451 
452  return result;
453 }
454 
455 } // namespace ripple
456 
457 #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:111
ripple::detail::parseMessageContent
std::shared_ptr< T > parseMessageContent(MessageHeader const &header, Buffers const &buffers)
Definition: ProtocolMessage.h:242
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:105
ripple::detail::invoke
bool invoke(MessageHeader const &header, Buffers const &buffers, Handler &handler)
Definition: ProtocolMessage.h:277
std::pair
ripple::detail::MessageHeader::header_size
std::uint32_t header_size
The size of the header associated with this message.
Definition: ProtocolMessage.h:102
vector
ripple::compression::headerBytes
constexpr std::size_t headerBytes
Definition: Compression.h:31
ripple::detail::buffersEnd
auto buffersEnd(BufferSequence const &bufs)
Definition: ProtocolMessage.h:130
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:122
ripple::detail::MessageHeader::algorithm
compression::Algorithm algorithm
Indicates which compression algorithm the payload is compressed with.
Definition: ProtocolMessage.h:117
ripple::protocolMessageName
std::string protocolMessageName(int type)
Returns the name of a protocol message given its type.
Definition: ProtocolMessage.h:43
ripple::detail::MessageHeader::uncompressed_size
std::uint32_t uncompressed_size
Uncompressed message size if the message is compressed.
Definition: ProtocolMessage.h:108
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:99
ripple::detail::MessageHeader
Definition: ProtocolMessage.h:93
cstdint
std::uint32_t
ripple::compression
Definition: Compression.h:29
ripple::compression::Algorithm
Algorithm
Definition: Compression.h:36
memory
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:312
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:147
std::is_base_of
ripple::compression::Algorithm::None
@ None