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