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 41static 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 78static const u_char * 79zmtp1_print_frame(netdissect_options *ndo, const u_char *cp, const u_char *ep) 80{ 81 uint64_t body_len_declared, body_len_captured, header_len; 82 uint8_t flags; 83 84 ND_PRINT((ndo, "\n\t")); 85 ND_TCHECK2(*cp, 1); /* length/0xFF */ 86 87 if (cp[0] != 0xFF) { 88 header_len = 1; /* length */ 89 body_len_declared = cp[0]; 90 if (body_len_declared == 0) 91 return cp + header_len; /* skip to next frame */ 92 ND_PRINT((ndo, " frame flags+body (8-bit) length %u", cp[0])); 93 ND_TCHECK2(*cp, header_len + 1); /* length, flags */ 94 flags = cp[1]; 95 } else { 96 header_len = 1 + 8; /* 0xFF, length */ 97 ND_PRINT((ndo, " frame flags+body (64-bit) length")); 98 ND_TCHECK2(*cp, header_len); /* 0xFF, length */ 99 body_len_declared = EXTRACT_64BITS(cp + 1); 100 if (body_len_declared == 0) 101 return cp + header_len; /* skip to next frame */ 102 ND_PRINT((ndo, " %" PRIu64, body_len_declared)); 103 ND_TCHECK2(*cp, header_len + 1); /* 0xFF, length, flags */ 104 flags = cp[9]; 105 } 106 107 body_len_captured = ep - cp - header_len; 108 if (body_len_declared > body_len_captured) 109 ND_PRINT((ndo, " (%" PRIu64 " captured)", body_len_captured)); 110 ND_PRINT((ndo, ", flags 0x%02x", flags)); 111 112 if (ndo->ndo_vflag) { 113 uint64_t body_len_printed = min(body_len_captured, body_len_declared); 114 115 ND_PRINT((ndo, " (%s|%s|%s|%s|%s|%s|%s|%s)", 116 flags & 0x80 ? "MBZ" : "-", 117 flags & 0x40 ? "MBZ" : "-", 118 flags & 0x20 ? "MBZ" : "-", 119 flags & 0x10 ? "MBZ" : "-", 120 flags & 0x08 ? "MBZ" : "-", 121 flags & 0x04 ? "MBZ" : "-", 122 flags & 0x02 ? "MBZ" : "-", 123 flags & 0x01 ? "MORE" : "-")); 124 125 if (ndo->ndo_vflag == 1) 126 body_len_printed = min(VBYTES + 1, body_len_printed); 127 if (body_len_printed > 1) { 128 ND_PRINT((ndo, ", first %" PRIu64 " byte(s) of body:", body_len_printed - 1)); 129 hex_and_ascii_print(ndo, "\n\t ", cp + header_len + 1, body_len_printed - 1); 130 ND_PRINT((ndo, "\n")); 131 } 132 } 133 134 ND_TCHECK2(*cp, header_len + body_len_declared); /* Next frame within the buffer ? */ 135 return cp + header_len + body_len_declared; 136 137trunc: 138 ND_PRINT((ndo, "%s", tstr)); 139 return ep; 140} 141 142void 143zmtp1_print(netdissect_options *ndo, const u_char *cp, u_int len) 144{ 145 const u_char *ep = min(ndo->ndo_snapend, cp + len); 146 147 ND_PRINT((ndo, ": ZMTP/1.0")); 148 while (cp < ep) 149 cp = zmtp1_print_frame(ndo, cp, ep); 150} 151 152/* The functions below decode a ZeroMQ datagram, supposedly stored in the "Data" 153 * field of an ODATA/RDATA [E]PGM packet. An excerpt from zmq_pgm(7) man page 154 * follows. 155 * 156 * In order for late joining consumers to be able to identify message 157 * boundaries, each PGM datagram payload starts with a 16-bit unsigned integer 158 * in network byte order specifying either the offset of the first message frame 159 * in the datagram or containing the value 0xFFFF if the datagram contains 160 * solely an intermediate part of a larger message. 161 * 162 * Note that offset specifies where the first message begins rather than the 163 * first message part. Thus, if there are trailing message parts at the 164 * beginning of the packet the offset ignores them and points to first initial 165 * message part in the packet. 166 */ 167 168static const u_char * 169zmtp1_print_intermediate_part(netdissect_options *ndo, const u_char *cp, const u_int len) 170{ 171 u_int frame_offset; 172 uint64_t remaining_len; 173 174 ND_TCHECK2(*cp, 2); 175 frame_offset = EXTRACT_16BITS(cp); 176 ND_PRINT((ndo, "\n\t frame offset 0x%04x", frame_offset)); 177 cp += 2; 178 remaining_len = ndo->ndo_snapend - cp; /* without the frame length */ 179 180 if (frame_offset == 0xFFFF) 181 frame_offset = len - 2; /* always within the declared length */ 182 else if (2 + frame_offset > len) { 183 ND_PRINT((ndo, " (exceeds datagram declared length)")); 184 goto trunc; 185 } 186 187 /* offset within declared length of the datagram */ 188 if (frame_offset) { 189 ND_PRINT((ndo, "\n\t frame intermediate part, %u bytes", frame_offset)); 190 if (frame_offset > remaining_len) 191 ND_PRINT((ndo, " (%"PRIu64" captured)", remaining_len)); 192 if (ndo->ndo_vflag) { 193 uint64_t len_printed = min(frame_offset, remaining_len); 194 195 if (ndo->ndo_vflag == 1) 196 len_printed = min(VBYTES, len_printed); 197 if (len_printed > 1) { 198 ND_PRINT((ndo, ", first %"PRIu64" byte(s):", len_printed)); 199 hex_and_ascii_print(ndo, "\n\t ", cp, len_printed); 200 ND_PRINT((ndo, "\n")); 201 } 202 } 203 } 204 return cp + frame_offset; 205 206trunc: 207 ND_PRINT((ndo, "%s", tstr)); 208 return cp + len; 209} 210 211void 212zmtp1_print_datagram(netdissect_options *ndo, const u_char *cp, const u_int len) 213{ 214 const u_char *ep = min(ndo->ndo_snapend, cp + len); 215 216 cp = zmtp1_print_intermediate_part(ndo, cp, len); 217 while (cp < ep) 218 cp = zmtp1_print_frame(ndo, cp, ep); 219} 220