1 1.1 christos /* 2 1.1 christos * Decode and print Zephyr packets. 3 1.1 christos * 4 1.9 christos * https://web.mit.edu/zephyr/doc/protocol 5 1.1 christos * 6 1.1 christos * Copyright (c) 2001 Nickolai Zeldovich <kolya (at) MIT.EDU> 7 1.1 christos * All rights reserved. 8 1.1 christos * 9 1.1 christos * Redistribution and use in source and binary forms, with or without 10 1.1 christos * modification, are permitted provided that: (1) source code 11 1.1 christos * distributions retain the above copyright notice and this paragraph 12 1.1 christos * in its entirety, and (2) distributions including binary code include 13 1.1 christos * the above copyright notice and this paragraph in its entirety in 14 1.1 christos * the documentation or other materials provided with the distribution. 15 1.1 christos * The name of the author(s) may not be used to endorse or promote 16 1.1 christos * products derived from this software without specific prior written 17 1.1 christos * permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY 18 1.1 christos * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE 19 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 1.1 christos * PURPOSE. 21 1.1 christos */ 22 1.1 christos 23 1.2 christos #include <sys/cdefs.h> 24 1.1 christos #ifndef lint 25 1.10 christos __RCSID("$NetBSD: print-zephyr.c,v 1.10 2024/09/02 16:15:33 christos Exp $"); 26 1.1 christos #endif 27 1.1 christos 28 1.7 spz /* \summary: Zephyr printer */ 29 1.7 spz 30 1.9 christos #include <config.h> 31 1.1 christos 32 1.9 christos #include "netdissect-stdinc.h" 33 1.1 christos 34 1.1 christos #include <stdio.h> 35 1.1 christos #include <string.h> 36 1.1 christos #include <stdlib.h> 37 1.1 christos 38 1.9 christos #include "netdissect-ctype.h" 39 1.9 christos 40 1.6 christos #include "netdissect.h" 41 1.9 christos #include "extract.h" 42 1.1 christos 43 1.1 christos struct z_packet { 44 1.6 christos const char *version; 45 1.1 christos int numfields; 46 1.1 christos int kind; 47 1.6 christos const char *uid; 48 1.1 christos int port; 49 1.1 christos int auth; 50 1.1 christos int authlen; 51 1.6 christos const char *authdata; 52 1.6 christos const char *class; 53 1.6 christos const char *inst; 54 1.6 christos const char *opcode; 55 1.6 christos const char *sender; 56 1.1 christos const char *recipient; 57 1.6 christos const char *format; 58 1.1 christos int cksum; 59 1.1 christos int multi; 60 1.6 christos const char *multi_uid; 61 1.1 christos /* Other fields follow here.. */ 62 1.1 christos }; 63 1.1 christos 64 1.1 christos enum z_packet_type { 65 1.1 christos Z_PACKET_UNSAFE = 0, 66 1.1 christos Z_PACKET_UNACKED, 67 1.1 christos Z_PACKET_ACKED, 68 1.1 christos Z_PACKET_HMACK, 69 1.1 christos Z_PACKET_HMCTL, 70 1.1 christos Z_PACKET_SERVACK, 71 1.1 christos Z_PACKET_SERVNAK, 72 1.1 christos Z_PACKET_CLIENTACK, 73 1.1 christos Z_PACKET_STAT 74 1.1 christos }; 75 1.1 christos 76 1.4 christos static const struct tok z_types[] = { 77 1.1 christos { Z_PACKET_UNSAFE, "unsafe" }, 78 1.1 christos { Z_PACKET_UNACKED, "unacked" }, 79 1.1 christos { Z_PACKET_ACKED, "acked" }, 80 1.1 christos { Z_PACKET_HMACK, "hm-ack" }, 81 1.1 christos { Z_PACKET_HMCTL, "hm-ctl" }, 82 1.1 christos { Z_PACKET_SERVACK, "serv-ack" }, 83 1.1 christos { Z_PACKET_SERVNAK, "serv-nak" }, 84 1.1 christos { Z_PACKET_CLIENTACK, "client-ack" }, 85 1.8 christos { Z_PACKET_STAT, "stat" }, 86 1.8 christos { 0, NULL } 87 1.1 christos }; 88 1.1 christos 89 1.5 christos static char z_buf[256]; 90 1.1 christos 91 1.6 christos static const char * 92 1.9 christos parse_field(netdissect_options *ndo, const char **pptr, int *len) 93 1.1 christos { 94 1.6 christos const char *s; 95 1.1 christos 96 1.8 christos /* Start of string */ 97 1.1 christos s = *pptr; 98 1.8 christos /* Scan for the NUL terminator */ 99 1.8 christos for (;;) { 100 1.8 christos if (*len == 0) { 101 1.8 christos /* Ran out of packet data without finding it */ 102 1.8 christos return NULL; 103 1.8 christos } 104 1.9 christos if (GET_U_1(*pptr) == '\0') { 105 1.8 christos /* Found it */ 106 1.8 christos break; 107 1.8 christos } 108 1.8 christos /* Keep scanning */ 109 1.1 christos (*pptr)++; 110 1.1 christos (*len)--; 111 1.1 christos } 112 1.8 christos /* Skip the NUL terminator */ 113 1.1 christos (*pptr)++; 114 1.1 christos (*len)--; 115 1.1 christos return s; 116 1.1 christos } 117 1.1 christos 118 1.1 christos static const char * 119 1.6 christos z_triple(const char *class, const char *inst, const char *recipient) 120 1.1 christos { 121 1.1 christos if (!*recipient) 122 1.1 christos recipient = "*"; 123 1.1 christos snprintf(z_buf, sizeof(z_buf), "<%s,%s,%s>", class, inst, recipient); 124 1.1 christos z_buf[sizeof(z_buf)-1] = '\0'; 125 1.1 christos return z_buf; 126 1.1 christos } 127 1.1 christos 128 1.1 christos static const char * 129 1.6 christos str_to_lower(const char *string) 130 1.1 christos { 131 1.6 christos char *zb_string; 132 1.6 christos 133 1.1 christos strncpy(z_buf, string, sizeof(z_buf)); 134 1.1 christos z_buf[sizeof(z_buf)-1] = '\0'; 135 1.1 christos 136 1.6 christos zb_string = z_buf; 137 1.6 christos while (*zb_string) { 138 1.9 christos *zb_string = ND_ASCII_TOLOWER(*zb_string); 139 1.6 christos zb_string++; 140 1.1 christos } 141 1.1 christos 142 1.1 christos return z_buf; 143 1.1 christos } 144 1.1 christos 145 1.9 christos #define ZEPHYR_PRINT(str1,str2) \ 146 1.9 christos { ND_PRINT("%s", (str1)); fn_print_str(ndo, (const u_char *)(str2)); } 147 1.9 christos 148 1.1 christos void 149 1.9 christos zephyr_print(netdissect_options *ndo, const u_char *cp, u_int length) 150 1.1 christos { 151 1.9 christos struct z_packet z = { 152 1.9 christos NULL, /* version */ 153 1.9 christos 0, /* numfields */ 154 1.9 christos 0, /* kind */ 155 1.9 christos NULL, /* uid */ 156 1.9 christos 0, /* port */ 157 1.9 christos 0, /* auth */ 158 1.9 christos 0, /* authlen */ 159 1.9 christos NULL, /* authdata */ 160 1.9 christos NULL, /* class */ 161 1.9 christos NULL, /* inst */ 162 1.9 christos NULL, /* opcode */ 163 1.9 christos NULL, /* sender */ 164 1.9 christos NULL, /* recipient */ 165 1.9 christos NULL, /* format */ 166 1.9 christos 0, /* cksum */ 167 1.9 christos 0, /* multi */ 168 1.9 christos NULL /* multi_uid */ 169 1.9 christos }; 170 1.6 christos const char *parse = (const char *) cp; 171 1.1 christos int parselen = length; 172 1.6 christos const char *s; 173 1.1 christos int lose = 0; 174 1.1 christos 175 1.9 christos ndo->ndo_protocol = "zephyr"; 176 1.1 christos /* squelch compiler warnings */ 177 1.1 christos 178 1.8 christos #define PARSE_STRING \ 179 1.9 christos s = parse_field(ndo, &parse, &parselen); \ 180 1.1 christos if (!s) lose = 1; 181 1.1 christos 182 1.1 christos #define PARSE_FIELD_INT(field) \ 183 1.1 christos PARSE_STRING \ 184 1.1 christos if (!lose) field = strtol(s, 0, 16); 185 1.1 christos 186 1.1 christos #define PARSE_FIELD_STR(field) \ 187 1.1 christos PARSE_STRING \ 188 1.1 christos if (!lose) field = s; 189 1.1 christos 190 1.1 christos PARSE_FIELD_STR(z.version); 191 1.9 christos if (lose) 192 1.9 christos goto invalid; 193 1.9 christos 194 1.1 christos if (strncmp(z.version, "ZEPH", 4)) 195 1.1 christos return; 196 1.1 christos 197 1.1 christos PARSE_FIELD_INT(z.numfields); 198 1.1 christos PARSE_FIELD_INT(z.kind); 199 1.1 christos PARSE_FIELD_STR(z.uid); 200 1.1 christos PARSE_FIELD_INT(z.port); 201 1.1 christos PARSE_FIELD_INT(z.auth); 202 1.1 christos PARSE_FIELD_INT(z.authlen); 203 1.1 christos PARSE_FIELD_STR(z.authdata); 204 1.1 christos PARSE_FIELD_STR(z.class); 205 1.1 christos PARSE_FIELD_STR(z.inst); 206 1.1 christos PARSE_FIELD_STR(z.opcode); 207 1.1 christos PARSE_FIELD_STR(z.sender); 208 1.1 christos PARSE_FIELD_STR(z.recipient); 209 1.1 christos PARSE_FIELD_STR(z.format); 210 1.1 christos PARSE_FIELD_INT(z.cksum); 211 1.1 christos PARSE_FIELD_INT(z.multi); 212 1.1 christos PARSE_FIELD_STR(z.multi_uid); 213 1.1 christos 214 1.8 christos if (lose) 215 1.9 christos goto invalid; 216 1.1 christos 217 1.9 christos ND_PRINT(" zephyr"); 218 1.1 christos if (strncmp(z.version+4, "0.2", 3)) { 219 1.9 christos ZEPHYR_PRINT(" v", z.version+4) 220 1.1 christos return; 221 1.1 christos } 222 1.1 christos 223 1.9 christos ND_PRINT(" %s", tok2str(z_types, "type %d", z.kind)); 224 1.1 christos if (z.kind == Z_PACKET_SERVACK) { 225 1.1 christos /* Initialization to silence warnings */ 226 1.6 christos const char *ackdata = NULL; 227 1.1 christos PARSE_FIELD_STR(ackdata); 228 1.1 christos if (!lose && strcmp(ackdata, "SENT")) 229 1.9 christos ZEPHYR_PRINT("/", str_to_lower(ackdata)) 230 1.1 christos } 231 1.9 christos if (*z.sender) ZEPHYR_PRINT(" ", z.sender); 232 1.1 christos 233 1.1 christos if (!strcmp(z.class, "USER_LOCATE")) { 234 1.1 christos if (!strcmp(z.opcode, "USER_HIDE")) 235 1.9 christos ND_PRINT(" hide"); 236 1.1 christos else if (!strcmp(z.opcode, "USER_UNHIDE")) 237 1.9 christos ND_PRINT(" unhide"); 238 1.1 christos else 239 1.9 christos ZEPHYR_PRINT(" locate ", z.inst); 240 1.1 christos return; 241 1.1 christos } 242 1.1 christos 243 1.1 christos if (!strcmp(z.class, "ZEPHYR_ADMIN")) { 244 1.9 christos ZEPHYR_PRINT(" zephyr-admin ", str_to_lower(z.opcode)); 245 1.1 christos return; 246 1.1 christos } 247 1.1 christos 248 1.1 christos if (!strcmp(z.class, "ZEPHYR_CTL")) { 249 1.1 christos if (!strcmp(z.inst, "CLIENT")) { 250 1.1 christos if (!strcmp(z.opcode, "SUBSCRIBE") || 251 1.1 christos !strcmp(z.opcode, "SUBSCRIBE_NODEFS") || 252 1.1 christos !strcmp(z.opcode, "UNSUBSCRIBE")) { 253 1.1 christos 254 1.9 christos ND_PRINT(" %ssub%s", strcmp(z.opcode, "SUBSCRIBE") ? "un" : "", 255 1.1 christos strcmp(z.opcode, "SUBSCRIBE_NODEFS") ? "" : 256 1.9 christos "-nodefs"); 257 1.1 christos if (z.kind != Z_PACKET_SERVACK) { 258 1.1 christos /* Initialization to silence warnings */ 259 1.6 christos const char *c = NULL, *i = NULL, *r = NULL; 260 1.1 christos PARSE_FIELD_STR(c); 261 1.1 christos PARSE_FIELD_STR(i); 262 1.1 christos PARSE_FIELD_STR(r); 263 1.9 christos if (!lose) ZEPHYR_PRINT(" ", z_triple(c, i, r)); 264 1.1 christos } 265 1.1 christos return; 266 1.1 christos } 267 1.1 christos 268 1.1 christos if (!strcmp(z.opcode, "GIMME")) { 269 1.9 christos ND_PRINT(" ret"); 270 1.1 christos return; 271 1.1 christos } 272 1.1 christos 273 1.1 christos if (!strcmp(z.opcode, "GIMMEDEFS")) { 274 1.9 christos ND_PRINT(" gimme-defs"); 275 1.1 christos return; 276 1.1 christos } 277 1.1 christos 278 1.1 christos if (!strcmp(z.opcode, "CLEARSUB")) { 279 1.9 christos ND_PRINT(" clear-subs"); 280 1.1 christos return; 281 1.1 christos } 282 1.1 christos 283 1.9 christos ZEPHYR_PRINT(" ", str_to_lower(z.opcode)); 284 1.1 christos return; 285 1.1 christos } 286 1.1 christos 287 1.1 christos if (!strcmp(z.inst, "HM")) { 288 1.9 christos ZEPHYR_PRINT(" ", str_to_lower(z.opcode)); 289 1.1 christos return; 290 1.1 christos } 291 1.1 christos 292 1.1 christos if (!strcmp(z.inst, "REALM")) { 293 1.1 christos if (!strcmp(z.opcode, "ADD_SUBSCRIBE")) 294 1.9 christos ND_PRINT(" realm add-subs"); 295 1.1 christos if (!strcmp(z.opcode, "REQ_SUBSCRIBE")) 296 1.9 christos ND_PRINT(" realm req-subs"); 297 1.1 christos if (!strcmp(z.opcode, "RLM_SUBSCRIBE")) 298 1.9 christos ND_PRINT(" realm rlm-sub"); 299 1.1 christos if (!strcmp(z.opcode, "RLM_UNSUBSCRIBE")) 300 1.9 christos ND_PRINT(" realm rlm-unsub"); 301 1.1 christos return; 302 1.1 christos } 303 1.1 christos } 304 1.1 christos 305 1.1 christos if (!strcmp(z.class, "HM_CTL")) { 306 1.9 christos ZEPHYR_PRINT(" hm_ctl ", str_to_lower(z.inst)); 307 1.9 christos ZEPHYR_PRINT(" ", str_to_lower(z.opcode)); 308 1.1 christos return; 309 1.1 christos } 310 1.1 christos 311 1.1 christos if (!strcmp(z.class, "HM_STAT")) { 312 1.1 christos if (!strcmp(z.inst, "HMST_CLIENT") && !strcmp(z.opcode, "GIMMESTATS")) { 313 1.9 christos ND_PRINT(" get-client-stats"); 314 1.1 christos return; 315 1.1 christos } 316 1.1 christos } 317 1.1 christos 318 1.1 christos if (!strcmp(z.class, "WG_CTL")) { 319 1.9 christos ZEPHYR_PRINT(" wg_ctl ", str_to_lower(z.inst)); 320 1.9 christos ZEPHYR_PRINT(" ", str_to_lower(z.opcode)); 321 1.1 christos return; 322 1.1 christos } 323 1.1 christos 324 1.1 christos if (!strcmp(z.class, "LOGIN")) { 325 1.1 christos if (!strcmp(z.opcode, "USER_FLUSH")) { 326 1.9 christos ND_PRINT(" flush_locs"); 327 1.1 christos return; 328 1.1 christos } 329 1.1 christos 330 1.1 christos if (!strcmp(z.opcode, "NONE") || 331 1.1 christos !strcmp(z.opcode, "OPSTAFF") || 332 1.1 christos !strcmp(z.opcode, "REALM-VISIBLE") || 333 1.1 christos !strcmp(z.opcode, "REALM-ANNOUNCED") || 334 1.1 christos !strcmp(z.opcode, "NET-VISIBLE") || 335 1.1 christos !strcmp(z.opcode, "NET-ANNOUNCED")) { 336 1.9 christos ZEPHYR_PRINT(" set-exposure ", str_to_lower(z.opcode)); 337 1.1 christos return; 338 1.1 christos } 339 1.1 christos } 340 1.1 christos 341 1.1 christos if (!*z.recipient) 342 1.1 christos z.recipient = "*"; 343 1.1 christos 344 1.9 christos ZEPHYR_PRINT(" to ", z_triple(z.class, z.inst, z.recipient)); 345 1.1 christos if (*z.opcode) 346 1.9 christos ZEPHYR_PRINT(" op ", z.opcode); 347 1.8 christos return; 348 1.8 christos 349 1.9 christos invalid: 350 1.9 christos nd_print_invalid(ndo); 351 1.1 christos } 352