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 
123 template <class BufferSequence>
124 boost::optional<MessageHeader>
125 parseMessageHeader(BufferSequence const& bufs, std::size_t size)
126 {
127  using namespace ripple::compression;
128  auto iter = buffersBegin(bufs);
129 
130  MessageHeader hdr;
131  auto const compressed = (*iter & 0x80) == 0x80;
132 
133  // Check valid header
134  if ((*iter & 0xFC) == 0 || compressed)
135  {
136  hdr.header_size = compressed ? headerBytesCompressed : headerBytes;
137 
138  if (size < hdr.header_size)
139  return {};
140 
141  if (compressed)
142  {
143  uint8_t algorithm = (*iter & 0x70) >> 4;
144  if (algorithm !=
146  return {};
148  }
149 
150  for (int i = 0; i != 4; ++i)
151  hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++;
152  // clear the compression bits
153  hdr.payload_wire_size &= 0x03FFFFFF;
154 
156 
157  for (int i = 0; i != 2; ++i)
158  hdr.message_type = (hdr.message_type << 8) + *iter++;
159 
160  if (compressed)
161  for (int i = 0; i != 4; ++i)
162  hdr.uncompressed_size = (hdr.uncompressed_size << 8) + *iter++;
163 
164  return hdr;
165  }
166 
167  return {};
168 }
169 
170 template <
171  class T,
172  class Buffers,
173  class Handler,
174  class = std::enable_if_t<
176 bool
177 invoke(MessageHeader const& header, Buffers const& buffers, Handler& handler)
178 {
179  auto const m = std::make_shared<T>();
180 
181  ZeroCopyInputStream<Buffers> stream(buffers);
182  stream.Skip(header.header_size);
183 
185  {
187  payload.resize(header.uncompressed_size);
188 
189  auto payloadSize = ripple::compression::decompress(
190  stream,
191  header.payload_wire_size,
192  payload.data(),
193  header.uncompressed_size,
194  header.algorithm);
195 
196  if (payloadSize == 0 || !m->ParseFromArray(payload.data(), payloadSize))
197  return false;
198  }
199  else if (!m->ParseFromZeroCopyStream(&stream))
200  return false;
201 
202  handler.onMessageBegin(header.message_type, m, header.payload_wire_size);
203  handler.onMessage(m);
204  handler.onMessageEnd(header.message_type, m);
205 
206  return true;
207 }
208 
209 } // namespace detail
210 
218 template <class Buffers, class Handler>
220 invokeProtocolMessage(Buffers const& buffers, Handler& handler)
221 {
223 
224  auto const size = boost::asio::buffer_size(buffers);
225 
226  if (size == 0)
227  return result;
228 
229  auto header = detail::parseMessageHeader(buffers, size);
230 
231  // If we can't parse the header then it may be that we don't have enough
232  // bytes yet, or because the message was cut off.
233  if (!header)
234  return result;
235 
236  // We implement a maximum size for protocol messages. Sending a message
237  // whose size exceeds this may result in the connection being dropped. A
238  // larger message size may be supported in the future or negotiated as
239  // part of a protocol upgrade.
240  if (header->payload_wire_size > megabytes(64))
241  {
242  result.second = make_error_code(boost::system::errc::message_size);
243  return result;
244  }
245 
246  // We don't have the whole message yet. This isn't an error but we have
247  // nothing to do.
248  if (header->total_wire_size > size)
249  return result;
250 
251  bool success;
252 
253  switch (header->message_type)
254  {
255  case protocol::mtMANIFESTS:
256  success = detail::invoke<protocol::TMManifests>(
257  *header, buffers, handler);
258  break;
259  case protocol::mtPING:
260  success =
261  detail::invoke<protocol::TMPing>(*header, buffers, handler);
262  break;
263  case protocol::mtCLUSTER:
264  success =
265  detail::invoke<protocol::TMCluster>(*header, buffers, handler);
266  break;
267  case protocol::mtGET_SHARD_INFO:
268  success = detail::invoke<protocol::TMGetShardInfo>(
269  *header, buffers, handler);
270  break;
271  case protocol::mtSHARD_INFO:
272  success = detail::invoke<protocol::TMShardInfo>(
273  *header, buffers, handler);
274  break;
275  case protocol::mtGET_PEER_SHARD_INFO:
276  success = detail::invoke<protocol::TMGetPeerShardInfo>(
277  *header, buffers, handler);
278  break;
279  case protocol::mtPEER_SHARD_INFO:
280  success = detail::invoke<protocol::TMPeerShardInfo>(
281  *header, buffers, handler);
282  break;
283  case protocol::mtENDPOINTS:
284  success = detail::invoke<protocol::TMEndpoints>(
285  *header, buffers, handler);
286  break;
287  case protocol::mtTRANSACTION:
288  success = detail::invoke<protocol::TMTransaction>(
289  *header, buffers, handler);
290  break;
291  case protocol::mtGET_LEDGER:
292  success = detail::invoke<protocol::TMGetLedger>(
293  *header, buffers, handler);
294  break;
295  case protocol::mtLEDGER_DATA:
296  success = detail::invoke<protocol::TMLedgerData>(
297  *header, buffers, handler);
298  break;
299  case protocol::mtPROPOSE_LEDGER:
300  success = detail::invoke<protocol::TMProposeSet>(
301  *header, buffers, handler);
302  break;
303  case protocol::mtSTATUS_CHANGE:
304  success = detail::invoke<protocol::TMStatusChange>(
305  *header, buffers, handler);
306  break;
307  case protocol::mtHAVE_SET:
308  success = detail::invoke<protocol::TMHaveTransactionSet>(
309  *header, buffers, handler);
310  break;
311  case protocol::mtVALIDATION:
312  success = detail::invoke<protocol::TMValidation>(
313  *header, buffers, handler);
314  break;
315  case protocol::mtVALIDATORLIST:
316  success = detail::invoke<protocol::TMValidatorList>(
317  *header, buffers, handler);
318  break;
319  case protocol::mtGET_OBJECTS:
320  success = detail::invoke<protocol::TMGetObjectByHash>(
321  *header, buffers, handler);
322  break;
323  default:
324  handler.onMessageUnknown(header->message_type);
325  success = true;
326  break;
327  }
328 
329  result.first = header->total_wire_size;
330 
331  if (!success)
332  result.second = make_error_code(boost::system::errc::bad_message);
333 
334  return result;
335 }
336 
337 } // namespace ripple
338 
339 #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:177
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::MessageHeader
Definition: ProtocolMessage.h:88
cstdint
std::uint32_t
ripple::detail::parseMessageHeader
boost::optional< MessageHeader > parseMessageHeader(BufferSequence const &bufs, std::size_t size)
Definition: ProtocolMessage.h:125
ripple::compression
Definition: Compression.h:29
ripple::compression::Algorithm
Algorithm
Definition: Compression.h:34
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:48
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:220
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