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