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