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