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