rippled
Loading...
Searching...
No Matches
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 <xrpld/overlay/Compression.h>
24#include <xrpld/overlay/Message.h>
25#include <xrpld/overlay/detail/ZeroCopyStream.h>
26#include <xrpl/basics/ByteUtilities.h>
27#include <xrpl/beast/utility/instrumentation.h>
28#include <xrpl/protocol/messages.h>
29#include <boost/asio/buffer.hpp>
30#include <boost/asio/buffers_iterator.hpp>
31#include <boost/system/error_code.hpp>
32#include <cstdint>
33#include <memory>
34#include <optional>
35#include <type_traits>
36#include <vector>
37
38namespace ripple {
39
40inline protocol::MessageType
41protocolMessageType(protocol::TMGetLedger const&)
42{
43 return protocol::mtGET_LEDGER;
44}
45
46inline protocol::MessageType
47protocolMessageType(protocol::TMLedgerData const&)
48{
49 return protocol::mtLEDGER_DATA;
50}
51
52inline protocol::MessageType
53protocolMessageType(protocol::TMReplayDeltaRequest const&)
54{
55 return protocol::mtREPLAY_DELTA_REQ;
56}
57
58inline protocol::MessageType
59protocolMessageType(protocol::TMProofPathRequest const&)
60{
61 return protocol::mtPROOF_PATH_REQ;
62}
63
65template <class = void>
68{
69 switch (type)
70 {
71 case protocol::mtMANIFESTS:
72 return "manifests";
73 case protocol::mtPING:
74 return "ping";
75 case protocol::mtCLUSTER:
76 return "cluster";
77 case protocol::mtENDPOINTS:
78 return "endpoints";
79 case protocol::mtTRANSACTION:
80 return "tx";
81 case protocol::mtGET_LEDGER:
82 return "get_ledger";
83 case protocol::mtLEDGER_DATA:
84 return "ledger_data";
85 case protocol::mtPROPOSE_LEDGER:
86 return "propose";
87 case protocol::mtSTATUS_CHANGE:
88 return "status";
89 case protocol::mtHAVE_SET:
90 return "have_set";
91 case protocol::mtVALIDATORLIST:
92 return "validator_list";
93 case protocol::mtVALIDATORLISTCOLLECTION:
94 return "validator_list_collection";
95 case protocol::mtVALIDATION:
96 return "validation";
97 case protocol::mtGET_OBJECTS:
98 return "get_objects";
99 case protocol::mtHAVE_TRANSACTIONS:
100 return "have_transactions";
101 case protocol::mtTRANSACTIONS:
102 return "transactions";
103 case protocol::mtSQUELCH:
104 return "squelch";
105 case protocol::mtPROOF_PATH_REQ:
106 return "proof_path_request";
107 case protocol::mtPROOF_PATH_RESPONSE:
108 return "proof_path_response";
109 case protocol::mtREPLAY_DELTA_REQ:
110 return "replay_delta_request";
111 case protocol::mtREPLAY_DELTA_RESPONSE:
112 return "replay_delta_response";
113 default:
114 break;
115 }
116 return "unknown";
117}
118
119namespace detail {
120
122{
128
131
134
137
140
146};
147
148template <typename BufferSequence>
149auto
150buffersBegin(BufferSequence const& bufs)
151{
152 return boost::asio::buffers_iterator<BufferSequence, std::uint8_t>::begin(
153 bufs);
154}
155
156template <typename BufferSequence>
157auto
158buffersEnd(BufferSequence const& bufs)
159{
160 return boost::asio::buffers_iterator<BufferSequence, std::uint8_t>::end(
161 bufs);
162}
163
173template <class BufferSequence>
176 boost::system::error_code& ec,
177 BufferSequence const& bufs,
178 std::size_t size)
179{
180 using namespace ripple::compression;
181
182 MessageHeader hdr;
183 auto iter = buffersBegin(bufs);
184 XRPL_ASSERT(
185 iter != buffersEnd(bufs),
186 "ripple::detail::parseMessageHeader : non-empty buffer");
187
188 // Check valid header compressed message:
189 // - 4 bits are the compression algorithm, 1st bit is always set to 1
190 // - 2 bits are always set to 0
191 // - 26 bits are the payload size
192 // - 32 bits are the uncompressed data size
193 if (*iter & 0x80)
194 {
195 hdr.header_size = headerBytesCompressed;
196
197 // not enough bytes to parse the header
198 if (size < hdr.header_size)
199 {
200 ec = make_error_code(boost::system::errc::success);
201 return std::nullopt;
202 }
203
204 if (*iter & 0x0C)
205 {
206 ec = make_error_code(boost::system::errc::protocol_error);
207 return std::nullopt;
208 }
209
210 hdr.algorithm = static_cast<compression::Algorithm>(*iter & 0xF0);
211
213 {
214 ec = make_error_code(boost::system::errc::protocol_error);
215 return std::nullopt;
216 }
217
218 for (int i = 0; i != 4; ++i)
219 hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++;
220
221 // clear the top four bits (the compression bits).
222 hdr.payload_wire_size &= 0x0FFFFFFF;
223
225
226 for (int i = 0; i != 2; ++i)
227 hdr.message_type = (hdr.message_type << 8) + *iter++;
228
229 for (int i = 0; i != 4; ++i)
230 hdr.uncompressed_size = (hdr.uncompressed_size << 8) + *iter++;
231
232 return hdr;
233 }
234
235 // Check valid header uncompressed message:
236 // - 6 bits are set to 0
237 // - 26 bits are the payload size
238 if ((*iter & 0xFC) == 0)
239 {
240 hdr.header_size = headerBytes;
241
242 if (size < hdr.header_size)
243 {
244 ec = make_error_code(boost::system::errc::success);
245 return std::nullopt;
246 }
247
248 hdr.algorithm = Algorithm::None;
249
250 for (int i = 0; i != 4; ++i)
251 hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++;
252
255
256 for (int i = 0; i != 2; ++i)
257 hdr.message_type = (hdr.message_type << 8) + *iter++;
258
259 return hdr;
260 }
261
262 ec = make_error_code(boost::system::errc::no_message);
263 return std::nullopt;
264}
265
266template <
267 class T,
268 class Buffers,
269 class = std::enable_if_t<
272parseMessageContent(MessageHeader const& header, Buffers const& buffers)
273{
274 auto const m = std::make_shared<T>();
275
276 ZeroCopyInputStream<Buffers> stream(buffers);
277 stream.Skip(header.header_size);
278
280 {
282 payload.resize(header.uncompressed_size);
283
284 auto const payloadSize = ripple::compression::decompress(
285 stream,
286 header.payload_wire_size,
287 payload.data(),
288 header.uncompressed_size,
289 header.algorithm);
290
291 if (payloadSize == 0 || !m->ParseFromArray(payload.data(), payloadSize))
292 return {};
293 }
294 else if (!m->ParseFromZeroCopyStream(&stream))
295 return {};
296
297 return m;
298}
299
300template <
301 class T,
302 class Buffers,
303 class Handler,
304 class = std::enable_if_t<
306bool
307invoke(MessageHeader const& header, Buffers const& buffers, Handler& handler)
308{
309 auto const m = parseMessageContent<T>(header, buffers);
310 if (!m)
311 return false;
312
313 using namespace ripple::compression;
314 handler.onMessageBegin(
315 header.message_type,
316 m,
317 header.payload_wire_size,
318 header.uncompressed_size,
319 header.algorithm != Algorithm::None);
320 handler.onMessage(m);
321 handler.onMessageEnd(header.message_type, m);
322
323 return true;
324}
325
326} // namespace detail
327
340template <class Buffers, class Handler>
343 Buffers const& buffers,
344 Handler& handler,
345 std::size_t& hint)
346{
348
349 auto const size = boost::asio::buffer_size(buffers);
350
351 if (size == 0)
352 return result;
353
354 auto header = detail::parseMessageHeader(result.second, buffers, size);
355
356 // If we can't parse the header then it may be that we don't have enough
357 // bytes yet, or because the message was cut off (if error_code is success).
358 // Otherwise we failed to match the header's marker (error_code is set to
359 // no_message) or the compression algorithm is invalid (error_code is
360 // protocol_error) and signal an error.
361 if (!header)
362 return result;
363
364 // We implement a maximum size for protocol messages. Sending a message
365 // whose size exceeds this may result in the connection being dropped. A
366 // larger message size may be supported in the future or negotiated as
367 // part of a protocol upgrade.
368 if (header->payload_wire_size > maximiumMessageSize ||
369 header->uncompressed_size > maximiumMessageSize)
370 {
371 result.second = make_error_code(boost::system::errc::message_size);
372 return result;
373 }
374
375 // We requested uncompressed messages from the peer but received compressed.
376 if (!handler.compressionEnabled() &&
377 header->algorithm != compression::Algorithm::None)
378 {
379 result.second = make_error_code(boost::system::errc::protocol_error);
380 return result;
381 }
382
383 // We don't have the whole message yet. This isn't an error but we have
384 // nothing to do.
385 if (header->total_wire_size > size)
386 {
387 hint = header->total_wire_size - size;
388 return result;
389 }
390
391 bool success;
392
393 switch (header->message_type)
394 {
395 case protocol::mtMANIFESTS:
396 success = detail::invoke<protocol::TMManifests>(
397 *header, buffers, handler);
398 break;
399 case protocol::mtPING:
400 success =
401 detail::invoke<protocol::TMPing>(*header, buffers, handler);
402 break;
403 case protocol::mtCLUSTER:
404 success =
405 detail::invoke<protocol::TMCluster>(*header, buffers, handler);
406 break;
407 case protocol::mtENDPOINTS:
408 success = detail::invoke<protocol::TMEndpoints>(
409 *header, buffers, handler);
410 break;
411 case protocol::mtTRANSACTION:
412 success = detail::invoke<protocol::TMTransaction>(
413 *header, buffers, handler);
414 break;
415 case protocol::mtGET_LEDGER:
416 success = detail::invoke<protocol::TMGetLedger>(
417 *header, buffers, handler);
418 break;
419 case protocol::mtLEDGER_DATA:
420 success = detail::invoke<protocol::TMLedgerData>(
421 *header, buffers, handler);
422 break;
423 case protocol::mtPROPOSE_LEDGER:
424 success = detail::invoke<protocol::TMProposeSet>(
425 *header, buffers, handler);
426 break;
427 case protocol::mtSTATUS_CHANGE:
428 success = detail::invoke<protocol::TMStatusChange>(
429 *header, buffers, handler);
430 break;
431 case protocol::mtHAVE_SET:
432 success = detail::invoke<protocol::TMHaveTransactionSet>(
433 *header, buffers, handler);
434 break;
435 case protocol::mtVALIDATION:
436 success = detail::invoke<protocol::TMValidation>(
437 *header, buffers, handler);
438 break;
439 case protocol::mtVALIDATORLIST:
440 success = detail::invoke<protocol::TMValidatorList>(
441 *header, buffers, handler);
442 break;
443 case protocol::mtVALIDATORLISTCOLLECTION:
444 success = detail::invoke<protocol::TMValidatorListCollection>(
445 *header, buffers, handler);
446 break;
447 case protocol::mtGET_OBJECTS:
448 success = detail::invoke<protocol::TMGetObjectByHash>(
449 *header, buffers, handler);
450 break;
451 case protocol::mtHAVE_TRANSACTIONS:
452 success = detail::invoke<protocol::TMHaveTransactions>(
453 *header, buffers, handler);
454 break;
455 case protocol::mtTRANSACTIONS:
456 success = detail::invoke<protocol::TMTransactions>(
457 *header, buffers, handler);
458 break;
459 case protocol::mtSQUELCH:
460 success =
461 detail::invoke<protocol::TMSquelch>(*header, buffers, handler);
462 break;
463 case protocol::mtPROOF_PATH_REQ:
464 success = detail::invoke<protocol::TMProofPathRequest>(
465 *header, buffers, handler);
466 break;
467 case protocol::mtPROOF_PATH_RESPONSE:
468 success = detail::invoke<protocol::TMProofPathResponse>(
469 *header, buffers, handler);
470 break;
471 case protocol::mtREPLAY_DELTA_REQ:
472 success = detail::invoke<protocol::TMReplayDeltaRequest>(
473 *header, buffers, handler);
474 break;
475 case protocol::mtREPLAY_DELTA_RESPONSE:
476 success = detail::invoke<protocol::TMReplayDeltaResponse>(
477 *header, buffers, handler);
478 break;
479 default:
480 handler.onMessageUnknown(header->message_type);
481 success = true;
482 break;
483 }
484
485 result.first = header->total_wire_size;
486
487 if (!success)
488 result.second = make_error_code(boost::system::errc::bad_message);
489
490 return result;
491}
492
493} // namespace ripple
494
495namespace protocol {
496
497template <class Hasher>
498void
499hash_append(Hasher& h, TMGetLedger const& msg)
500{
501 using beast::hash_append;
502 using namespace ripple;
503 hash_append(h, safe_cast<int>(protocolMessageType(msg)));
504 hash_append(h, safe_cast<int>(msg.itype()));
505 if (msg.has_ltype())
506 hash_append(h, safe_cast<int>(msg.ltype()));
507
508 if (msg.has_ledgerhash())
509 hash_append(h, msg.ledgerhash());
510
511 if (msg.has_ledgerseq())
512 hash_append(h, msg.ledgerseq());
513
514 for (auto const& nodeId : msg.nodeids())
515 hash_append(h, nodeId);
516 hash_append(h, msg.nodeids_size());
517
518 // Do NOT include the request cookie. It does not affect the content of the
519 // request, but only where to route the results.
520 // if (msg.has_requestcookie())
521 // hash_append(h, msg.requestcookie());
522
523 if (msg.has_querytype())
524 hash_append(h, safe_cast<int>(msg.querytype()));
525
526 if (msg.has_querydepth())
527 hash_append(h, msg.querydepth());
528}
529
530template <class Hasher>
531void
532hash_append(Hasher& h, TMLedgerData const& msg)
533{
534 using beast::hash_append;
535 using namespace ripple;
536 hash_append(h, safe_cast<int>(protocolMessageType(msg)));
537 hash_append(h, msg.ledgerhash());
538 hash_append(h, msg.ledgerseq());
539 hash_append(h, safe_cast<int>(msg.type()));
540 for (auto const& node : msg.nodes())
541 {
542 hash_append(h, node.nodedata());
543 if (node.has_nodeid())
544 hash_append(h, node.nodeid());
545 }
546 hash_append(h, msg.nodes_size());
547 if (msg.has_requestcookie())
548 hash_append(h, msg.requestcookie());
549 if (msg.has_error())
550 hash_append(h, safe_cast<int>(msg.error()));
551}
552
553} // namespace protocol
554
555#endif
Implements ZeroCopyInputStream around a buffer sequence.
T data(T... args)
std::enable_if_t< is_contiguously_hashable< T, Hasher >::value > hash_append(Hasher &h, T const &t) noexcept
Logically concatenate input data to a Hasher.
Definition: hash_append.h:236
void hash_append(Hasher &h, TMValidatorList const &msg)
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
std::shared_ptr< T > parseMessageContent(MessageHeader const &header, Buffers const &buffers)
bool invoke(MessageHeader const &header, Buffers const &buffers, Handler &handler)
auto buffersBegin(BufferSequence const &bufs)
std::optional< MessageHeader > parseMessageHeader(boost::system::error_code &ec, BufferSequence const &bufs, std::size_t size)
Parse a message header.
auto buffersEnd(BufferSequence const &bufs)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::string protocolMessageName(int type)
Returns the name of a protocol message given its type.
protocol::MessageType protocolMessageType(protocol::TMGetLedger const &)
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.
std::error_code make_error_code(ripple::TokenCodecErrc e)
Definition: token_errors.h:97
constexpr std::size_t maximiumMessageSize
Definition: Message.h:38
T resize(T... args)
std::uint32_t header_size
The size of the header associated with this message.
compression::Algorithm algorithm
Indicates which compression algorithm the payload is compressed with.
std::uint16_t message_type
The type of the message.
std::uint32_t payload_wire_size
The size of the payload on the wire.
std::uint32_t total_wire_size
The size of the message on the wire.
std::uint32_t uncompressed_size
Uncompressed message size if the message is compressed.