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  default:
81  break;
82  }
83  return "unknown";
84 }
85 
86 namespace detail {
87 
89 {
95 
98 
101 
104 
107 
113 };
114 
115 template <typename BufferSequence>
116 auto
117 buffersBegin(BufferSequence const& bufs)
118 {
119  return boost::asio::buffers_iterator<BufferSequence, std::uint8_t>::begin(
120  bufs);
121 }
122 
130 template <class BufferSequence>
131 boost::optional<MessageHeader>
133  boost::system::error_code& ec,
134  BufferSequence const& bufs,
135  std::size_t size)
136 {
137  using namespace ripple::compression;
138 
139  MessageHeader hdr;
140  auto iter = buffersBegin(bufs);
141 
142  // Check valid header
143  if (*iter & 0x80)
144  {
146 
147  // not enough bytes to parse the header
148  if (size < hdr.header_size)
149  {
150  ec = make_error_code(boost::system::errc::success);
151  return boost::none;
152  }
153 
154  if (*iter & 0x0C)
155  {
156  ec = make_error_code(boost::system::errc::protocol_error);
157  return boost::none;
158  }
159 
160  hdr.algorithm = static_cast<compression::Algorithm>(*iter);
161 
163  {
164  ec = make_error_code(boost::system::errc::protocol_error);
165  return boost::none;
166  }
167 
168  for (int i = 0; i != 4; ++i)
169  hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++;
170 
171  // clear the top four bits (the compression bits).
172  hdr.payload_wire_size &= 0x0FFFFFFF;
173 
175 
176  for (int i = 0; i != 2; ++i)
177  hdr.message_type = (hdr.message_type << 8) + *iter++;
178 
179  for (int i = 0; i != 4; ++i)
180  hdr.uncompressed_size = (hdr.uncompressed_size << 8) + *iter++;
181 
182  return hdr;
183  }
184 
185  if ((*iter & 0xFC) == 0)
186  {
187  hdr.header_size = headerBytes;
188 
189  if (size < hdr.header_size)
190  {
191  ec = make_error_code(boost::system::errc::success);
192  return boost::none;
193  }
194 
195  hdr.algorithm = Algorithm::None;
196 
197  for (int i = 0; i != 4; ++i)
198  hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++;
199 
202 
203  for (int i = 0; i != 2; ++i)
204  hdr.message_type = (hdr.message_type << 8) + *iter++;
205 
206  return hdr;
207  }
208 
209  ec = make_error_code(boost::system::errc::no_message);
210  return boost::none;
211 }
212 
213 template <
214  class T,
215  class Buffers,
216  class Handler,
217  class = std::enable_if_t<
219 bool
220 invoke(MessageHeader const& header, Buffers const& buffers, Handler& handler)
221 {
222  auto const m = std::make_shared<T>();
223 
224  ZeroCopyInputStream<Buffers> stream(buffers);
225  stream.Skip(header.header_size);
226 
228  {
230  payload.resize(header.uncompressed_size);
231 
232  auto const payloadSize = ripple::compression::decompress(
233  stream,
234  header.payload_wire_size,
235  payload.data(),
236  header.uncompressed_size,
237  header.algorithm);
238 
239  if (payloadSize == 0 || !m->ParseFromArray(payload.data(), payloadSize))
240  return false;
241  }
242  else if (!m->ParseFromZeroCopyStream(&stream))
243  return false;
244 
245  handler.onMessageBegin(header.message_type, m, header.payload_wire_size);
246  handler.onMessage(m);
247  handler.onMessageEnd(header.message_type, m);
248 
249  return true;
250 }
251 
252 } // namespace detail
253 
261 template <class Buffers, class Handler>
263 invokeProtocolMessage(Buffers const& buffers, Handler& handler)
264 {
266 
267  auto const size = boost::asio::buffer_size(buffers);
268 
269  if (size == 0)
270  return result;
271 
272  auto header = detail::parseMessageHeader(result.second, buffers, size);
273 
274  // If we can't parse the header then it may be that we don't have enough
275  // bytes yet, or because the message was cut off (if error_code is success).
276  // Otherwise we failed to match the header's marker (error_code is set to
277  // no_message) or the compression algorithm is invalid (error_code is
278  // protocol_error) and signal an error.
279  if (!header)
280  return result;
281 
282  // We implement a maximum size for protocol messages. Sending a message
283  // whose size exceeds this may result in the connection being dropped. A
284  // larger message size may be supported in the future or negotiated as
285  // part of a protocol upgrade.
286  if (header->payload_wire_size > megabytes(64) ||
287  header->uncompressed_size > megabytes(64))
288  {
289  result.second = make_error_code(boost::system::errc::message_size);
290  return result;
291  }
292 
293  // We requested uncompressed messages from the peer but received compressed.
294  if (!handler.compressionEnabled() &&
295  header->algorithm != compression::Algorithm::None)
296  {
297  result.second = make_error_code(boost::system::errc::protocol_error);
298  return result;
299  }
300 
301  // We don't have the whole message yet. This isn't an error but we have
302  // nothing to do.
303  if (header->total_wire_size > size)
304  return result;
305 
306  bool success;
307 
308  switch (header->message_type)
309  {
310  case protocol::mtMANIFESTS:
311  success = detail::invoke<protocol::TMManifests>(
312  *header, buffers, handler);
313  break;
314  case protocol::mtPING:
315  success =
316  detail::invoke<protocol::TMPing>(*header, buffers, handler);
317  break;
318  case protocol::mtCLUSTER:
319  success =
320  detail::invoke<protocol::TMCluster>(*header, buffers, handler);
321  break;
322  case protocol::mtGET_SHARD_INFO:
323  success = detail::invoke<protocol::TMGetShardInfo>(
324  *header, buffers, handler);
325  break;
326  case protocol::mtSHARD_INFO:
327  success = detail::invoke<protocol::TMShardInfo>(
328  *header, buffers, handler);
329  break;
330  case protocol::mtGET_PEER_SHARD_INFO:
331  success = detail::invoke<protocol::TMGetPeerShardInfo>(
332  *header, buffers, handler);
333  break;
334  case protocol::mtPEER_SHARD_INFO:
335  success = detail::invoke<protocol::TMPeerShardInfo>(
336  *header, buffers, handler);
337  break;
338  case protocol::mtENDPOINTS:
339  success = detail::invoke<protocol::TMEndpoints>(
340  *header, buffers, handler);
341  break;
342  case protocol::mtTRANSACTION:
343  success = detail::invoke<protocol::TMTransaction>(
344  *header, buffers, handler);
345  break;
346  case protocol::mtGET_LEDGER:
347  success = detail::invoke<protocol::TMGetLedger>(
348  *header, buffers, handler);
349  break;
350  case protocol::mtLEDGER_DATA:
351  success = detail::invoke<protocol::TMLedgerData>(
352  *header, buffers, handler);
353  break;
354  case protocol::mtPROPOSE_LEDGER:
355  success = detail::invoke<protocol::TMProposeSet>(
356  *header, buffers, handler);
357  break;
358  case protocol::mtSTATUS_CHANGE:
359  success = detail::invoke<protocol::TMStatusChange>(
360  *header, buffers, handler);
361  break;
362  case protocol::mtHAVE_SET:
363  success = detail::invoke<protocol::TMHaveTransactionSet>(
364  *header, buffers, handler);
365  break;
366  case protocol::mtVALIDATION:
367  success = detail::invoke<protocol::TMValidation>(
368  *header, buffers, handler);
369  break;
370  case protocol::mtVALIDATORLIST:
371  success = detail::invoke<protocol::TMValidatorList>(
372  *header, buffers, handler);
373  break;
374  case protocol::mtGET_OBJECTS:
375  success = detail::invoke<protocol::TMGetObjectByHash>(
376  *header, buffers, handler);
377  break;
378  default:
379  handler.onMessageUnknown(header->message_type);
380  success = true;
381  break;
382  }
383 
384  result.first = header->total_wire_size;
385 
386  if (!success)
387  result.second = make_error_code(boost::system::errc::bad_message);
388 
389  return result;
390 }
391 
392 } // namespace ripple
393 
394 #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:106
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:100
ripple::detail::invoke
bool invoke(MessageHeader const &header, Buffers const &buffers, Handler &handler)
Definition: ProtocolMessage.h:220
std::pair
ripple::detail::MessageHeader::header_size
std::uint32_t header_size
The size of the header associated with this message.
Definition: ProtocolMessage.h:97
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:117
ripple::detail::MessageHeader::algorithm
compression::Algorithm algorithm
Indicates which compression algorithm the payload is compressed with.
Definition: ProtocolMessage.h:112
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:103
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:94
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:132
ripple::detail::MessageHeader
Definition: ProtocolMessage.h:88
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:263
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