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 
263 template <class Buffers, class Handler>
265 invokeProtocolMessage(Buffers const& buffers, Handler& handler)
266 {
268 
269  auto const size = boost::asio::buffer_size(buffers);
270 
271  if (size == 0)
272  return result;
273 
274  auto header = detail::parseMessageHeader(result.second, buffers, size);
275 
276  // If we can't parse the header then it may be that we don't have enough
277  // bytes yet, or because the message was cut off (if error_code is success).
278  // Otherwise we failed to match the header's marker (error_code is set to
279  // no_message) or the compression algorithm is invalid (error_code is
280  // protocol_error) and signal an error.
281  if (!header)
282  return result;
283 
284  // We implement a maximum size for protocol messages. Sending a message
285  // whose size exceeds this may result in the connection being dropped. A
286  // larger message size may be supported in the future or negotiated as
287  // part of a protocol upgrade.
288  if (header->payload_wire_size > megabytes(64) ||
289  header->uncompressed_size > megabytes(64))
290  {
291  result.second = make_error_code(boost::system::errc::message_size);
292  return result;
293  }
294 
295  // We requested uncompressed messages from the peer but received compressed.
296  if (!handler.compressionEnabled() &&
297  header->algorithm != compression::Algorithm::None)
298  {
299  result.second = make_error_code(boost::system::errc::protocol_error);
300  return result;
301  }
302 
303  // We don't have the whole message yet. This isn't an error but we have
304  // nothing to do.
305  if (header->total_wire_size > size)
306  return result;
307 
308  bool success;
309 
310  switch (header->message_type)
311  {
312  case protocol::mtMANIFESTS:
313  success = detail::invoke<protocol::TMManifests>(
314  *header, buffers, handler);
315  break;
316  case protocol::mtPING:
317  success =
318  detail::invoke<protocol::TMPing>(*header, buffers, handler);
319  break;
320  case protocol::mtCLUSTER:
321  success =
322  detail::invoke<protocol::TMCluster>(*header, buffers, handler);
323  break;
324  case protocol::mtGET_SHARD_INFO:
325  success = detail::invoke<protocol::TMGetShardInfo>(
326  *header, buffers, handler);
327  break;
328  case protocol::mtSHARD_INFO:
329  success = detail::invoke<protocol::TMShardInfo>(
330  *header, buffers, handler);
331  break;
332  case protocol::mtGET_PEER_SHARD_INFO:
333  success = detail::invoke<protocol::TMGetPeerShardInfo>(
334  *header, buffers, handler);
335  break;
336  case protocol::mtPEER_SHARD_INFO:
337  success = detail::invoke<protocol::TMPeerShardInfo>(
338  *header, buffers, handler);
339  break;
340  case protocol::mtENDPOINTS:
341  success = detail::invoke<protocol::TMEndpoints>(
342  *header, buffers, handler);
343  break;
344  case protocol::mtTRANSACTION:
345  success = detail::invoke<protocol::TMTransaction>(
346  *header, buffers, handler);
347  break;
348  case protocol::mtGET_LEDGER:
349  success = detail::invoke<protocol::TMGetLedger>(
350  *header, buffers, handler);
351  break;
352  case protocol::mtLEDGER_DATA:
353  success = detail::invoke<protocol::TMLedgerData>(
354  *header, buffers, handler);
355  break;
356  case protocol::mtPROPOSE_LEDGER:
357  success = detail::invoke<protocol::TMProposeSet>(
358  *header, buffers, handler);
359  break;
360  case protocol::mtSTATUS_CHANGE:
361  success = detail::invoke<protocol::TMStatusChange>(
362  *header, buffers, handler);
363  break;
364  case protocol::mtHAVE_SET:
365  success = detail::invoke<protocol::TMHaveTransactionSet>(
366  *header, buffers, handler);
367  break;
368  case protocol::mtVALIDATION:
369  success = detail::invoke<protocol::TMValidation>(
370  *header, buffers, handler);
371  break;
372  case protocol::mtVALIDATORLIST:
373  success = detail::invoke<protocol::TMValidatorList>(
374  *header, buffers, handler);
375  break;
376  case protocol::mtGET_OBJECTS:
377  success = detail::invoke<protocol::TMGetObjectByHash>(
378  *header, buffers, handler);
379  break;
380  case protocol::mtSQUELCH:
381  success =
382  detail::invoke<protocol::TMSquelch>(*header, buffers, handler);
383  break;
384  default:
385  handler.onMessageUnknown(header->message_type);
386  success = true;
387  break;
388  }
389 
390  result.first = header->total_wire_size;
391 
392  if (!success)
393  result.second = make_error_code(boost::system::errc::bad_message);
394 
395  return result;
396 }
397 
398 } // namespace ripple
399 
400 #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
ripple::invokeProtocolMessage
std::pair< std::size_t, boost::system::error_code > invokeProtocolMessage(Buffers const &buffers, Handler &handler)
Calls the handler for up to one protocol message in the passed buffers.
Definition: ProtocolMessage.h:265
std::size_t
ripple::compression::headerBytesCompressed
constexpr std::size_t headerBytesCompressed
Definition: Compression.h:32
std::vector::data
T data(T... args)
type_traits
std::is_base_of
ripple::compression::Algorithm::None
@ None