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
145  if (*iter & 0x80)
146  {
148 
149  // not enough bytes to parse the header
150  if (size < hdr.header_size)
151  {
152  ec = make_error_code(boost::system::errc::success);
153  return boost::none;
154  }
155 
156  if (*iter & 0x0C)
157  {
158  ec = make_error_code(boost::system::errc::protocol_error);
159  return boost::none;
160  }
161 
162  hdr.algorithm = static_cast<compression::Algorithm>(*iter);
163 
165  {
166  ec = make_error_code(boost::system::errc::protocol_error);
167  return boost::none;
168  }
169 
170  for (int i = 0; i != 4; ++i)
171  hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++;
172 
173  // clear the top four bits (the compression bits).
174  hdr.payload_wire_size &= 0x0FFFFFFF;
175 
177 
178  for (int i = 0; i != 2; ++i)
179  hdr.message_type = (hdr.message_type << 8) + *iter++;
180 
181  for (int i = 0; i != 4; ++i)
182  hdr.uncompressed_size = (hdr.uncompressed_size << 8) + *iter++;
183 
184  return hdr;
185  }
186 
187  if ((*iter & 0xFC) == 0)
188  {
189  hdr.header_size = headerBytes;
190 
191  if (size < hdr.header_size)
192  {
193  ec = make_error_code(boost::system::errc::success);
194  return boost::none;
195  }
196 
197  hdr.algorithm = Algorithm::None;
198 
199  for (int i = 0; i != 4; ++i)
200  hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++;
201 
204 
205  for (int i = 0; i != 2; ++i)
206  hdr.message_type = (hdr.message_type << 8) + *iter++;
207 
208  return hdr;
209  }
210 
211  ec = make_error_code(boost::system::errc::no_message);
212  return boost::none;
213 }
214 
215 template <
216  class T,
217  class Buffers,
218  class Handler,
219  class = std::enable_if_t<
221 bool
222 invoke(MessageHeader const& header, Buffers const& buffers, Handler& handler)
223 {
224  auto const m = std::make_shared<T>();
225 
226  ZeroCopyInputStream<Buffers> stream(buffers);
227  stream.Skip(header.header_size);
228 
230  {
232  payload.resize(header.uncompressed_size);
233 
234  auto const payloadSize = ripple::compression::decompress(
235  stream,
236  header.payload_wire_size,
237  payload.data(),
238  header.uncompressed_size,
239  header.algorithm);
240 
241  if (payloadSize == 0 || !m->ParseFromArray(payload.data(), payloadSize))
242  return false;
243  }
244  else if (!m->ParseFromZeroCopyStream(&stream))
245  return false;
246 
247  handler.onMessageBegin(header.message_type, m, header.payload_wire_size);
248  handler.onMessage(m);
249  handler.onMessageEnd(header.message_type, m);
250 
251  return true;
252 }
253 
254 } // namespace detail
255 
268 template <class Buffers, class Handler>
271  Buffers const& buffers,
272  Handler& handler,
273  std::size_t& hint)
274 {
276 
277  auto const size = boost::asio::buffer_size(buffers);
278 
279  if (size == 0)
280  return result;
281 
282  auto header = detail::parseMessageHeader(result.second, buffers, size);
283 
284  // If we can't parse the header then it may be that we don't have enough
285  // bytes yet, or because the message was cut off (if error_code is success).
286  // Otherwise we failed to match the header's marker (error_code is set to
287  // no_message) or the compression algorithm is invalid (error_code is
288  // protocol_error) and signal an error.
289  if (!header)
290  return result;
291 
292  // We implement a maximum size for protocol messages. Sending a message
293  // whose size exceeds this may result in the connection being dropped. A
294  // larger message size may be supported in the future or negotiated as
295  // part of a protocol upgrade.
296  if (header->payload_wire_size > megabytes(64) ||
297  header->uncompressed_size > megabytes(64))
298  {
299  result.second = make_error_code(boost::system::errc::message_size);
300  return result;
301  }
302 
303  // We requested uncompressed messages from the peer but received compressed.
304  if (!handler.compressionEnabled() &&
305  header->algorithm != compression::Algorithm::None)
306  {
307  result.second = make_error_code(boost::system::errc::protocol_error);
308  return result;
309  }
310 
311  // We don't have the whole message yet. This isn't an error but we have
312  // nothing to do.
313  if (header->total_wire_size > size)
314  {
315  hint = header->total_wire_size - size;
316  return result;
317  }
318 
319  bool success;
320 
321  switch (header->message_type)
322  {
323  case protocol::mtMANIFESTS:
324  success = detail::invoke<protocol::TMManifests>(
325  *header, buffers, handler);
326  break;
327  case protocol::mtPING:
328  success =
329  detail::invoke<protocol::TMPing>(*header, buffers, handler);
330  break;
331  case protocol::mtCLUSTER:
332  success =
333  detail::invoke<protocol::TMCluster>(*header, buffers, handler);
334  break;
335  case protocol::mtGET_SHARD_INFO:
336  success = detail::invoke<protocol::TMGetShardInfo>(
337  *header, buffers, handler);
338  break;
339  case protocol::mtSHARD_INFO:
340  success = detail::invoke<protocol::TMShardInfo>(
341  *header, buffers, handler);
342  break;
343  case protocol::mtGET_PEER_SHARD_INFO:
344  success = detail::invoke<protocol::TMGetPeerShardInfo>(
345  *header, buffers, handler);
346  break;
347  case protocol::mtPEER_SHARD_INFO:
348  success = detail::invoke<protocol::TMPeerShardInfo>(
349  *header, buffers, handler);
350  break;
351  case protocol::mtENDPOINTS:
352  success = detail::invoke<protocol::TMEndpoints>(
353  *header, buffers, handler);
354  break;
355  case protocol::mtTRANSACTION:
356  success = detail::invoke<protocol::TMTransaction>(
357  *header, buffers, handler);
358  break;
359  case protocol::mtGET_LEDGER:
360  success = detail::invoke<protocol::TMGetLedger>(
361  *header, buffers, handler);
362  break;
363  case protocol::mtLEDGER_DATA:
364  success = detail::invoke<protocol::TMLedgerData>(
365  *header, buffers, handler);
366  break;
367  case protocol::mtPROPOSE_LEDGER:
368  success = detail::invoke<protocol::TMProposeSet>(
369  *header, buffers, handler);
370  break;
371  case protocol::mtSTATUS_CHANGE:
372  success = detail::invoke<protocol::TMStatusChange>(
373  *header, buffers, handler);
374  break;
375  case protocol::mtHAVE_SET:
376  success = detail::invoke<protocol::TMHaveTransactionSet>(
377  *header, buffers, handler);
378  break;
379  case protocol::mtVALIDATION:
380  success = detail::invoke<protocol::TMValidation>(
381  *header, buffers, handler);
382  break;
383  case protocol::mtVALIDATORLIST:
384  success = detail::invoke<protocol::TMValidatorList>(
385  *header, buffers, handler);
386  break;
387  case protocol::mtGET_OBJECTS:
388  success = detail::invoke<protocol::TMGetObjectByHash>(
389  *header, buffers, handler);
390  break;
391  case protocol::mtSQUELCH:
392  success =
393  detail::invoke<protocol::TMSquelch>(*header, buffers, handler);
394  break;
395  default:
396  handler.onMessageUnknown(header->message_type);
397  success = true;
398  break;
399  }
400 
401  result.first = header->total_wire_size;
402 
403  if (!success)
404  result.second = make_error_code(boost::system::errc::bad_message);
405 
406  return result;
407 }
408 
409 } // namespace ripple
410 
411 #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:222
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:270
std::vector::data
T data(T... args)
type_traits
std::is_base_of
ripple::compression::Algorithm::None
@ None