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
40struct 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
61enum 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
73static 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
86static char z_buf[256];
87
88static const char *
89parse_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
115static const char *
116z_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
125static const char *
126str_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
145void
146zephyr_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
346invalid:
347    nd_print_invalid(ndo);
348}
349