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