print-zeromq.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /*
  2. * Copyright (c) 2013 The TCPDUMP project
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  15. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  16. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  17. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  18. * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  19. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  20. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  21. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  23. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  24. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  25. * POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. /* \summary: ZeroMQ Message Transport Protocol (ZMTP) printer */
  28. #ifdef HAVE_CONFIG_H
  29. #include "config.h"
  30. #endif
  31. #include <netdissect-stdinc.h>
  32. #include "netdissect.h"
  33. #include "extract.h"
  34. static const char tstr[] = " [|zmtp1]";
  35. /* Maximum number of ZMTP/1.0 frame body bytes (without the flags) to dump in
  36. * hex and ASCII under a single "-v" flag.
  37. */
  38. #define VBYTES 128
  39. /*
  40. * Below is an excerpt from the "13/ZMTP" specification:
  41. *
  42. * A ZMTP message consists of 1 or more frames.
  43. *
  44. * A ZMTP frame consists of a length, followed by a flags field and a frame
  45. * body of (length - 1) octets. Note: the length includes the flags field, so
  46. * an empty frame has a length of 1.
  47. *
  48. * For frames with a length of 1 to 254 octets, the length SHOULD BE encoded
  49. * as a single octet. The minimum valid length of a frame is 1 octet, thus a
  50. * length of 0 is invalid and such frames SHOULD be discarded silently.
  51. *
  52. * For frames with lengths of 255 and greater, the length SHALL BE encoded as
  53. * a single octet with the value 255, followed by the length encoded as a
  54. * 64-bit unsigned integer in network byte order. For frames with lengths of
  55. * 1 to 254 octets this encoding MAY be also used.
  56. *
  57. * The flags field consists of a single octet containing various control
  58. * flags. Bit 0 is the least significant bit.
  59. *
  60. * - Bit 0 (MORE): More frames to follow. A value of 0 indicates that there
  61. * are no more frames to follow. A value of 1 indicates that more frames
  62. * will follow. On messages consisting of a single frame the MORE flag MUST
  63. * be 0.
  64. *
  65. * - Bits 1-7: Reserved. Bits 1-7 are reserved for future use and SHOULD be
  66. * zero.
  67. */
  68. static const u_char *
  69. zmtp1_print_frame(netdissect_options *ndo, const u_char *cp, const u_char *ep)
  70. {
  71. uint64_t body_len_declared, body_len_captured, header_len;
  72. uint8_t flags;
  73. ND_PRINT((ndo, "\n\t"));
  74. ND_TCHECK2(*cp, 1); /* length/0xFF */
  75. if (cp[0] != 0xFF) {
  76. header_len = 1; /* length */
  77. body_len_declared = cp[0];
  78. ND_PRINT((ndo, " frame flags+body (8-bit) length %" PRIu64, body_len_declared));
  79. } else {
  80. header_len = 1 + 8; /* 0xFF, length */
  81. ND_PRINT((ndo, " frame flags+body (64-bit) length"));
  82. ND_TCHECK2(*cp, header_len); /* 0xFF, length */
  83. body_len_declared = EXTRACT_64BITS(cp + 1);
  84. ND_PRINT((ndo, " %" PRIu64, body_len_declared));
  85. }
  86. if (body_len_declared == 0)
  87. return cp + header_len; /* skip to the next frame */
  88. ND_TCHECK2(*cp, header_len + 1); /* ..., flags */
  89. flags = cp[header_len];
  90. body_len_captured = ep - cp - header_len;
  91. if (body_len_declared > body_len_captured)
  92. ND_PRINT((ndo, " (%" PRIu64 " captured)", body_len_captured));
  93. ND_PRINT((ndo, ", flags 0x%02x", flags));
  94. if (ndo->ndo_vflag) {
  95. uint64_t body_len_printed = min(body_len_captured, body_len_declared);
  96. ND_PRINT((ndo, " (%s|%s|%s|%s|%s|%s|%s|%s)",
  97. flags & 0x80 ? "MBZ" : "-",
  98. flags & 0x40 ? "MBZ" : "-",
  99. flags & 0x20 ? "MBZ" : "-",
  100. flags & 0x10 ? "MBZ" : "-",
  101. flags & 0x08 ? "MBZ" : "-",
  102. flags & 0x04 ? "MBZ" : "-",
  103. flags & 0x02 ? "MBZ" : "-",
  104. flags & 0x01 ? "MORE" : "-"));
  105. if (ndo->ndo_vflag == 1)
  106. body_len_printed = min(VBYTES + 1, body_len_printed);
  107. if (body_len_printed > 1) {
  108. ND_PRINT((ndo, ", first %" PRIu64 " byte(s) of body:", body_len_printed - 1));
  109. hex_and_ascii_print(ndo, "\n\t ", cp + header_len + 1, body_len_printed - 1);
  110. ND_PRINT((ndo, "\n"));
  111. }
  112. }
  113. /*
  114. * Do not advance cp by the sum of header_len and body_len_declared
  115. * before each offset has successfully passed ND_TCHECK2() as the
  116. * sum can roll over (9 + 0xfffffffffffffff7 = 0) and cause an
  117. * infinite loop.
  118. */
  119. cp += header_len;
  120. ND_TCHECK2(*cp, body_len_declared); /* Next frame within the buffer ? */
  121. return cp + body_len_declared;
  122. trunc:
  123. ND_PRINT((ndo, "%s", tstr));
  124. return ep;
  125. }
  126. void
  127. zmtp1_print(netdissect_options *ndo, const u_char *cp, u_int len)
  128. {
  129. const u_char *ep = min(ndo->ndo_snapend, cp + len);
  130. ND_PRINT((ndo, ": ZMTP/1.0"));
  131. while (cp < ep)
  132. cp = zmtp1_print_frame(ndo, cp, ep);
  133. }
  134. /* The functions below decode a ZeroMQ datagram, supposedly stored in the "Data"
  135. * field of an ODATA/RDATA [E]PGM packet. An excerpt from zmq_pgm(7) man page
  136. * follows.
  137. *
  138. * In order for late joining consumers to be able to identify message
  139. * boundaries, each PGM datagram payload starts with a 16-bit unsigned integer
  140. * in network byte order specifying either the offset of the first message frame
  141. * in the datagram or containing the value 0xFFFF if the datagram contains
  142. * solely an intermediate part of a larger message.
  143. *
  144. * Note that offset specifies where the first message begins rather than the
  145. * first message part. Thus, if there are trailing message parts at the
  146. * beginning of the packet the offset ignores them and points to first initial
  147. * message part in the packet.
  148. */
  149. static const u_char *
  150. zmtp1_print_intermediate_part(netdissect_options *ndo, const u_char *cp, const u_int len)
  151. {
  152. u_int frame_offset;
  153. uint64_t remaining_len;
  154. ND_TCHECK2(*cp, 2);
  155. frame_offset = EXTRACT_16BITS(cp);
  156. ND_PRINT((ndo, "\n\t frame offset 0x%04x", frame_offset));
  157. cp += 2;
  158. remaining_len = ndo->ndo_snapend - cp; /* without the frame length */
  159. if (frame_offset == 0xFFFF)
  160. frame_offset = len - 2; /* always within the declared length */
  161. else if (2 + frame_offset > len) {
  162. ND_PRINT((ndo, " (exceeds datagram declared length)"));
  163. goto trunc;
  164. }
  165. /* offset within declared length of the datagram */
  166. if (frame_offset) {
  167. ND_PRINT((ndo, "\n\t frame intermediate part, %u bytes", frame_offset));
  168. if (frame_offset > remaining_len)
  169. ND_PRINT((ndo, " (%"PRIu64" captured)", remaining_len));
  170. if (ndo->ndo_vflag) {
  171. uint64_t len_printed = min(frame_offset, remaining_len);
  172. if (ndo->ndo_vflag == 1)
  173. len_printed = min(VBYTES, len_printed);
  174. if (len_printed > 1) {
  175. ND_PRINT((ndo, ", first %"PRIu64" byte(s):", len_printed));
  176. hex_and_ascii_print(ndo, "\n\t ", cp, len_printed);
  177. ND_PRINT((ndo, "\n"));
  178. }
  179. }
  180. }
  181. return cp + frame_offset;
  182. trunc:
  183. ND_PRINT((ndo, "%s", tstr));
  184. return cp + len;
  185. }
  186. void
  187. zmtp1_print_datagram(netdissect_options *ndo, const u_char *cp, const u_int len)
  188. {
  189. const u_char *ep = min(ndo->ndo_snapend, cp + len);
  190. cp = zmtp1_print_intermediate_part(ndo, cp, len);
  191. while (cp < ep)
  192. cp = zmtp1_print_frame(ndo, cp, ep);
  193. }