1 /*
2 * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22 /* \summary: AppleTalk printer */
23
24 #include <sys/cdefs.h>
25 #ifndef lint
26 __RCSID("$NetBSD: print-atalk.c,v 1.7 2017/02/05 04:05:05 spz Exp $");
27 #endif
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <netdissect-stdinc.h>
34
35 #include <stdio.h>
36 #include <string.h>
37
38 #include "netdissect.h"
39 #include "addrtoname.h"
40 #include "ethertype.h"
41 #include "extract.h"
42 #include "appletalk.h"
43
44 static const char tstr[] = "[|atalk]";
45
46 static const struct tok type2str[] = {
47 { ddpRTMP, "rtmp" },
48 { ddpRTMPrequest, "rtmpReq" },
49 { ddpECHO, "echo" },
50 { ddpIP, "IP" },
51 { ddpARP, "ARP" },
52 { ddpKLAP, "KLAP" },
53 { 0, NULL }
54 };
55
56 struct aarp {
57 uint16_t htype, ptype;
58 uint8_t halen, palen;
59 uint16_t op;
60 uint8_t hsaddr[6];
61 uint8_t psaddr[4];
62 uint8_t hdaddr[6];
63 uint8_t pdaddr[4];
64 };
65
66 static void atp_print(netdissect_options *, const struct atATP *, u_int);
67 static void atp_bitmap_print(netdissect_options *, u_char);
68 static void nbp_print(netdissect_options *, const struct atNBP *, u_int, u_short, u_char, u_char);
69 static const struct atNBPtuple *nbp_tuple_print(netdissect_options *ndo, const struct atNBPtuple *,
70 const u_char *,
71 u_short, u_char, u_char);
72 static const struct atNBPtuple *nbp_name_print(netdissect_options *, const struct atNBPtuple *,
73 const u_char *);
74 static const char *ataddr_string(netdissect_options *, u_short, u_char);
75 static void ddp_print(netdissect_options *, const u_char *, u_int, int, u_short, u_char, u_char);
76 static const char *ddpskt_string(netdissect_options *, int);
77
78 /*
79 * Print LLAP packets received on a physical LocalTalk interface.
80 */
81 u_int
ltalk_if_print(netdissect_options * ndo,const struct pcap_pkthdr * h,const u_char * p)82 ltalk_if_print(netdissect_options *ndo,
83 const struct pcap_pkthdr *h, const u_char *p)
84 {
85 u_int hdrlen;
86
87 hdrlen = llap_print(ndo, p, h->len);
88 if (hdrlen == 0) {
89 /* Cut short by the snapshot length. */
90 return (h->caplen);
91 }
92 return (hdrlen);
93 }
94
95 /*
96 * Print AppleTalk LLAP packets.
97 */
98 u_int
llap_print(netdissect_options * ndo,register const u_char * bp,u_int length)99 llap_print(netdissect_options *ndo,
100 register const u_char *bp, u_int length)
101 {
102 register const struct LAP *lp;
103 register const struct atDDP *dp;
104 register const struct atShortDDP *sdp;
105 u_short snet;
106 u_int hdrlen;
107
108 if (length < sizeof(*lp)) {
109 ND_PRINT((ndo, " [|llap %u]", length));
110 return (length);
111 }
112 if (!ND_TTEST2(*bp, sizeof(*lp))) {
113 ND_PRINT((ndo, " [|llap]"));
114 return (0); /* cut short by the snapshot length */
115 }
116 lp = (const struct LAP *)bp;
117 bp += sizeof(*lp);
118 length -= sizeof(*lp);
119 hdrlen = sizeof(*lp);
120 switch (lp->type) {
121
122 case lapShortDDP:
123 if (length < ddpSSize) {
124 ND_PRINT((ndo, " [|sddp %u]", length));
125 return (length);
126 }
127 if (!ND_TTEST2(*bp, ddpSSize)) {
128 ND_PRINT((ndo, " [|sddp]"));
129 return (0); /* cut short by the snapshot length */
130 }
131 sdp = (const struct atShortDDP *)bp;
132 ND_PRINT((ndo, "%s.%s",
133 ataddr_string(ndo, 0, lp->src), ddpskt_string(ndo, sdp->srcSkt)));
134 ND_PRINT((ndo, " > %s.%s:",
135 ataddr_string(ndo, 0, lp->dst), ddpskt_string(ndo, sdp->dstSkt)));
136 bp += ddpSSize;
137 length -= ddpSSize;
138 hdrlen += ddpSSize;
139 ddp_print(ndo, bp, length, sdp->type, 0, lp->src, sdp->srcSkt);
140 break;
141
142 case lapDDP:
143 if (length < ddpSize) {
144 ND_PRINT((ndo, " [|ddp %u]", length));
145 return (length);
146 }
147 if (!ND_TTEST2(*bp, ddpSize)) {
148 ND_PRINT((ndo, " [|ddp]"));
149 return (0); /* cut short by the snapshot length */
150 }
151 dp = (const struct atDDP *)bp;
152 snet = EXTRACT_16BITS(&dp->srcNet);
153 ND_PRINT((ndo, "%s.%s", ataddr_string(ndo, snet, dp->srcNode),
154 ddpskt_string(ndo, dp->srcSkt)));
155 ND_PRINT((ndo, " > %s.%s:",
156 ataddr_string(ndo, EXTRACT_16BITS(&dp->dstNet), dp->dstNode),
157 ddpskt_string(ndo, dp->dstSkt)));
158 bp += ddpSize;
159 length -= ddpSize;
160 hdrlen += ddpSize;
161 ddp_print(ndo, bp, length, dp->type, snet, dp->srcNode, dp->srcSkt);
162 break;
163
164 #ifdef notdef
165 case lapKLAP:
166 klap_print(bp, length);
167 break;
168 #endif
169
170 default:
171 ND_PRINT((ndo, "%d > %d at-lap#%d %u",
172 lp->src, lp->dst, lp->type, length));
173 break;
174 }
175 return (hdrlen);
176 }
177
178 /*
179 * Print EtherTalk/TokenTalk packets (or FDDITalk, or whatever it's called
180 * when it runs over FDDI; yes, I've seen FDDI captures with AppleTalk
181 * packets in them).
182 */
183 void
atalk_print(netdissect_options * ndo,register const u_char * bp,u_int length)184 atalk_print(netdissect_options *ndo,
185 register const u_char *bp, u_int length)
186 {
187 register const struct atDDP *dp;
188 u_short snet;
189
190 if(!ndo->ndo_eflag)
191 ND_PRINT((ndo, "AT "));
192
193 if (length < ddpSize) {
194 ND_PRINT((ndo, " [|ddp %u]", length));
195 return;
196 }
197 if (!ND_TTEST2(*bp, ddpSize)) {
198 ND_PRINT((ndo, " [|ddp]"));
199 return;
200 }
201 dp = (const struct atDDP *)bp;
202 snet = EXTRACT_16BITS(&dp->srcNet);
203 ND_PRINT((ndo, "%s.%s", ataddr_string(ndo, snet, dp->srcNode),
204 ddpskt_string(ndo, dp->srcSkt)));
205 ND_PRINT((ndo, " > %s.%s: ",
206 ataddr_string(ndo, EXTRACT_16BITS(&dp->dstNet), dp->dstNode),
207 ddpskt_string(ndo, dp->dstSkt)));
208 bp += ddpSize;
209 length -= ddpSize;
210 ddp_print(ndo, bp, length, dp->type, snet, dp->srcNode, dp->srcSkt);
211 }
212
213 /* XXX should probably pass in the snap header and do checks like arp_print() */
214 void
aarp_print(netdissect_options * ndo,register const u_char * bp,u_int length)215 aarp_print(netdissect_options *ndo,
216 register const u_char *bp, u_int length)
217 {
218 register const struct aarp *ap;
219
220 #define AT(member) ataddr_string(ndo, (ap->member[1]<<8)|ap->member[2],ap->member[3])
221
222 ND_PRINT((ndo, "aarp "));
223 ap = (const struct aarp *)bp;
224 if (!ND_TTEST(*ap)) {
225 /* Just bail if we don't have the whole chunk. */
226 ND_PRINT((ndo, " [|aarp]"));
227 return;
228 }
229 if (length < sizeof(*ap)) {
230 ND_PRINT((ndo, " [|aarp %u]", length));
231 return;
232 }
233 if (EXTRACT_16BITS(&ap->htype) == 1 &&
234 EXTRACT_16BITS(&ap->ptype) == ETHERTYPE_ATALK &&
235 ap->halen == 6 && ap->palen == 4 )
236 switch (EXTRACT_16BITS(&ap->op)) {
237
238 case 1: /* request */
239 ND_PRINT((ndo, "who-has %s tell %s", AT(pdaddr), AT(psaddr)));
240 return;
241
242 case 2: /* response */
243 ND_PRINT((ndo, "reply %s is-at %s", AT(psaddr), etheraddr_string(ndo, ap->hsaddr)));
244 return;
245
246 case 3: /* probe (oy!) */
247 ND_PRINT((ndo, "probe %s tell %s", AT(pdaddr), AT(psaddr)));
248 return;
249 }
250 ND_PRINT((ndo, "len %u op %u htype %u ptype %#x halen %u palen %u",
251 length, EXTRACT_16BITS(&ap->op), EXTRACT_16BITS(&ap->htype),
252 EXTRACT_16BITS(&ap->ptype), ap->halen, ap->palen));
253 }
254
255 /*
256 * Print AppleTalk Datagram Delivery Protocol packets.
257 */
258 static void
ddp_print(netdissect_options * ndo,register const u_char * bp,register u_int length,register int t,register u_short snet,register u_char snode,u_char skt)259 ddp_print(netdissect_options *ndo,
260 register const u_char *bp, register u_int length, register int t,
261 register u_short snet, register u_char snode, u_char skt)
262 {
263
264 switch (t) {
265
266 case ddpNBP:
267 nbp_print(ndo, (const struct atNBP *)bp, length, snet, snode, skt);
268 break;
269
270 case ddpATP:
271 atp_print(ndo, (const struct atATP *)bp, length);
272 break;
273
274 case ddpEIGRP:
275 eigrp_print(ndo, bp, length);
276 break;
277
278 default:
279 ND_PRINT((ndo, " at-%s %d", tok2str(type2str, NULL, t), length));
280 break;
281 }
282 }
283
284 static void
atp_print(netdissect_options * ndo,register const struct atATP * ap,u_int length)285 atp_print(netdissect_options *ndo,
286 register const struct atATP *ap, u_int length)
287 {
288 char c;
289 uint32_t data;
290
291 if ((const u_char *)(ap + 1) > ndo->ndo_snapend) {
292 /* Just bail if we don't have the whole chunk. */
293 ND_PRINT((ndo, "%s", tstr));
294 return;
295 }
296 if (length < sizeof(*ap)) {
297 ND_PRINT((ndo, " [|atp %u]", length));
298 return;
299 }
300 length -= sizeof(*ap);
301 switch (ap->control & 0xc0) {
302
303 case atpReqCode:
304 ND_PRINT((ndo, " atp-req%s %d",
305 ap->control & atpXO? " " : "*",
306 EXTRACT_16BITS(&ap->transID)));
307
308 atp_bitmap_print(ndo, ap->bitmap);
309
310 if (length != 0)
311 ND_PRINT((ndo, " [len=%u]", length));
312
313 switch (ap->control & (atpEOM|atpSTS)) {
314 case atpEOM:
315 ND_PRINT((ndo, " [EOM]"));
316 break;
317 case atpSTS:
318 ND_PRINT((ndo, " [STS]"));
319 break;
320 case atpEOM|atpSTS:
321 ND_PRINT((ndo, " [EOM,STS]"));
322 break;
323 }
324 break;
325
326 case atpRspCode:
327 ND_PRINT((ndo, " atp-resp%s%d:%d (%u)",
328 ap->control & atpEOM? "*" : " ",
329 EXTRACT_16BITS(&ap->transID), ap->bitmap, length));
330 switch (ap->control & (atpXO|atpSTS)) {
331 case atpXO:
332 ND_PRINT((ndo, " [XO]"));
333 break;
334 case atpSTS:
335 ND_PRINT((ndo, " [STS]"));
336 break;
337 case atpXO|atpSTS:
338 ND_PRINT((ndo, " [XO,STS]"));
339 break;
340 }
341 break;
342
343 case atpRelCode:
344 ND_PRINT((ndo, " atp-rel %d", EXTRACT_16BITS(&ap->transID)));
345
346 atp_bitmap_print(ndo, ap->bitmap);
347
348 /* length should be zero */
349 if (length)
350 ND_PRINT((ndo, " [len=%u]", length));
351
352 /* there shouldn't be any control flags */
353 if (ap->control & (atpXO|atpEOM|atpSTS)) {
354 c = '[';
355 if (ap->control & atpXO) {
356 ND_PRINT((ndo, "%cXO", c));
357 c = ',';
358 }
359 if (ap->control & atpEOM) {
360 ND_PRINT((ndo, "%cEOM", c));
361 c = ',';
362 }
363 if (ap->control & atpSTS) {
364 ND_PRINT((ndo, "%cSTS", c));
365 c = ',';
366 }
367 ND_PRINT((ndo, "]"));
368 }
369 break;
370
371 default:
372 ND_PRINT((ndo, " atp-0x%x %d (%u)", ap->control,
373 EXTRACT_16BITS(&ap->transID), length));
374 break;
375 }
376 data = EXTRACT_32BITS(&ap->userData);
377 if (data != 0)
378 ND_PRINT((ndo, " 0x%x", data));
379 }
380
381 static void
atp_bitmap_print(netdissect_options * ndo,register u_char bm)382 atp_bitmap_print(netdissect_options *ndo,
383 register u_char bm)
384 {
385 register char c;
386 register int i;
387
388 /*
389 * The '& 0xff' below is needed for compilers that want to sign
390 * extend a u_char, which is the case with the Ultrix compiler.
391 * (gcc is smart enough to eliminate it, at least on the Sparc).
392 */
393 if ((bm + 1) & (bm & 0xff)) {
394 c = '<';
395 for (i = 0; bm; ++i) {
396 if (bm & 1) {
397 ND_PRINT((ndo, "%c%d", c, i));
398 c = ',';
399 }
400 bm >>= 1;
401 }
402 ND_PRINT((ndo, ">"));
403 } else {
404 for (i = 0; bm; ++i)
405 bm >>= 1;
406 if (i > 1)
407 ND_PRINT((ndo, "<0-%d>", i - 1));
408 else
409 ND_PRINT((ndo, "<0>"));
410 }
411 }
412
413 static void
nbp_print(netdissect_options * ndo,register const struct atNBP * np,u_int length,register u_short snet,register u_char snode,register u_char skt)414 nbp_print(netdissect_options *ndo,
415 register const struct atNBP *np, u_int length, register u_short snet,
416 register u_char snode, register u_char skt)
417 {
418 register const struct atNBPtuple *tp =
419 (const struct atNBPtuple *)((const u_char *)np + nbpHeaderSize);
420 int i;
421 const u_char *ep;
422
423 if (length < nbpHeaderSize) {
424 ND_PRINT((ndo, " truncated-nbp %u", length));
425 return;
426 }
427
428 length -= nbpHeaderSize;
429 if (length < 8) {
430 /* must be room for at least one tuple */
431 ND_PRINT((ndo, " truncated-nbp %u", length + nbpHeaderSize));
432 return;
433 }
434 /* ep points to end of available data */
435 ep = ndo->ndo_snapend;
436 if ((const u_char *)tp > ep) {
437 ND_PRINT((ndo, "%s", tstr));
438 return;
439 }
440 switch (i = np->control & 0xf0) {
441
442 case nbpBrRq:
443 case nbpLkUp:
444 ND_PRINT((ndo, i == nbpLkUp? " nbp-lkup %d:":" nbp-brRq %d:", np->id));
445 if ((const u_char *)(tp + 1) > ep) {
446 ND_PRINT((ndo, "%s", tstr));
447 return;
448 }
449 (void)nbp_name_print(ndo, tp, ep);
450 /*
451 * look for anomalies: the spec says there can only
452 * be one tuple, the address must match the source
453 * address and the enumerator should be zero.
454 */
455 if ((np->control & 0xf) != 1)
456 ND_PRINT((ndo, " [ntup=%d]", np->control & 0xf));
457 if (tp->enumerator)
458 ND_PRINT((ndo, " [enum=%d]", tp->enumerator));
459 if (EXTRACT_16BITS(&tp->net) != snet ||
460 tp->node != snode || tp->skt != skt)
461 ND_PRINT((ndo, " [addr=%s.%d]",
462 ataddr_string(ndo, EXTRACT_16BITS(&tp->net),
463 tp->node), tp->skt));
464 break;
465
466 case nbpLkUpReply:
467 ND_PRINT((ndo, " nbp-reply %d:", np->id));
468
469 /* print each of the tuples in the reply */
470 for (i = np->control & 0xf; --i >= 0 && tp; )
471 tp = nbp_tuple_print(ndo, tp, ep, snet, snode, skt);
472 break;
473
474 default:
475 ND_PRINT((ndo, " nbp-0x%x %d (%u)", np->control, np->id, length));
476 break;
477 }
478 }
479
480 /* print a counted string */
481 static const char *
print_cstring(netdissect_options * ndo,register const char * cp,register const u_char * ep)482 print_cstring(netdissect_options *ndo,
483 register const char *cp, register const u_char *ep)
484 {
485 register u_int length;
486
487 if (cp >= (const char *)ep) {
488 ND_PRINT((ndo, "%s", tstr));
489 return (0);
490 }
491 length = *cp++;
492
493 /* Spec says string can be at most 32 bytes long */
494 if (length > 32) {
495 ND_PRINT((ndo, "[len=%u]", length));
496 return (0);
497 }
498 while ((int)--length >= 0) {
499 if (cp >= (const char *)ep) {
500 ND_PRINT((ndo, "%s", tstr));
501 return (0);
502 }
503 ND_PRINT((ndo, "%c", *cp++));
504 }
505 return (cp);
506 }
507
508 static const struct atNBPtuple *
nbp_tuple_print(netdissect_options * ndo,register const struct atNBPtuple * tp,register const u_char * ep,register u_short snet,register u_char snode,register u_char skt)509 nbp_tuple_print(netdissect_options *ndo,
510 register const struct atNBPtuple *tp, register const u_char *ep,
511 register u_short snet, register u_char snode, register u_char skt)
512 {
513 register const struct atNBPtuple *tpn;
514
515 if ((const u_char *)(tp + 1) > ep) {
516 ND_PRINT((ndo, "%s", tstr));
517 return 0;
518 }
519 tpn = nbp_name_print(ndo, tp, ep);
520
521 /* if the enumerator isn't 1, print it */
522 if (tp->enumerator != 1)
523 ND_PRINT((ndo, "(%d)", tp->enumerator));
524
525 /* if the socket doesn't match the src socket, print it */
526 if (tp->skt != skt)
527 ND_PRINT((ndo, " %d", tp->skt));
528
529 /* if the address doesn't match the src address, it's an anomaly */
530 if (EXTRACT_16BITS(&tp->net) != snet || tp->node != snode)
531 ND_PRINT((ndo, " [addr=%s]",
532 ataddr_string(ndo, EXTRACT_16BITS(&tp->net), tp->node)));
533
534 return (tpn);
535 }
536
537 static const struct atNBPtuple *
nbp_name_print(netdissect_options * ndo,const struct atNBPtuple * tp,register const u_char * ep)538 nbp_name_print(netdissect_options *ndo,
539 const struct atNBPtuple *tp, register const u_char *ep)
540 {
541 register const char *cp = (const char *)tp + nbpTupleSize;
542
543 ND_PRINT((ndo, " "));
544
545 /* Object */
546 ND_PRINT((ndo, "\""));
547 if ((cp = print_cstring(ndo, cp, ep)) != NULL) {
548 /* Type */
549 ND_PRINT((ndo, ":"));
550 if ((cp = print_cstring(ndo, cp, ep)) != NULL) {
551 /* Zone */
552 ND_PRINT((ndo, "@"));
553 if ((cp = print_cstring(ndo, cp, ep)) != NULL)
554 ND_PRINT((ndo, "\""));
555 }
556 }
557 return ((const struct atNBPtuple *)cp);
558 }
559
560
561 #define HASHNAMESIZE 4096
562
563 struct hnamemem {
564 int addr;
565 char *name;
566 struct hnamemem *nxt;
567 };
568
569 static struct hnamemem hnametable[HASHNAMESIZE];
570
571 static const char *
ataddr_string(netdissect_options * ndo,u_short atnet,u_char athost)572 ataddr_string(netdissect_options *ndo,
573 u_short atnet, u_char athost)
574 {
575 register struct hnamemem *tp, *tp2;
576 register int i = (atnet << 8) | athost;
577 char nambuf[256+1];
578 static int first = 1;
579 FILE *fp;
580
581 /*
582 * if this is the first call, see if there's an AppleTalk
583 * number to name map file.
584 */
585 if (first && (first = 0, !ndo->ndo_nflag)
586 && (fp = fopen("/etc/atalk.names", "r"))) {
587 char line[256];
588 int i1, i2;
589
590 while (fgets(line, sizeof(line), fp)) {
591 if (line[0] == '\n' || line[0] == 0 || line[0] == '#')
592 continue;
593 if (sscanf(line, "%d.%d %256s", &i1, &i2, nambuf) == 3)
594 /* got a hostname. */
595 i2 |= (i1 << 8);
596 else if (sscanf(line, "%d %256s", &i1, nambuf) == 2)
597 /* got a net name */
598 i2 = (i1 << 8) | 255;
599 else
600 continue;
601
602 for (tp = &hnametable[i2 & (HASHNAMESIZE-1)];
603 tp->nxt; tp = tp->nxt)
604 ;
605 tp->addr = i2;
606 tp->nxt = newhnamemem(ndo);
607 tp->name = strdup(nambuf);
608 if (tp->name == NULL)
609 (*ndo->ndo_error)(ndo,
610 "ataddr_string: strdup(nambuf)");
611 }
612 fclose(fp);
613 }
614
615 for (tp = &hnametable[i & (HASHNAMESIZE-1)]; tp->nxt; tp = tp->nxt)
616 if (tp->addr == i)
617 return (tp->name);
618
619 /* didn't have the node name -- see if we've got the net name */
620 i |= 255;
621 for (tp2 = &hnametable[i & (HASHNAMESIZE-1)]; tp2->nxt; tp2 = tp2->nxt)
622 if (tp2->addr == i) {
623 tp->addr = (atnet << 8) | athost;
624 tp->nxt = newhnamemem(ndo);
625 (void)snprintf(nambuf, sizeof(nambuf), "%s.%d",
626 tp2->name, athost);
627 tp->name = strdup(nambuf);
628 if (tp->name == NULL)
629 (*ndo->ndo_error)(ndo,
630 "ataddr_string: strdup(nambuf)");
631 return (tp->name);
632 }
633
634 tp->addr = (atnet << 8) | athost;
635 tp->nxt = newhnamemem(ndo);
636 if (athost != 255)
637 (void)snprintf(nambuf, sizeof(nambuf), "%d.%d", atnet, athost);
638 else
639 (void)snprintf(nambuf, sizeof(nambuf), "%d", atnet);
640 tp->name = strdup(nambuf);
641 if (tp->name == NULL)
642 (*ndo->ndo_error)(ndo, "ataddr_string: strdup(nambuf)");
643
644 return (tp->name);
645 }
646
647 static const struct tok skt2str[] = {
648 { rtmpSkt, "rtmp" }, /* routing table maintenance */
649 { nbpSkt, "nis" }, /* name info socket */
650 { echoSkt, "echo" }, /* AppleTalk echo protocol */
651 { zipSkt, "zip" }, /* zone info protocol */
652 { 0, NULL }
653 };
654
655 static const char *
ddpskt_string(netdissect_options * ndo,register int skt)656 ddpskt_string(netdissect_options *ndo,
657 register int skt)
658 {
659 static char buf[8];
660
661 if (ndo->ndo_nflag) {
662 (void)snprintf(buf, sizeof(buf), "%d", skt);
663 return (buf);
664 }
665 return (tok2str(skt2str, "%d", skt));
666 }
667