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