1/*	$OpenBSD: print-domain.c,v 1.28 2023/03/01 08:15:58 claudio Exp $	*/
2
3/*
4 * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that: (1) source code distributions
9 * retain the above copyright notice and this paragraph in its entirety, (2)
10 * distributions including binary code include the above copyright notice and
11 * this paragraph in its entirety in the documentation or other materials
12 * provided with the distribution, and (3) all advertising materials mentioning
13 * features or use of this software display the following acknowledgement:
14 * ``This product includes software developed by the University of California,
15 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
16 * the University nor the names of its contributors may be used to endorse
17 * or promote products derived from this software without specific prior
18 * written permission.
19 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
20 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 */
23
24#include <sys/time.h>
25#include <sys/socket.h>
26
27#include <net/if.h>
28
29#include <netinet/in.h>
30#include <netinet/if_ether.h>
31#include <netinet/ip.h>
32#include <netinet/ip_var.h>
33#include <netinet/udp.h>
34#include <netinet/udp_var.h>
35#include <netinet/tcp.h>
36
37#ifdef NOERROR
38#undef NOERROR					/* Solaris sucks */
39#endif
40#ifdef NOERROR
41#undef T_UNSPEC					/* SINIX does too */
42#endif
43#include "nameser.h"
44
45#include <stdio.h>
46#include <string.h>
47
48#include "interface.h"
49#include "addrtoname.h"
50#include "extract.h"                    /* must come after interface.h */
51
52static const char *ns_ops[] = {
53	"", " inv_q", " stat", " op3", " notify", " update", " op6", " op7",
54	" op8", " updataA", " updateD", " updateDA",
55	" updateM", " updateMA", " zoneInit", " zoneRef",
56};
57
58static const char *ns_resp[] = {
59	"", " FormErr", " ServFail", " NXDomain",
60	" NotImp", " Refused", " YXDomain", " YXRRSet",
61	" NXRRSet", " NotAuth", " NotZone", " Resp11",
62	" Resp12", " Resp13", " Resp14", " NoChange",
63};
64
65/* skip over a domain name */
66static const u_char *
67ns_nskip(const u_char *cp)
68{
69	u_char i;
70
71	if (!TTEST2(*cp, 1))
72		return (NULL);
73	i = *cp++;
74	while (i) {
75		if ((i & INDIR_MASK) == INDIR_MASK)
76			return (cp + 1);
77		if ((i & INDIR_MASK) == EDNS0_MASK) {
78			int bitlen, bytelen;
79
80			if ((i & ~INDIR_MASK) != EDNS0_ELT_BITLABEL)
81				return(NULL); /* unknown ELT */
82			if (!TTEST2(*cp, 1))
83				return (NULL);
84			if ((bitlen = *cp++) == 0)
85				bitlen = 256;
86			bytelen = (bitlen + 7) / 8;
87			cp += bytelen;
88		} else
89			cp += i;
90		if (!TTEST2(*cp, 1))
91			return (NULL);
92		i = *cp++;
93	}
94	return (cp);
95}
96
97/* print a <domain-name> */
98static const u_char *
99blabel_print(const u_char *cp)
100{
101	int bitlen, slen, b;
102	const u_char *bitp, *lim;
103	char tc;
104
105	if (!TTEST2(*cp, 1))
106		return(NULL);
107	if ((bitlen = *cp) == 0)
108		bitlen = 256;
109	slen = (bitlen + 3) / 4;
110	lim = cp + 1 + slen;
111
112	/* print the bit string as a hex string */
113	printf("\\[x");
114	for (bitp = cp + 1, b = bitlen; bitp < lim && b > 7; b -= 8, bitp++) {
115		TCHECK(*bitp);
116		printf("%02x", *bitp);
117	}
118	if (b > 4) {
119		TCHECK(*bitp);
120		tc = *bitp++;
121		printf("%02x", tc & (0xff << (8 - b)));
122	} else if (b > 0) {
123		TCHECK(*bitp);
124		tc = *bitp++;
125		printf("%1x", ((tc >> 4) & 0x0f) & (0x0f << (4 - b)));
126	}
127	printf("/%d]", bitlen);
128	return lim;
129trunc:
130	printf(".../%d]", bitlen);
131	return NULL;
132}
133
134static int
135labellen(const u_char *cp)
136{
137	u_int i;
138
139	if (!TTEST2(*cp, 1))
140		return(-1);
141	i = *cp;
142	if ((i & INDIR_MASK) == EDNS0_MASK) {
143		int bitlen, elt;
144		if ((elt = (i & ~INDIR_MASK)) != EDNS0_ELT_BITLABEL) {
145			printf("<ELT %d>", elt);
146			return(-1);
147		}
148		if (!TTEST2(*(cp + 1), 1))
149			return(-1);
150		if ((bitlen = *(cp + 1)) == 0)
151			bitlen = 256;
152		return(((bitlen + 7) / 8) + 1);
153	} else
154		return(i);
155}
156
157static const u_char *
158ns_nprint(const u_char *cp, const u_char *bp)
159{
160	u_int i, l;
161	const u_char *rp = NULL;
162	int compress = 0;
163	int chars_processed;
164	int elt;
165	int data_size = snapend - bp;
166
167	if ((l = labellen(cp)) == (u_int)-1)
168		return(NULL);
169	if (!TTEST2(*cp, 1))
170		return(NULL);
171	chars_processed = 1;
172	if (((i = *cp++) & INDIR_MASK) != INDIR_MASK) {
173		compress = 0;
174		rp = cp + l;
175	}
176
177	if (i != 0)
178		while (i && cp < snapend) {
179			if ((i & INDIR_MASK) == INDIR_MASK) {
180				if (!compress) {
181					rp = cp + 1;
182					compress = 1;
183				}
184				if (!TTEST2(*cp, 1))
185					return(NULL);
186				cp = bp + (((i << 8) | *cp) & 0x3fff);
187				if ((l = labellen(cp)) == (u_int)-1)
188					return(NULL);
189				if (!TTEST2(*cp, 1))
190					return(NULL);
191				i = *cp++;
192				chars_processed++;
193
194				/*
195				 * If we've looked at every character in
196				 * the message, this pointer will make
197				 * us look at some character again,
198				 * which means we're looping.
199				 */
200				if (chars_processed >= data_size) {
201					printf("<LOOP>");
202					return (NULL);
203				}
204				continue;
205			}
206			if ((i & INDIR_MASK) == EDNS0_MASK) {
207				elt = (i & ~INDIR_MASK);
208				switch(elt) {
209				case EDNS0_ELT_BITLABEL:
210					if (blabel_print(cp) == NULL)
211						return (NULL);
212					break;
213				default:
214					/* unknown ELT */
215					printf("<ELT %d>", elt);
216					return(NULL);
217				}
218			} else {
219				if (fn_printn(cp, l, snapend))
220					return(NULL);
221			}
222
223			cp += l;
224			chars_processed += l;
225			putchar('.');
226			if ((l = labellen(cp)) == (u_int)-1)
227				return(NULL);
228			if (!TTEST2(*cp, 1))
229				return(NULL);
230			i = *cp++;
231			chars_processed++;
232			if (!compress)
233				rp += l + 1;
234		}
235	else
236		putchar('.');
237	return (rp);
238}
239
240/* print a <character-string> */
241static const u_char *
242ns_cprint(const u_char *cp)
243{
244	u_int i;
245
246	if (!TTEST2(*cp, 1))
247		return (NULL);
248	i = *cp++;
249	if (fn_printn(cp, i, snapend))
250		return (NULL);
251	return (cp + i);
252}
253
254/* https://www.iana.org/assignments/dns-parameters */
255struct tok ns_type2str[] = {
256	{ T_A,		"A" },			/* RFC 1035 */
257	{ T_NS,		"NS" },			/* RFC 1035 */
258	{ T_MD,		"MD" },			/* RFC 1035 */
259	{ T_MF,		"MF" },			/* RFC 1035 */
260	{ T_CNAME,	"CNAME" },		/* RFC 1035 */
261	{ T_SOA,	"SOA" },		/* RFC 1035 */
262	{ T_MB,		"MB" },			/* RFC 1035 */
263	{ T_MG,		"MG" },			/* RFC 1035 */
264	{ T_MR,		"MR" },			/* RFC 1035 */
265	{ T_NULL,	"NULL" },		/* RFC 1035 */
266	{ T_WKS,	"WKS" },		/* RFC 1035 */
267	{ T_PTR,	"PTR" },		/* RFC 1035 */
268	{ T_HINFO,	"HINFO" },		/* RFC 1035 */
269	{ T_MINFO,	"MINFO" },		/* RFC 1035 */
270	{ T_MX,		"MX" },			/* RFC 1035 */
271	{ T_TXT,	"TXT" },		/* RFC 1035 */
272	{ T_RP,		"RP" },			/* RFC 1183 */
273	{ T_AFSDB,	"AFSDB" },		/* RFC 1183 */
274	{ T_X25,	"X25" },		/* RFC 1183 */
275	{ T_ISDN,	"ISDN" },		/* RFC 1183 */
276	{ T_RT,		"RT" },			/* RFC 1183 */
277	{ T_NSAP,	"NSAP" },		/* RFC 1706 */
278	{ T_NSAP_PTR,	"NSAP_PTR" },
279	{ T_SIG,	"SIG" },		/* RFC 2535 */
280	{ T_KEY,	"KEY" },		/* RFC 2535 */
281	{ T_PX,		"PX" },			/* RFC 2163 */
282	{ T_GPOS,	"GPOS" },		/* RFC 1712 */
283	{ T_AAAA,	"AAAA" },		/* RFC 1886 */
284	{ T_LOC,	"LOC" },		/* RFC 1876 */
285	{ T_NXT,	"NXT" },		/* RFC 2535 */
286	{ T_EID,	"EID" },		/* Nimrod */
287	{ T_NIMLOC,	"NIMLOC" },		/* Nimrod */
288	{ T_SRV,	"SRV" },		/* RFC 2782 */
289	{ T_ATMA,	"ATMA" },		/* ATM Forum */
290	{ T_NAPTR,	"NAPTR" },		/* RFC 2168, RFC 2915 */
291	{ T_KX,		"KX" },			/* RFC 2230 */
292	{ T_CERT,	"CERT" },		/* RFC 2538 */
293	{ T_A6,		"A6" },			/* RFC 2874 */
294	{ T_DNAME,	"DNAME" },		/* RFC 2672 */
295	{ T_SINK, 	"SINK" },
296	{ T_OPT,	"OPT" },		/* RFC 2671 */
297	{ T_APL, 	"APL" },		/* RFC 3123 */
298	{ T_DS,		"DS" },			/* RFC 4034 */
299	{ T_SSHFP,	"SSHFP" },		/* RFC 4255 */
300	{ T_IPSECKEY,	"IPSECKEY" },		/* RFC 4025 */
301	{ T_RRSIG, 	"RRSIG" },		/* RFC 4034 */
302	{ T_NSEC,	"NSEC" },		/* RFC 4034 */
303	{ T_DNSKEY,	"DNSKEY" },		/* RFC 4034 */
304	{ T_SPF,	"SPF" },		/* RFC-schlitt-spf-classic-02.txt */
305	{ T_UINFO,	"UINFO" },
306	{ T_UID,	"UID" },
307	{ T_GID,	"GID" },
308	{ T_UNSPEC,	"UNSPEC" },
309	{ T_UNSPECA,	"UNSPECA" },
310	{ T_TKEY,	"TKEY" },		/* RFC 2930 */
311	{ T_TSIG,	"TSIG" },		/* RFC 2845 */
312	{ T_IXFR,	"IXFR" },		/* RFC 1995 */
313	{ T_AXFR,	"AXFR" },		/* RFC 1035 */
314	{ T_MAILB,	"MAILB" },		/* RFC 1035 */
315	{ T_MAILA,	"MAILA" },		/* RFC 1035 */
316	{ T_ANY,	"ANY" },
317	{ 0,		NULL }
318};
319
320struct tok ns_class2str[] = {
321	{ C_IN,		"IN" },		/* Not used */
322	{ C_CHAOS,	"CHAOS" },
323	{ C_HS,		"HS" },
324	{ C_ANY,	"ANY" },
325	{ 0,		NULL }
326};
327
328/* print a query */
329static const u_char *
330ns_qprint(const u_char *cp, const u_char *bp, int is_mdns)
331{
332	const u_char *np = cp;
333	u_int i, class;
334
335	cp = ns_nskip(cp);
336
337	if (cp == NULL || !TTEST2(*cp, 4))
338		return(NULL);
339
340	/* print the qtype */
341	i = EXTRACT_16BITS(cp);
342	cp += 2;
343	printf(" %s", tok2str(ns_type2str, "Type%d", i));
344	/* print the qclass (if it's not IN) */
345	i = EXTRACT_16BITS(cp);
346	cp += 2;
347	if (is_mdns)
348		class = (i & ~C_QU);
349	else
350		class = i;
351	if (class != C_IN)
352		printf(" %s", tok2str(ns_class2str, "(Class %d)", class));
353	if (is_mdns && (i & C_QU))
354		printf(" (QU)");
355
356	printf("? ");
357	cp = ns_nprint(np, bp);
358	return(cp ? cp + 4 : NULL);
359}
360
361/* print a reply */
362static const u_char *
363ns_rprint(const u_char *cp, const u_char *bp, int is_mdns)
364{
365	u_int i, class, opt_flags = 0;
366	u_short typ, len;
367	const u_char *rp;
368
369	if (vflag) {
370		putchar(' ');
371		if ((cp = ns_nprint(cp, bp)) == NULL)
372			return NULL;
373	} else
374		cp = ns_nskip(cp);
375
376	if (cp == NULL || !TTEST2(*cp, 10))
377		return (snapend);
378
379	/* print the type/qtype */
380	typ = EXTRACT_16BITS(cp);
381	cp += 2;
382	/* print the class (if it's not IN and the type isn't OPT) */
383	i = EXTRACT_16BITS(cp);
384	cp += 2;
385	if (is_mdns)
386		class = (i & ~C_CACHE_FLUSH);
387	else
388		class = i;
389	if (class != C_IN && typ != T_OPT)
390		printf(" %s", tok2str(ns_class2str, "(Class %d)", class));
391	if (is_mdns) {
392		if (i & C_CACHE_FLUSH)
393			printf(" (Cache flush)");
394	}
395
396	if (typ == T_OPT) {
397		/* get opt flags */
398		cp += 2;
399		opt_flags = EXTRACT_16BITS(cp);
400		/* ignore rest of ttl field */
401		cp += 2;
402	} else if (vflag > 2) {
403		/* print ttl */
404		printf(" [");
405		relts_print(EXTRACT_32BITS(cp));
406		printf("]");
407		cp += 4;
408	} else {
409		/* ignore ttl */
410		cp += 4;
411	}
412
413	len = EXTRACT_16BITS(cp);
414	cp += 2;
415
416	rp = cp + len;
417
418	printf(" %s", tok2str(ns_type2str, "Type%d", typ));
419	if (rp > snapend)
420		return(NULL);
421
422	switch (typ) {
423	case T_A:
424		if (!TTEST2(*cp, sizeof(struct in_addr)))
425			return(NULL);
426		printf(" %s", ipaddr_string(cp));
427		break;
428
429	case T_NS:
430	case T_CNAME:
431	case T_PTR:
432#ifdef T_DNAME
433	case T_DNAME:
434#endif
435		putchar(' ');
436		if (ns_nprint(cp, bp) == NULL)
437			return(NULL);
438		break;
439
440	case T_SOA:
441		if (!vflag)
442			break;
443		putchar(' ');
444		if ((cp = ns_nprint(cp, bp)) == NULL)
445			return(NULL);
446		putchar(' ');
447		if ((cp = ns_nprint(cp, bp)) == NULL)
448			return(NULL);
449		if (!TTEST2(*cp, 5 * 4))
450			return(NULL);
451		printf(" %u", EXTRACT_32BITS(cp));
452		cp += 4;
453		printf(" %u", EXTRACT_32BITS(cp));
454		cp += 4;
455		printf(" %u", EXTRACT_32BITS(cp));
456		cp += 4;
457		printf(" %u", EXTRACT_32BITS(cp));
458		cp += 4;
459		printf(" %u", EXTRACT_32BITS(cp));
460		cp += 4;
461		break;
462	case T_MX:
463		putchar(' ');
464		if (!TTEST2(*cp, 2))
465			return(NULL);
466		if (ns_nprint(cp + 2, bp) == NULL)
467			return(NULL);
468		printf(" %d", EXTRACT_16BITS(cp));
469		break;
470
471	case T_TXT:
472		while (cp < rp) {
473			printf(" \"");
474			cp = ns_cprint(cp);
475			if (cp == NULL)
476				return(NULL);
477			putchar('"');
478		}
479		break;
480
481	case T_SRV:
482		putchar(' ');
483		if (!TTEST2(*cp, 6))
484			return(NULL);
485		if (ns_nprint(cp + 6, bp) == NULL)
486			return(NULL);
487		printf(":%d %d %d", EXTRACT_16BITS(cp + 4),
488			EXTRACT_16BITS(cp), EXTRACT_16BITS(cp + 2));
489		break;
490
491	case T_AAAA:
492		if (!TTEST2(*cp, sizeof(struct in6_addr)))
493			return(NULL);
494		printf(" %s", ip6addr_string(cp));
495		break;
496
497	case T_A6:
498	    {
499		struct in6_addr a;
500		int pbit, pbyte;
501
502		if (!TTEST2(*cp, 1))
503			return(NULL);
504		pbit = *cp;
505		pbyte = (pbit & ~7) / 8;
506		if (pbit > 128) {
507			printf(" %u(bad plen)", pbit);
508			break;
509		} else if (pbit < 128) {
510			if (!TTEST2(*(cp + 1), sizeof(a) - pbyte))
511				return(NULL);
512			memset(&a, 0, sizeof(a));
513			memcpy(&a.s6_addr[pbyte], cp + 1, sizeof(a) - pbyte);
514			printf(" %u %s", pbit, ip6addr_string(&a));
515		}
516		if (pbit > 0) {
517			putchar(' ');
518			if (ns_nprint(cp + 1 + sizeof(a) - pbyte, bp) == NULL)
519				return(NULL);
520		}
521		break;
522	    }
523
524	case T_OPT:
525		printf(" UDPsize=%u", class);
526		if (opt_flags & 0x8000)
527			printf(" DO");
528		break;
529
530	case T_UNSPECA:		/* One long string */
531		if (!TTEST2(*cp, len))
532			return(NULL);
533		if (fn_printn(cp, len, snapend))
534			return(NULL);
535		break;
536
537	case T_TSIG:
538	    {
539		if (cp + len > snapend)
540			return(NULL);
541		if (!vflag)
542			break;
543		putchar(' ');
544		if ((cp = ns_nprint(cp, bp)) == NULL)
545			return(NULL);
546		cp += 6;
547		if (!TTEST2(*cp, 2))
548			return(NULL);
549		printf(" fudge=%u", EXTRACT_16BITS(cp));
550		cp += 2;
551		if (!TTEST2(*cp, 2))
552			return(NULL);
553		printf(" maclen=%u", EXTRACT_16BITS(cp));
554		cp += 2 + EXTRACT_16BITS(cp);
555		if (!TTEST2(*cp, 2))
556			return(NULL);
557		printf(" origid=%u", EXTRACT_16BITS(cp));
558		cp += 2;
559		if (!TTEST2(*cp, 2))
560			return(NULL);
561		printf(" error=%u", EXTRACT_16BITS(cp));
562		cp += 2;
563		if (!TTEST2(*cp, 2))
564			return(NULL);
565		printf(" otherlen=%u", EXTRACT_16BITS(cp));
566		cp += 2;
567	    }
568	}
569	return (rp);		/* XXX This isn't always right */
570}
571
572void
573ns_print(const u_char *bp, u_int length, int is_mdns)
574{
575	const HEADER *np;
576	int qdcount, ancount, nscount, arcount;
577	const u_char *cp;
578	u_int16_t b2;
579
580	np = (const HEADER *)bp;
581	TCHECK(*np);
582	/* get the byte-order right */
583	qdcount = EXTRACT_16BITS(&np->qdcount);
584	ancount = EXTRACT_16BITS(&np->ancount);
585	nscount = EXTRACT_16BITS(&np->nscount);
586	arcount = EXTRACT_16BITS(&np->arcount);
587
588	if (DNS_QR(np)) {
589		/* this is a response */
590		printf("%d%s%s%s%s%s%s",
591			EXTRACT_16BITS(&np->id),
592			ns_ops[DNS_OPCODE(np)],
593			ns_resp[DNS_RCODE(np)],
594			DNS_AA(np)? "*" : "",
595			DNS_RA(np)? "" : "-",
596			DNS_TC(np)? "|" : "",
597			DNS_AD(np)? "$" : "");
598
599		if (qdcount != 1)
600			printf(" [%dq]", qdcount);
601		/* Print QUESTION section on -vv */
602		cp = (const u_char *)(np + 1);
603		while (qdcount--) {
604			if (qdcount < EXTRACT_16BITS(&np->qdcount) - 1)
605				putchar(',');
606			if (vflag > 1) {
607				printf(" q:");
608				if ((cp = ns_qprint(cp, bp, is_mdns)) == NULL)
609					goto trunc;
610			} else {
611				if ((cp = ns_nskip(cp)) == NULL)
612					goto trunc;
613				cp += 4;	/* skip QTYPE and QCLASS */
614			}
615		}
616		printf(" %d/%d/%d", ancount, nscount, arcount);
617		if (ancount--) {
618			if ((cp = ns_rprint(cp, bp, is_mdns)) == NULL)
619				goto trunc;
620			while (cp < snapend && ancount--) {
621				putchar(',');
622				if ((cp = ns_rprint(cp, bp, is_mdns)) == NULL)
623					goto trunc;
624			}
625		}
626		if (ancount > 0)
627			goto trunc;
628		/* Print NS and AR sections on -vv */
629		if (vflag > 1) {
630			if (cp < snapend && nscount--) {
631				printf(" ns:");
632				if ((cp = ns_rprint(cp, bp, is_mdns)) == NULL)
633					goto trunc;
634				while (cp < snapend && nscount--) {
635					putchar(',');
636					if ((cp = ns_rprint(cp, bp, is_mdns)) == NULL)
637						goto trunc;
638				}
639			}
640			if (nscount > 0)
641				goto trunc;
642			if (cp < snapend && arcount--) {
643				printf(" ar:");
644				if ((cp = ns_rprint(cp, bp, is_mdns)) == NULL)
645					goto trunc;
646				while (cp < snapend && arcount--) {
647					putchar(',');
648					if ((cp = ns_rprint(cp, bp, is_mdns)) == NULL)
649						goto trunc;
650				}
651			}
652			if (arcount > 0)
653				goto trunc;
654		}
655	} else {
656		/* this is a request */
657		printf("%d%s%s%s", EXTRACT_16BITS(&np->id), ns_ops[DNS_OPCODE(np)],
658		    DNS_RD(np) ? "+" : "",
659		    DNS_CD(np) ? "%" : "");
660
661		/* any weirdness? AA is expected in NOTIFY. */
662		b2 = EXTRACT_16BITS(((u_short *)np)+1);
663		if ((b2 & 0x6cf) !=
664		    (DNS_OPCODE(np) == NS_NOTIFY_OP ? htons(0x400) : 0))
665			printf(" [b2&3=0x%x]", b2);
666
667		if (DNS_OPCODE(np) == IQUERY) {
668			if (qdcount)
669				printf(" [%dq]", qdcount);
670			if (ancount != 1)
671				printf(" [%da]", ancount);
672		}
673		else {
674			if (ancount)
675				printf(" [%da]", ancount);
676			if (qdcount != 1)
677				printf(" [%dq]", qdcount);
678		}
679		if (nscount)
680			printf(" [%dn]", nscount);
681		if (arcount)
682			printf(" [%dau]", arcount);
683
684		cp = (const u_char *)(np + 1);
685		if (qdcount--) {
686			cp = ns_qprint(cp, (const u_char *)np, is_mdns);
687			if (!cp)
688				goto trunc;
689			while (cp < snapend && qdcount--) {
690				cp = ns_qprint((const u_char *)cp,
691					       (const u_char *)np,
692					       is_mdns);
693				if (!cp)
694					goto trunc;
695			}
696		}
697		if (qdcount > 0)
698			goto trunc;
699
700		/* Print remaining sections on -vv */
701		if (vflag > 1) {
702			if (ancount--) {
703				if ((cp = ns_rprint(cp, bp, is_mdns)) == NULL)
704					goto trunc;
705				while (cp < snapend && ancount--) {
706					putchar(',');
707					if ((cp = ns_rprint(cp, bp, is_mdns)) == NULL)
708						goto trunc;
709				}
710			}
711			if (ancount > 0)
712				goto trunc;
713			if (cp < snapend && nscount--) {
714				printf(" ns:");
715				if ((cp = ns_rprint(cp, bp, is_mdns)) == NULL)
716					goto trunc;
717				while (nscount-- && cp < snapend) {
718					putchar(',');
719					if ((cp = ns_rprint(cp, bp, is_mdns)) == NULL)
720						goto trunc;
721				}
722			}
723			if (nscount > 0)
724				goto trunc;
725			if (cp < snapend && arcount--) {
726				printf(" ar:");
727				if ((cp = ns_rprint(cp, bp, is_mdns)) == NULL)
728					goto trunc;
729				while (cp < snapend && arcount--) {
730					putchar(',');
731					if ((cp = ns_rprint(cp, bp, is_mdns)) == NULL)
732						goto trunc;
733				}
734			}
735			if (arcount > 0)
736				goto trunc;
737		}
738	}
739	printf("(%d)", length);
740	return;
741
742  trunc:
743	printf("[|domain]");
744	return;
745}
746