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/protocol/messages.h>
25 #include <ripple/overlay/Compression.h>
26 #include <ripple/overlay/Message.h>
27 #include <ripple/overlay/impl/ZeroCopyStream.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: return "manifests";
47  case protocol::mtPING: return "ping";
48  case protocol::mtCLUSTER: return "cluster";
49  case protocol::mtGET_SHARD_INFO: return "get_shard_info";
50  case protocol::mtSHARD_INFO: return "shard_info";
51  case protocol::mtGET_PEER_SHARD_INFO: return "get_peer_shard_info";
52  case protocol::mtPEER_SHARD_INFO: return "peer_shard_info";
53  case protocol::mtENDPOINTS: return "endpoints";
54  case protocol::mtTRANSACTION: return "tx";
55  case protocol::mtGET_LEDGER: return "get_ledger";
56  case protocol::mtLEDGER_DATA: return "ledger_data";
57  case protocol::mtPROPOSE_LEDGER: return "propose";
58  case protocol::mtSTATUS_CHANGE: return "status";
59  case protocol::mtHAVE_SET: return "have_set";
60  case protocol::mtVALIDATORLIST: return "validator_list";
61  case protocol::mtVALIDATION: return "validation";
62  case protocol::mtGET_OBJECTS: return "get_objects";
63  default:
64  break;
65  }
66  return "unknown";
67 }
68 
69 namespace detail {
70 
72 {
78 
81 
84 
87 
90 
95 };
96 
97 template<typename BufferSequence>
98 auto
99 buffersBegin(BufferSequence const &bufs)
100 {
101  return boost::asio::buffers_iterator<BufferSequence, std::uint8_t>::begin(bufs);
102 }
103 
104 template <class BufferSequence>
105 boost::optional<MessageHeader> parseMessageHeader(
106  BufferSequence const& bufs,
107  std::size_t size)
108 {
109  using namespace ripple::compression;
110  auto iter = buffersBegin(bufs);
111 
112  MessageHeader hdr;
113  auto const compressed = (*iter & 0x80) == 0x80;
114 
115  // Check valid header
116  if ((*iter & 0xFC) == 0 || compressed)
117  {
118  hdr.header_size = compressed ? headerBytesCompressed : headerBytes;
119 
120  if (size < hdr.header_size)
121  return {};
122 
123  if (compressed)
124  {
125  uint8_t algorithm = (*iter & 0x70) >> 4;
126  if (algorithm != static_cast<std::uint8_t>(compression::Algorithm::LZ4))
127  return {};
129  }
130 
131  for (int i = 0; i != 4; ++i)
132  hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++;
133  // clear the compression bits
134  hdr.payload_wire_size &= 0x03FFFFFF;
135 
137 
138  for (int i = 0; i != 2; ++i)
139  hdr.message_type = (hdr.message_type << 8) + *iter++;
140 
141  if (compressed)
142  for (int i = 0; i != 4; ++i)
143  hdr.uncompressed_size = (hdr.uncompressed_size << 8) + *iter++;
144 
145  return hdr;
146  }
147 
148  return {};
149 }
150 
151 template <class T, class Buffers, class Handler,
153 bool
155  MessageHeader const& header,
156  Buffers const& buffers,
157  Handler& handler)
158 {
159  auto const m = std::make_shared<T>();
160 
161  ZeroCopyInputStream<Buffers> stream(buffers);
162  stream.Skip(header.header_size);
163 
165  {
167  payload.resize(header.uncompressed_size);
168 
169  auto payloadSize = ripple::compression::decompress(
170  stream,
171  header.payload_wire_size,
172  payload.data(),
173  header.uncompressed_size,
174  header.algorithm);
175 
176  if (payloadSize == 0 || !m->ParseFromArray(payload.data(), payloadSize))
177  return false;
178  }
179  else if (!m->ParseFromZeroCopyStream(&stream))
180  return false;
181 
182  handler.onMessageBegin (header.message_type, m, header.payload_wire_size);
183  handler.onMessage (m);
184  handler.onMessageEnd (header.message_type, m);
185 
186  return true;
187 }
188 
189 }
190 
198 template <class Buffers, class Handler>
200 invokeProtocolMessage (Buffers const& buffers, Handler& handler)
201 {
203 
204  auto const size = boost::asio::buffer_size(buffers);
205 
206  if (size == 0)
207  return result;
208 
209  auto header = detail::parseMessageHeader(buffers, size);
210 
211  // If we can't parse the header then it may be that we don't have enough
212  // bytes yet, or because the message was cut off.
213  if (!header)
214  return result;
215 
216  // We implement a maximum size for protocol messages. Sending a message
217  // whose size exceeds this may result in the connection being dropped. A
218  // larger message size may be supported in the future or negotiated as
219  // part of a protocol upgrade.
220  if (header->payload_wire_size > megabytes(64))
221  {
222  result.second = make_error_code(boost::system::errc::message_size);
223  return result;
224  }
225 
226  // We don't have the whole message yet. This isn't an error but we have
227  // nothing to do.
228  if (header->total_wire_size > size)
229  return result;
230 
231  bool success;
232 
233  switch (header->message_type)
234  {
235  case protocol::mtMANIFESTS:
236  success = detail::invoke<protocol::TMManifests>(*header, buffers, handler);
237  break;
238  case protocol::mtPING:
239  success = detail::invoke<protocol::TMPing>(*header, buffers, handler);
240  break;
241  case protocol::mtCLUSTER:
242  success = detail::invoke<protocol::TMCluster>(*header, buffers, handler);
243  break;
244  case protocol::mtGET_SHARD_INFO:
245  success = detail::invoke<protocol::TMGetShardInfo>(*header, buffers, handler);
246  break;
247  case protocol::mtSHARD_INFO:
248  success = detail::invoke<protocol::TMShardInfo>(*header, buffers, handler);
249  break;
250  case protocol::mtGET_PEER_SHARD_INFO:
251  success = detail::invoke<protocol::TMGetPeerShardInfo>(*header, buffers, handler);
252  break;
253  case protocol::mtPEER_SHARD_INFO:
254  success = detail::invoke<protocol::TMPeerShardInfo>(*header, buffers, handler);
255  break;
256  case protocol::mtENDPOINTS:
257  success = detail::invoke<protocol::TMEndpoints>(*header, buffers, handler);
258  break;
259  case protocol::mtTRANSACTION:
260  success = detail::invoke<protocol::TMTransaction>(*header, buffers, handler);
261  break;
262  case protocol::mtGET_LEDGER:
263  success = detail::invoke<protocol::TMGetLedger>(*header, buffers, handler);
264  break;
265  case protocol::mtLEDGER_DATA:
266  success = detail::invoke<protocol::TMLedgerData>(*header, buffers, handler);
267  break;
268  case protocol::mtPROPOSE_LEDGER:
269  success = detail::invoke<protocol::TMProposeSet>(*header, buffers, handler);
270  break;
271  case protocol::mtSTATUS_CHANGE:
272  success = detail::invoke<protocol::TMStatusChange>(*header, buffers, handler);
273  break;
274  case protocol::mtHAVE_SET:
275  success = detail::invoke<protocol::TMHaveTransactionSet>(*header, buffers, handler);
276  break;
277  case protocol::mtVALIDATION:
278  success = detail::invoke<protocol::TMValidation>(*header, buffers, handler);
279  break;
280  case protocol::mtVALIDATORLIST:
281  success = detail::invoke<protocol::TMValidatorList> (*header, buffers, handler);
282  break;
283  case protocol::mtGET_OBJECTS:
284  success = detail::invoke<protocol::TMGetObjectByHash>(*header, buffers, handler);
285  break;
286  default:
287  handler.onMessageUnknown (header->message_type);
288  success = true;
289  break;
290  }
291 
292  result.first = header->total_wire_size;
293 
294  if (!success)
295  result.second = make_error_code(boost::system::errc::bad_message);
296 
297  return result;
298 }
299 
300 } // ripple
301 
302 #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:89
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:83
ripple::detail::invoke
bool invoke(MessageHeader const &header, Buffers const &buffers, Handler &handler)
Definition: ProtocolMessage.h:154
std::pair
ripple::detail::MessageHeader::header_size
std::uint32_t header_size
The size of the header associated with this message.
Definition: ProtocolMessage.h:80
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:34
ripple::detail::buffersBegin
auto buffersBegin(BufferSequence const &bufs)
Definition: ProtocolMessage.h:99
ripple::detail::MessageHeader::algorithm
compression::Algorithm algorithm
Indicates which compression algorithm the payload is compressed with.
Definition: ProtocolMessage.h:94
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:86
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:77
ripple::megabytes
constexpr auto megabytes(T value) noexcept
Definition: ByteUtilities.h:32
ripple::detail::MessageHeader
Definition: ProtocolMessage.h:71
cstdint
std::uint32_t
ripple::detail::parseMessageHeader
boost::optional< MessageHeader > parseMessageHeader(BufferSequence const &bufs, std::size_t size)
Definition: ProtocolMessage.h:105
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:54
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:200
std::size_t
ripple::compression::headerBytesCompressed
constexpr std::size_t headerBytesCompressed
Definition: Compression.h:32
std::vector::data
T data(T... args)
type_traits
ripple::compression::Algorithm::None
@ None