Home | History | Annotate | Line # | Download | only in dist
      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 #include <sys/cdefs.h>
     23 #ifndef lint
     24 __RCSID("$NetBSD: print-domain.c,v 1.12 2026/03/19 00:05:13 christos Exp $");
     25 #endif
     26 
     27 /* \summary: Domain Name System (DNS) printer */
     28 
     29 #include <config.h>
     30 
     31 #include "netdissect-stdinc.h"
     32 
     33 #include <string.h>
     34 
     35 #include "netdissect.h"
     36 #include "addrtoname.h"
     37 #include "addrtostr.h"
     38 #include "extract.h"
     39 
     40 #include "nameser.h"
     41 
     42 static const char *ns_ops[] = {
     43 	"", " inv_q", " stat", " op3", " notify", " update", " op6", " op7",
     44 	" op8", " updateA", " updateD", " updateDA",
     45 	" updateM", " updateMA", " zoneInit", " zoneRef",
     46 };
     47 
     48 static const char *ns_resp[] = {
     49 	"", " FormErr", " ServFail", " NXDomain",
     50 	" NotImp", " Refused", " YXDomain", " YXRRSet",
     51 	" NXRRSet", " NotAuth", " NotZone", " Resp11",
     52 	" Resp12", " Resp13", " Resp14", " NoChange",
     53 	" BadVers", "Resp17", " Resp18", " Resp19",
     54 	" Resp20", "Resp21", " Resp22", " BadCookie",
     55 };
     56 
     57 static const char *
     58 ns_rcode(u_int rcode) {
     59 	static char buf[sizeof(" Resp4095")];
     60 
     61 	if (rcode < sizeof(ns_resp)/sizeof(ns_resp[0])) {
     62 		return (ns_resp[rcode]);
     63 	}
     64 	snprintf(buf, sizeof(buf), " Resp%u", rcode & 0xfff);
     65 	return (buf);
     66 }
     67 
     68 /* skip over a domain name */
     69 static const u_char *
     70 ns_nskip(netdissect_options *ndo,
     71          const u_char *cp)
     72 {
     73 	u_char i;
     74 
     75 	if (!ND_TTEST_1(cp))
     76 		return (NULL);
     77 	i = GET_U_1(cp);
     78 	cp++;
     79 	while (i) {
     80 		switch (i & TYPE_MASK) {
     81 
     82 		case TYPE_INDIR:
     83 			return (cp + 1);
     84 
     85 		case TYPE_EDNS0: {
     86 			int bitlen, bytelen;
     87 
     88 			if ((i & ~TYPE_MASK) != EDNS0_ELT_BITLABEL)
     89 				return(NULL); /* unknown ELT */
     90 			if (!ND_TTEST_1(cp))
     91 				return (NULL);
     92 			if ((bitlen = GET_U_1(cp)) == 0)
     93 				bitlen = 256;
     94 			cp++;
     95 			bytelen = (bitlen + 7) / 8;
     96 			cp += bytelen;
     97 		}
     98 		break;
     99 
    100 		case TYPE_RESERVED:
    101 			return (NULL);
    102 
    103 		case TYPE_LABEL:
    104 			cp += i;
    105 			break;
    106 		}
    107 		if (!ND_TTEST_1(cp))
    108 			return (NULL);
    109 		i = GET_U_1(cp);
    110 		cp++;
    111 	}
    112 	return (cp);
    113 }
    114 
    115 static const u_char *
    116 blabel_print(netdissect_options *ndo,
    117              const u_char *cp)
    118 {
    119 	u_int bitlen, slen, b;
    120 	const u_char *bitp, *lim;
    121 	uint8_t tc;
    122 
    123 	if (!ND_TTEST_1(cp))
    124 		return(NULL);
    125 	if ((bitlen = GET_U_1(cp)) == 0)
    126 		bitlen = 256;
    127 	slen = (bitlen + 3) / 4;
    128 	lim = cp + 1 + slen;
    129 
    130 	/* print the bit string as a hex string */
    131 	ND_PRINT("\\[x");
    132 	for (bitp = cp + 1, b = bitlen; bitp < lim && b > 7; b -= 8, bitp++) {
    133 		ND_PRINT("%02x", GET_U_1(bitp));
    134 	}
    135 	if (b > 4) {
    136 		tc = GET_U_1(bitp);
    137 		bitp++;
    138 		ND_PRINT("%02x", tc & (0xff << (8 - b)));
    139 	} else if (b > 0) {
    140 		tc = GET_U_1(bitp);
    141 		bitp++;
    142 		ND_PRINT("%1x", ((tc >> 4) & 0x0f) & (0x0f << (4 - b)));
    143 	}
    144 	ND_PRINT("/%u]", bitlen);
    145 	return lim;
    146 }
    147 
    148 static int
    149 labellen(netdissect_options *ndo,
    150          const u_char *cp)
    151 {
    152 	u_int i;
    153 
    154 	if (!ND_TTEST_1(cp))
    155 		return(-1);
    156 	i = GET_U_1(cp);
    157 	switch (i & TYPE_MASK) {
    158 
    159 	case TYPE_EDNS0: {
    160 		u_int bitlen, elt;
    161 		if ((elt = (i & ~TYPE_MASK)) != EDNS0_ELT_BITLABEL) {
    162 			ND_PRINT("<ELT %d>", elt);
    163 			return(-1);
    164 		}
    165 		if (!ND_TTEST_1(cp + 1))
    166 			return(-1);
    167 		if ((bitlen = GET_U_1(cp + 1)) == 0)
    168 			bitlen = 256;
    169 		return(((bitlen + 7) / 8) + 1);
    170 	}
    171 
    172 	case TYPE_INDIR:
    173 	case TYPE_LABEL:
    174 		return(i);
    175 
    176 	default:
    177 		/*
    178 		 * TYPE_RESERVED, but we use default to suppress compiler
    179 		 * warnings about falling out of the switch statement.
    180 		 */
    181 		ND_PRINT("<BAD LABEL TYPE>");
    182 		return(-1);
    183 	}
    184 }
    185 
    186 /* print a <domain-name> */
    187 const u_char *
    188 fqdn_print(netdissect_options *ndo,
    189           const u_char *cp, const u_char *bp)
    190 {
    191 	u_int i, l;
    192 	const u_char *rp = NULL;
    193 	int compress = 0;
    194 	u_int elt;
    195 	u_int offset, max_offset;
    196 	u_int name_chars = 0;
    197 
    198 	if ((l = labellen(ndo, cp)) == (u_int)-1)
    199 		return(NULL);
    200 	if (!ND_TTEST_1(cp))
    201 		return(NULL);
    202 	max_offset = (u_int)(cp - bp);
    203 	i = GET_U_1(cp);
    204 	cp++;
    205 	if ((i & TYPE_MASK) != TYPE_INDIR) {
    206 		compress = 0;
    207 		rp = cp + l;
    208 	}
    209 
    210 	if (i != 0) {
    211 		while (i && cp < ndo->ndo_snapend) {
    212 			switch (i & TYPE_MASK) {
    213 
    214 			case TYPE_INDIR:
    215 				if (!compress) {
    216 					rp = cp + 1;
    217 					compress = 1;
    218 				}
    219 				if (!ND_TTEST_1(cp))
    220 					return(NULL);
    221 				offset = (((i << 8) | GET_U_1(cp)) & 0x3fff);
    222 				/*
    223 				 * This must move backwards in the packet.
    224 				 * No RFC explicitly says that, but BIND's
    225 				 * name decompression code requires it,
    226 				 * as a way of preventing infinite loops
    227 				 * and other bad behavior, and it's probably
    228 				 * what was intended (compress by pointing
    229 				 * to domain name suffixes already seen in
    230 				 * the packet).
    231 				 */
    232 				if (offset >= max_offset) {
    233 					ND_PRINT("<BAD PTR>");
    234 					return(NULL);
    235 				}
    236 				max_offset = offset;
    237 				cp = bp + offset;
    238 				if (!ND_TTEST_1(cp))
    239 					return(NULL);
    240 				i = GET_U_1(cp);
    241 				if ((l = labellen(ndo, cp)) == (u_int)-1)
    242 					return(NULL);
    243 				cp++;
    244 				continue;
    245 
    246 			case TYPE_EDNS0:
    247 				elt = (i & ~TYPE_MASK);
    248 				switch(elt) {
    249 				case EDNS0_ELT_BITLABEL:
    250 					if (blabel_print(ndo, cp) == NULL)
    251 						return (NULL);
    252 					break;
    253 				default:
    254 					/* unknown ELT */
    255 					ND_PRINT("<ELT %u>", elt);
    256 					return(NULL);
    257 				}
    258 				break;
    259 
    260 			case TYPE_RESERVED:
    261 				ND_PRINT("<BAD LABEL TYPE>");
    262 				return(NULL);
    263 
    264 			case TYPE_LABEL:
    265 				if (name_chars + l <= MAXCDNAME) {
    266 					if (nd_printn(ndo, cp, l, ndo->ndo_snapend))
    267 						return(NULL);
    268 				} else if (name_chars < MAXCDNAME) {
    269 					if (nd_printn(ndo, cp,
    270 					    MAXCDNAME - name_chars, ndo->ndo_snapend))
    271 						return(NULL);
    272 				}
    273 				name_chars += l;
    274 				break;
    275 			}
    276 
    277 			cp += l;
    278 			if (name_chars <= MAXCDNAME)
    279 				ND_PRINT(".");
    280 			name_chars++;
    281 			if (!ND_TTEST_1(cp))
    282 				return(NULL);
    283 			i = GET_U_1(cp);
    284 			if ((l = labellen(ndo, cp)) == (u_int)-1)
    285 				return(NULL);
    286 			cp++;
    287 			if (!compress)
    288 				rp += l + 1;
    289 		}
    290 		if (name_chars > MAXCDNAME)
    291 			ND_PRINT("<DOMAIN NAME TOO LONG>");
    292 	} else
    293 		ND_PRINT(".");
    294 	return (rp);
    295 }
    296 
    297 /* print a <character-string> */
    298 static const u_char *
    299 ns_cprint(netdissect_options *ndo,
    300           const u_char *cp)
    301 {
    302 	u_int i;
    303 
    304 	if (!ND_TTEST_1(cp))
    305 		return (NULL);
    306 	i = GET_U_1(cp);
    307 	cp++;
    308 	if (nd_printn(ndo, cp, i, ndo->ndo_snapend))
    309 		return (NULL);
    310 	return (cp + i);
    311 }
    312 
    313 static void
    314 print_eopt_ecs(netdissect_options *ndo, const u_char *cp,
    315                u_int data_len)
    316 {
    317     u_int family, addr_bits, src_len, scope_len;
    318 
    319     u_char padded[32];
    320     char addr[INET6_ADDRSTRLEN];
    321 
    322     /* ecs option must at least contain family, src len, and scope len */
    323     if (data_len < 4) {
    324         nd_print_invalid(ndo);
    325         return;
    326     }
    327 
    328     family = GET_BE_U_2(cp);
    329     cp += 2;
    330     src_len = GET_U_1(cp);
    331     cp += 1;
    332     scope_len = GET_U_1(cp);
    333     cp += 1;
    334 
    335     if (family == 1)
    336         addr_bits = 32;
    337     else if (family == 2)
    338         addr_bits = 128;
    339     else {
    340         nd_print_invalid(ndo);
    341         return;
    342     }
    343 
    344     if (data_len - 4 > (addr_bits / 8)) {
    345         nd_print_invalid(ndo);
    346         return;
    347     }
    348     /* checks for invalid ecs scope or source length */
    349     if (src_len > addr_bits || scope_len > addr_bits || ((src_len + 7) / 8) != (data_len - 4)) {
    350         nd_print_invalid(ndo);
    351         return;
    352     }
    353 
    354     /* pad the truncated address from ecs with zeros */
    355     memset(padded, 0, sizeof(padded));
    356     memcpy(padded, cp, data_len - 4);
    357 
    358 
    359     if (family == 1)
    360         ND_PRINT("%s/%d/%d", addrtostr(padded, addr, INET_ADDRSTRLEN),
    361                 src_len, scope_len);
    362     else
    363         ND_PRINT("%s/%d/%d", addrtostr6(padded, addr, INET6_ADDRSTRLEN),
    364                 src_len, scope_len);
    365 
    366 }
    367 
    368 extern const struct tok edns_opt2str[];
    369 extern const struct tok dau_alg2str[];
    370 extern const struct tok dhu_alg2str[];
    371 extern const struct tok n3u_alg2str[];
    372 
    373 
    374 /* print an <EDNS-option> */
    375 static const u_char *
    376 eopt_print(netdissect_options *ndo,
    377           const u_char *cp)
    378 {
    379     u_int opt, data_len, i;
    380 
    381     if (!ND_TTEST_2(cp))
    382         return (NULL);
    383     opt = GET_BE_U_2(cp);
    384     cp += 2;
    385     ND_PRINT("%s", tok2str(edns_opt2str, "Opt%u", opt));
    386     if (!ND_TTEST_2(cp))
    387         return (NULL);
    388     data_len = GET_BE_U_2(cp);
    389     cp += 2;
    390 
    391     ND_TCHECK_LEN(cp, data_len);
    392 
    393     if (data_len > 0) {
    394         ND_PRINT(" ");
    395         switch (opt) {
    396 
    397         case E_ECS:
    398             print_eopt_ecs(ndo, cp, data_len);
    399             break;
    400         case E_COOKIE:
    401             if (data_len < 8 || (data_len > 8 && data_len < 16) || data_len > 40)
    402                 nd_print_invalid(ndo);
    403             else {
    404                 for (i = 0; i < data_len; ++i) {
    405                     /* split client and server cookie */
    406                     if (i == 8)
    407                         ND_PRINT(" ");
    408                     ND_PRINT("%02x", GET_U_1(cp + i));
    409                 }
    410             }
    411             break;
    412         case E_KEEPALIVE:
    413             if (data_len != 2)
    414                 nd_print_invalid(ndo);
    415             else
    416                 /* keepalive is in increments of 100ms. Convert to seconds */
    417                 ND_PRINT("%0.1f sec", (GET_BE_U_2(cp) / 10.0));
    418             break;
    419         case E_EXPIRE:
    420             if (data_len != 4)
    421                 nd_print_invalid(ndo);
    422             else
    423                 ND_PRINT("%u sec", GET_BE_U_4(cp));
    424             break;
    425         case E_PADDING:
    426             /* ignore contents and just print length */
    427             ND_PRINT("(%u)", data_len);
    428             break;
    429         case E_KEYTAG:
    430             if (data_len % 2 != 0)
    431                 nd_print_invalid(ndo);
    432             else
    433                 for (i = 0; i < data_len; i += 2) {
    434                     if (i > 0)
    435                         ND_PRINT(" ");
    436                     ND_PRINT("%u", GET_BE_U_2(cp + i));
    437                 }
    438             break;
    439         case E_DAU:
    440             for (i = 0; i < data_len; ++i) {
    441                 if (i > 0)
    442                     ND_PRINT(" ");
    443                 ND_PRINT("%s", tok2str(dau_alg2str, "Alg_%u", GET_U_1(cp + i)));
    444             }
    445             break;
    446         case E_DHU:
    447             for (i = 0; i < data_len; ++i) {
    448                 if (i > 0)
    449                     ND_PRINT(" ");
    450                 ND_PRINT("%s", tok2str(dhu_alg2str, "Alg_%u", GET_U_1(cp + i)));
    451             }
    452             break;
    453         case E_N3U:
    454             for (i = 0; i < data_len; ++i) {
    455                 if (i > 0)
    456                     ND_PRINT(" ");
    457                 ND_PRINT("%s", tok2str(n3u_alg2str, "Alg_%u", GET_U_1(cp + i)));
    458             }
    459             break;
    460         case E_CHAIN:
    461             fqdn_print(ndo, cp, cp + data_len);
    462             break;
    463         case E_NSID:
    464             /* intentional fall-through. NSID is an undefined byte string */
    465         default:
    466             for (i = 0; i < data_len; ++i)
    467                 ND_PRINT("%02x", GET_U_1(cp + i));
    468             break;
    469         }
    470     }
    471     return (cp + data_len);
    472 
    473   trunc:
    474     return (NULL);
    475 
    476 }
    477 
    478 
    479 
    480 extern const struct tok ns_type2str[];
    481 
    482 /* https://www.iana.org/assignments/dns-parameters */
    483 const struct tok ns_type2str[] = {
    484 	{ T_A,		"A" },			/* RFC 1035 */
    485 	{ T_NS,		"NS" },			/* RFC 1035 */
    486 	{ T_MD,		"MD" },			/* RFC 1035 */
    487 	{ T_MF,		"MF" },			/* RFC 1035 */
    488 	{ T_CNAME,	"CNAME" },		/* RFC 1035 */
    489 	{ T_SOA,	"SOA" },		/* RFC 1035 */
    490 	{ T_MB,		"MB" },			/* RFC 1035 */
    491 	{ T_MG,		"MG" },			/* RFC 1035 */
    492 	{ T_MR,		"MR" },			/* RFC 1035 */
    493 	{ T_NULL,	"NULL" },		/* RFC 1035 */
    494 	{ T_WKS,	"WKS" },		/* RFC 1035 */
    495 	{ T_PTR,	"PTR" },		/* RFC 1035 */
    496 	{ T_HINFO,	"HINFO" },		/* RFC 1035 */
    497 	{ T_MINFO,	"MINFO" },		/* RFC 1035 */
    498 	{ T_MX,		"MX" },			/* RFC 1035 */
    499 	{ T_TXT,	"TXT" },		/* RFC 1035 */
    500 	{ T_RP,		"RP" },			/* RFC 1183 */
    501 	{ T_AFSDB,	"AFSDB" },		/* RFC 5864 */
    502 	{ T_X25,	"X25" },		/* RFC 1183 */
    503 	{ T_ISDN,	"ISDN" },		/* RFC 1183 */
    504 	{ T_RT,		"RT" },			/* RFC 1183 */
    505 	{ T_NSAP,	"NSAP" },		/* RFC 1706 */
    506 	{ T_NSAP_PTR,	"NSAP_PTR" },		/* RFC 1706 */
    507 	{ T_SIG,	"SIG" },		/* RFC 3008 */
    508 	{ T_KEY,	"KEY" },		/* RFC 3110 */
    509 	{ T_PX,		"PX" },			/* RFC 2163 */
    510 	{ T_GPOS,	"GPOS" },		/* RFC 1712 */
    511 	{ T_AAAA,	"AAAA" },		/* RFC 3596 */
    512 	{ T_LOC,	"LOC" },		/* RFC 1876 */
    513 	{ T_NXT,	"NXT" },		/* RFC 3755 */
    514 	{ T_EID,	"EID" },		/* Nimrod */
    515 	{ T_NIMLOC,	"NIMLOC" },		/* Nimrod */
    516 	{ T_SRV,	"SRV" },		/* RFC 2782 */
    517 	{ T_ATMA,	"ATMA" },		/* ATM Forum */
    518 	{ T_NAPTR,	"NAPTR" },		/* RFC 3403 */
    519 	{ T_KX,		"KX" },			/* RFC 2230 */
    520 	{ T_CERT,	"CERT" },		/* RFC 4398 */
    521 	{ T_A6,		"A6" },			/* RFC 6563 */
    522 	{ T_DNAME,	"DNAME" },		/* RFC 6672 */
    523 	{ T_SINK,	"SINK" },
    524 	{ T_OPT,	"OPT" },		/* RFC 6891 */
    525 	{ T_APL,	"APL" },		/* RFC 3123 */
    526 	{ T_DS,		"DS" },			/* RFC 4034 */
    527 	{ T_SSHFP,	"SSHFP" },		/* RFC 4255 */
    528 	{ T_IPSECKEY,	"IPSECKEY" },		/* RFC 4025 */
    529 	{ T_RRSIG,	"RRSIG" },		/* RFC 4034 */
    530 	{ T_NSEC,	"NSEC" },		/* RFC 4034 */
    531 	{ T_DNSKEY,	"DNSKEY" },		/* RFC 4034 */
    532 	{ T_DHCID,	"DHCID" },		/* RFC 4071 */
    533 	{ T_NSEC3,	"NSEC3" },		/* RFC 5155 */
    534 	{ T_NSEC3PARAM,	"NSEC3PARAM" },		/* RFC 5155 */
    535 	{ T_TLSA,	"TLSA" },		/* RFC 6698 */
    536 	{ T_SMIMEA,	"SMIMEA" },		/* RFC 8162 */
    537 	{ T_HIP,	"HIP" },		/* RFC 8005 */
    538 	{ T_NINFO,	"NINFO" },
    539 	{ T_RKEY,	"RKEY" },
    540 	{ T_TALINK,	"TALINK" },
    541 	{ T_CDS,	"CDS" },		/* RFC 7344 */
    542 	{ T_CDNSKEY,	"CDNSKEY" },		/* RFC 7344 */
    543 	{ T_OPENPGPKEY,	"OPENPGPKEY" },		/* RFC 7929 */
    544 	{ T_CSYNC,	"CSYNC" },		/* RFC 7477 */
    545 	{ T_ZONEMD,	"ZONEMD" },		/* RFC 8976 */
    546 	{ T_SVCB,	"SVCB" },
    547 	{ T_HTTPS,	"HTTPS" },
    548 	{ T_SPF,	"SPF" },		/* RFC 7208 */
    549 	{ T_UINFO,	"UINFO" },
    550 	{ T_UID,	"UID" },
    551 	{ T_GID,	"GID" },
    552 	{ T_UNSPEC,	"UNSPEC" },
    553 	{ T_NID,	"NID" },		/* RFC 6742 */
    554 	{ T_L32,	"L32" },		/* RFC 6742 */
    555 	{ T_L64,	"L64" },		/* RFC 6742 */
    556 	{ T_LP,		"LP" },			/* RFC 6742 */
    557 	{ T_EUI48,	"EUI48" },		/* RFC 7043 */
    558 	{ T_EUI64,	"EUI64" },		/* RFC 7043 */
    559 	{ T_TKEY,	"TKEY" },		/* RFC 2930 */
    560 	{ T_TSIG,	"TSIG" },		/* RFC 8945 */
    561 	{ T_IXFR,	"IXFR" },		/* RFC 1995 */
    562 	{ T_AXFR,	"AXFR" },		/* RFC 5936 */
    563 	{ T_MAILB,	"MAILB" },		/* RFC 1035 */
    564 	{ T_MAILA,	"MAILA" },		/* RFC 1035 */
    565 	{ T_ANY,	"ANY" },		/* RFC 8482 */
    566 	{ T_URI,	"URI" },		/* RFC 7553 */
    567 	{ T_CAA,	"CAA" },		/* RFC 8659 */
    568 	{ T_AVC,	"AVC" },
    569 	{ T_DOA,	"DOA" },
    570 	{ T_AMTRELAY,	"AMTRELAY" },		/* RFC 8777 */
    571 	{ T_TA,		"TA" },
    572 	{ T_DLV,	"DLV" },		/* RFC 8749 */
    573 	{ 0,		NULL }
    574 };
    575 
    576 extern const struct tok ns_class2str[];
    577 
    578 const struct tok ns_class2str[] = {
    579 	{ C_IN,		"IN" },		/* Not used */
    580 	{ C_CHAOS,	"CHAOS" },
    581 	{ C_HS,		"HS" },
    582 	{ C_ANY,	"ANY" },
    583 	{ 0,		NULL }
    584 };
    585 
    586 const struct tok edns_opt2str[] = {
    587     { E_LLQ,        "LLQ" },
    588     { E_UL,         "UL" },
    589     { E_NSID,       "NSID" },
    590     { E_DAU,        "DAU" },
    591     { E_DHU,        "DHU" },
    592     { E_N3U,        "N3U" },
    593     { E_ECS,        "ECS" },
    594     { E_EXPIRE,     "EXPIRE" },
    595     { E_COOKIE,     "COOKIE" },
    596     { E_KEEPALIVE,  "KEEPALIVE" },
    597     { E_PADDING,    "PADDING" },
    598     { E_CHAIN,      "CHAIN" },
    599     { E_KEYTAG,     "KEY-TAG" },
    600     { E_CLIENTTAG,  "CLIENT-TAG" },
    601     { E_SERVERTAG,  "SERVER-TAG" },
    602     { 0,            NULL }
    603 };
    604 
    605 const struct tok dau_alg2str[] = {
    606     { A_DELETE,             "DELETE" },
    607     { A_RSAMD5,             "RSAMD5" },
    608     { A_DH,                 "DH" },
    609     { A_DSA,                "DS" },
    610     { A_RSASHA1,            "RSASHA1" },
    611     { A_DSA_NSEC3_SHA1,     "DSA-NSEC3-SHA1" },
    612     { A_RSASHA1_NSEC3_SHA1, "RSASHA1-NSEC3-SHA1" },
    613     { A_RSASHA256,          "RSASHA256" },
    614     { A_RSASHA512,          "RSASHA512" },
    615     { A_ECC_GOST,           "ECC-GOST" },
    616     { A_ECDSAP256SHA256,    "ECDSAP256SHA256" },
    617     { A_ECDSAP384SHA384,    "ECDSAP384SHA384" },
    618     { A_ED25519,            "ED25519" },
    619     { A_ED448,              "ED448" },
    620     { A_INDIRECT,           "INDIRECT" },
    621     { A_PRIVATEDNS,         "PRIVATEDNS" },
    622     { A_PRIVATEOID,         "PRIVATEOID" },
    623     { 0,                NULL }
    624 };
    625 
    626 const struct tok dhu_alg2str[] = {
    627     { DS_SHA1,  "SHA-1" },
    628     { DS_SHA256,"SHA-256" },
    629     { DS_GOST,  "GOST_R_34.11-94" },
    630     { DS_SHA384,"SHA-384" },
    631     { 0,    NULL }
    632 };
    633 
    634 const struct tok n3u_alg2str[] = {
    635     { NSEC_SHA1,"SHA-1" },
    636     { 0,    NULL }
    637 };
    638 
    639 /* print a query */
    640 static const u_char *
    641 ns_qprint(netdissect_options *ndo,
    642           const u_char *cp, const u_char *bp, int is_mdns)
    643 {
    644 	const u_char *np = cp;
    645 	u_int i, class;
    646 
    647 	cp = ns_nskip(ndo, cp);
    648 
    649 	if (cp == NULL || !ND_TTEST_4(cp))
    650 		return(NULL);
    651 
    652 	/* print the qtype */
    653 	i = GET_BE_U_2(cp);
    654 	cp += 2;
    655 	ND_PRINT(" %s", tok2str(ns_type2str, "Type%u", i));
    656 	/* print the qclass (if it's not IN) */
    657 	i = GET_BE_U_2(cp);
    658 	cp += 2;
    659 	if (is_mdns)
    660 		class = (i & ~C_QU);
    661 	else
    662 		class = i;
    663 	if (class != C_IN)
    664 		ND_PRINT(" %s", tok2str(ns_class2str, "(Class %u)", class));
    665 	if (is_mdns) {
    666 		ND_PRINT(i & C_QU ? " (QU)" : " (QM)");
    667 	}
    668 
    669 	ND_PRINT("? ");
    670 	cp = fqdn_print(ndo, np, bp);
    671 	return(cp ? cp + 4 : NULL);
    672 }
    673 
    674 /* print a reply */
    675 static const u_char *
    676 ns_rprint(netdissect_options *ndo,
    677           const u_char *cp, const u_char *bp, int is_mdns)
    678 {
    679 	u_int i, class, opt_flags = 0;
    680 	u_short typ, len;
    681 	const u_char *rp;
    682 
    683 	if (ndo->ndo_vflag) {
    684 		ND_PRINT(" ");
    685 		if ((cp = fqdn_print(ndo, cp, bp)) == NULL)
    686 			return NULL;
    687 	} else
    688 		cp = ns_nskip(ndo, cp);
    689 
    690 	if (cp == NULL || !ND_TTEST_LEN(cp, 10))
    691 		return (ndo->ndo_snapend);
    692 
    693 	/* print the type/qtype */
    694 	typ = GET_BE_U_2(cp);
    695 	cp += 2;
    696 	/* print the class (if it's not IN and the type isn't OPT) */
    697 	i = GET_BE_U_2(cp);
    698 	cp += 2;
    699 	if (is_mdns)
    700 		class = (i & ~C_CACHE_FLUSH);
    701 	else
    702 		class = i;
    703 	if (class != C_IN && typ != T_OPT)
    704 		ND_PRINT(" %s", tok2str(ns_class2str, "(Class %u)", class));
    705 	if (is_mdns) {
    706 		if (i & C_CACHE_FLUSH)
    707 			ND_PRINT(" (Cache flush)");
    708 	}
    709 
    710 	if (typ == T_OPT) {
    711 		/* get opt flags */
    712 		cp += 2;
    713 		opt_flags = GET_BE_U_2(cp);
    714 		/* ignore rest of ttl field */
    715 		cp += 2;
    716 	} else if (ndo->ndo_vflag > 2) {
    717 		/* print ttl */
    718 		ND_PRINT(" [");
    719 		unsigned_relts_print(ndo, GET_BE_U_4(cp));
    720 		ND_PRINT("]");
    721 		cp += 4;
    722 	} else {
    723 		/* ignore ttl */
    724 		cp += 4;
    725 	}
    726 
    727 	len = GET_BE_U_2(cp);
    728 	cp += 2;
    729 
    730 	ND_PRINT(" %s", tok2str(ns_type2str, "Type%u", typ));
    731 
    732 	ND_TCHECK_LEN(cp, len);
    733 	rp = cp + len;
    734 
    735 	switch (typ) {
    736 	case T_A:
    737 		if (!ND_TTEST_LEN(cp, sizeof(nd_ipv4)))
    738 			return(NULL);
    739 		ND_PRINT(" %s", intoa(GET_IPV4_TO_NETWORK_ORDER(cp)));
    740 		break;
    741 
    742 	case T_NS:
    743 	case T_CNAME:
    744 	case T_PTR:
    745 	case T_DNAME:
    746 		ND_PRINT(" ");
    747 		if (fqdn_print(ndo, cp, bp) == NULL)
    748 			return(NULL);
    749 		break;
    750 
    751 	case T_SOA:
    752 		if (!ndo->ndo_vflag)
    753 			break;
    754 		ND_PRINT(" ");
    755 		if ((cp = fqdn_print(ndo, cp, bp)) == NULL)
    756 			return(NULL);
    757 		ND_PRINT(" ");
    758 		if ((cp = fqdn_print(ndo, cp, bp)) == NULL)
    759 			return(NULL);
    760 		if (!ND_TTEST_LEN(cp, 5 * 4))
    761 			return(NULL);
    762 		ND_PRINT(" %u", GET_BE_U_4(cp));
    763 		cp += 4;
    764 		ND_PRINT(" %u", GET_BE_U_4(cp));
    765 		cp += 4;
    766 		ND_PRINT(" %u", GET_BE_U_4(cp));
    767 		cp += 4;
    768 		ND_PRINT(" %u", GET_BE_U_4(cp));
    769 		cp += 4;
    770 		ND_PRINT(" %u", GET_BE_U_4(cp));
    771 		cp += 4;
    772 		break;
    773 	case T_MX:
    774 		ND_PRINT(" ");
    775 		if (!ND_TTEST_2(cp))
    776 			return(NULL);
    777 		if (fqdn_print(ndo, cp + 2, bp) == NULL)
    778 			return(NULL);
    779 		ND_PRINT(" %u", GET_BE_U_2(cp));
    780 		break;
    781 
    782 	case T_TXT:
    783 		while (cp < rp) {
    784 			ND_PRINT(" \"");
    785 			cp = ns_cprint(ndo, cp);
    786 			if (cp == NULL)
    787 				return(NULL);
    788 			ND_PRINT("\"");
    789 		}
    790 		break;
    791 
    792 	case T_SRV:
    793 		ND_PRINT(" ");
    794 		if (!ND_TTEST_6(cp))
    795 			return(NULL);
    796 		if (fqdn_print(ndo, cp + 6, bp) == NULL)
    797 			return(NULL);
    798 		ND_PRINT(":%u %u %u", GET_BE_U_2(cp + 4),
    799 			  GET_BE_U_2(cp), GET_BE_U_2(cp + 2));
    800 		break;
    801 
    802 	case T_AAAA:
    803 	    {
    804 		char ntop_buf[INET6_ADDRSTRLEN];
    805 
    806 		if (!ND_TTEST_LEN(cp, sizeof(nd_ipv6)))
    807 			return(NULL);
    808 		ND_PRINT(" %s",
    809 		    addrtostr6(cp, ntop_buf, sizeof(ntop_buf)));
    810 
    811 		break;
    812 	    }
    813 
    814 	case T_A6:
    815 	    {
    816 		nd_ipv6 a;
    817 		int pbit, pbyte;
    818 		char ntop_buf[INET6_ADDRSTRLEN];
    819 
    820 		if (!ND_TTEST_1(cp))
    821 			return(NULL);
    822 		pbit = GET_U_1(cp);
    823 		pbyte = (pbit & ~7) / 8;
    824 		if (pbit > 128) {
    825 			ND_PRINT(" %u(bad plen)", pbit);
    826 			break;
    827 		} else if (pbit < 128) {
    828 			memset(a, 0, sizeof(a));
    829 			GET_CPY_BYTES(a + pbyte, cp + 1, sizeof(a) - pbyte);
    830 			ND_PRINT(" %u %s", pbit,
    831 			    addrtostr6(&a, ntop_buf, sizeof(ntop_buf)));
    832 		}
    833 		if (pbit > 0) {
    834 			ND_PRINT(" ");
    835 			if (fqdn_print(ndo, cp + 1 + sizeof(a) - pbyte, bp) == NULL)
    836 				return(NULL);
    837 		}
    838 		break;
    839 	    }
    840 
    841 	case T_URI:
    842 		if (!ND_TTEST_LEN(cp, len))
    843 			return(NULL);
    844 		if (len < 4) {
    845 			ND_PRINT(" len %u is too short (< 4)", len);
    846 			break;
    847 		}
    848 		ND_PRINT(" %u %u ", GET_BE_U_2(cp), GET_BE_U_2(cp + 2));
    849 		if (nd_printn(ndo, cp + 4, len - 4, ndo->ndo_snapend))
    850 			return(NULL);
    851 		break;
    852 
    853 	case T_OPT:
    854 		ND_PRINT(" UDPsize=%u", class);
    855 		if (opt_flags & 0x8000)
    856 			ND_PRINT(" DO");
    857         if (cp < rp) {
    858             ND_PRINT(" [");
    859             while (cp < rp) {
    860                 cp = eopt_print(ndo, cp);
    861                 if (cp == NULL)
    862                     return(NULL);
    863                 if (cp < rp)
    864                     ND_PRINT(",");
    865             }
    866             ND_PRINT("]");
    867         }
    868 		break;
    869 
    870 	case T_TSIG:
    871 	    {
    872 		if (cp + len > ndo->ndo_snapend)
    873 			return(NULL);
    874 		if (!ndo->ndo_vflag)
    875 			break;
    876 		ND_PRINT(" ");
    877 		if ((cp = fqdn_print(ndo, cp, bp)) == NULL)
    878 			return(NULL);
    879 		cp += 6;
    880 		if (!ND_TTEST_2(cp))
    881 			return(NULL);
    882 		ND_PRINT(" fudge=%u", GET_BE_U_2(cp));
    883 		cp += 2;
    884 		if (!ND_TTEST_2(cp))
    885 			return(NULL);
    886 		ND_PRINT(" maclen=%u", GET_BE_U_2(cp));
    887 		cp += 2 + GET_BE_U_2(cp);
    888 		if (!ND_TTEST_2(cp))
    889 			return(NULL);
    890 		ND_PRINT(" origid=%u", GET_BE_U_2(cp));
    891 		cp += 2;
    892 		if (!ND_TTEST_2(cp))
    893 			return(NULL);
    894 		ND_PRINT(" error=%u", GET_BE_U_2(cp));
    895 		cp += 2;
    896 		if (!ND_TTEST_2(cp))
    897 			return(NULL);
    898 		ND_PRINT(" otherlen=%u", GET_BE_U_2(cp));
    899 		cp += 2;
    900 	    }
    901 	}
    902 	return (rp);		/* XXX This isn't always right */
    903 
    904 trunc:
    905 	return(NULL);
    906 }
    907 
    908 void
    909 domain_print(netdissect_options *ndo,
    910              const u_char *bp, u_int length, int over_tcp, int is_mdns)
    911 {
    912 	const dns_header_t *np;
    913 	uint16_t flags, rcode, rdlen, type;
    914 	u_int qdcount, ancount, nscount, arcount;
    915 	u_int i;
    916 	const u_char *cp;
    917 	uint16_t b2;
    918 
    919 	ndo->ndo_protocol = "domain";
    920 
    921 	if (over_tcp) {
    922 		/*
    923 		 * The message is prefixed with a two byte length field
    924 		 * which gives the message length, excluding the two byte
    925 		 * length field. (RFC 1035 - 4.2.2. TCP usage)
    926 		 */
    927 		if (length < 2) {
    928 			ND_PRINT(" [DNS over TCP: length %u < 2]", length);
    929 			nd_print_invalid(ndo);
    930 			return;
    931 		} else {
    932 			length -= 2; /* excluding the two byte length field */
    933 			if (GET_BE_U_2(bp) != length) {
    934 				ND_PRINT(" [prefix length(%u) != length(%u)]",
    935 					 GET_BE_U_2(bp), length);
    936 				nd_print_invalid(ndo);
    937 				return;
    938 			} else {
    939 				bp += 2;
    940 				/* in over TCP case, we need to prepend a space
    941 				 * (not needed in over UDP case)
    942 				 */
    943 				ND_PRINT(" ");
    944 			}
    945 		}
    946 	}
    947 
    948 	np = (const dns_header_t *)bp;
    949 
    950 	if(length < sizeof(*np)) {
    951 		nd_print_protocol(ndo);
    952 		ND_PRINT(" [length %u < %zu]", length, sizeof(*np));
    953 		nd_print_invalid(ndo);
    954 		return;
    955 	}
    956 
    957 	ND_TCHECK_SIZE(np);
    958 	flags = GET_BE_U_2(np->flags);
    959 	/* get the byte-order right */
    960 	qdcount = GET_BE_U_2(np->qdcount);
    961 	ancount = GET_BE_U_2(np->ancount);
    962 	nscount = GET_BE_U_2(np->nscount);
    963 	arcount = GET_BE_U_2(np->arcount);
    964 
    965 	/* find the opt record to extract extended rcode */
    966 	cp = (const u_char *)(np + 1);
    967 	rcode = DNS_RCODE(flags);
    968 	for (i = 0; i < qdcount; i++) {
    969 		if ((cp = ns_nskip(ndo, cp)) == NULL)
    970 			goto print;
    971 		cp += 4;	/* skip QTYPE and QCLASS */
    972 		if (cp >= ndo->ndo_snapend)
    973 			goto print;
    974 	}
    975 	for (i = 0; i < ancount + nscount; i++) {
    976 		if ((cp = ns_nskip(ndo, cp)) == NULL)
    977 			goto print;
    978 		cp += 8;	/* skip TYPE, CLASS and TTL */
    979 		if (cp + 2 > ndo->ndo_snapend)
    980 			goto print;
    981 		rdlen = GET_BE_U_2(cp);
    982 		cp += 2 + rdlen;
    983 		if (cp >= ndo->ndo_snapend)
    984 			goto print;
    985 	}
    986 	for (i = 0; i < arcount; i++) {
    987 		if ((cp = ns_nskip(ndo, cp)) == NULL)
    988 			goto print;
    989 		if (cp + 2 > ndo->ndo_snapend)
    990 			goto print;
    991 		type = GET_BE_U_2(cp);
    992 		cp += 4;	/* skip TYPE and CLASS */
    993 		if (cp + 1 > ndo->ndo_snapend)
    994 			goto print;
    995 		if (type == T_OPT) {
    996 			rcode |= (GET_U_1(cp) << 4);
    997 			goto print;
    998 		}
    999 		cp += 4;
   1000 		if (cp + 2 > ndo->ndo_snapend)
   1001 			goto print;
   1002 		rdlen = GET_BE_U_2(cp);
   1003 		cp += 2 + rdlen;
   1004 		if (cp >= ndo->ndo_snapend)
   1005 			goto print;
   1006 	}
   1007 
   1008  print:
   1009 	if (DNS_QR(flags)) {
   1010 		/* this is a response */
   1011 		ND_PRINT("%u%s%s%s%s%s%s",
   1012 			GET_BE_U_2(np->id),
   1013 			ns_ops[DNS_OPCODE(flags)],
   1014 			ns_rcode(rcode),
   1015 			DNS_AA(flags)? "*" : "",
   1016 			DNS_RA(flags)? "" : "-",
   1017 			DNS_TC(flags)? "|" : "",
   1018 			DNS_AD(flags)? "$" : "");
   1019 
   1020 		if (qdcount != 1)
   1021 			ND_PRINT(" [%uq]", qdcount);
   1022 		/* Print QUESTION section on -vv */
   1023 		cp = (const u_char *)(np + 1);
   1024 		for (i = 0; i < qdcount; i++) {
   1025 			if (i != 0)
   1026 				ND_PRINT(",");
   1027 			if (ndo->ndo_vflag > 1) {
   1028 				ND_PRINT(" q:");
   1029 				if ((cp = ns_qprint(ndo, cp, bp, is_mdns)) == NULL)
   1030 					goto trunc;
   1031 			} else {
   1032 				if ((cp = ns_nskip(ndo, cp)) == NULL)
   1033 					goto trunc;
   1034 				cp += 4;	/* skip QTYPE and QCLASS */
   1035 			}
   1036 		}
   1037 		ND_PRINT(" %u/%u/%u", ancount, nscount, arcount);
   1038 		if (ancount) {
   1039 			if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
   1040 				goto trunc;
   1041 			ancount--;
   1042 			while (cp < ndo->ndo_snapend && ancount) {
   1043 				ND_PRINT(",");
   1044 				if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
   1045 					goto trunc;
   1046 				ancount--;
   1047 			}
   1048 		}
   1049 		if (ancount)
   1050 			goto trunc;
   1051 		/* Print NS and AR sections on -vv */
   1052 		if (ndo->ndo_vflag > 1) {
   1053 			if (cp < ndo->ndo_snapend && nscount) {
   1054 				ND_PRINT(" ns:");
   1055 				if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
   1056 					goto trunc;
   1057 				nscount--;
   1058 				while (cp < ndo->ndo_snapend && nscount) {
   1059 					ND_PRINT(",");
   1060 					if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
   1061 						goto trunc;
   1062 					nscount--;
   1063 				}
   1064 			}
   1065 			if (nscount)
   1066 				goto trunc;
   1067 			if (cp < ndo->ndo_snapend && arcount) {
   1068 				ND_PRINT(" ar:");
   1069 				if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
   1070 					goto trunc;
   1071 				arcount--;
   1072 				while (cp < ndo->ndo_snapend && arcount) {
   1073 					ND_PRINT(",");
   1074 					if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
   1075 						goto trunc;
   1076 					arcount--;
   1077 				}
   1078 			}
   1079 			if (arcount)
   1080 				goto trunc;
   1081 		}
   1082 	} else {
   1083 		/* this is a request */
   1084 		ND_PRINT("%u%s%s%s", GET_BE_U_2(np->id),
   1085 			  ns_ops[DNS_OPCODE(flags)],
   1086 			  DNS_RD(flags) ? "+" : "",
   1087 			  DNS_CD(flags) ? "%" : "");
   1088 
   1089 		/* any weirdness? */
   1090 		b2 = GET_BE_U_2(((const u_short *)np) + 1);
   1091 		if (b2 & 0x6cf)
   1092 			ND_PRINT(" [b2&3=0x%x]", b2);
   1093 
   1094 		if (DNS_OPCODE(flags) == IQUERY) {
   1095 			if (qdcount)
   1096 				ND_PRINT(" [%uq]", qdcount);
   1097 			if (ancount != 1)
   1098 				ND_PRINT(" [%ua]", ancount);
   1099 		} else {
   1100 			if (ancount)
   1101 				ND_PRINT(" [%ua]", ancount);
   1102 			if (qdcount != 1)
   1103 				ND_PRINT(" [%uq]", qdcount);
   1104 		}
   1105 		if (nscount)
   1106 			ND_PRINT(" [%un]", nscount);
   1107 		if (arcount)
   1108 			ND_PRINT(" [%uau]", arcount);
   1109 
   1110 		cp = (const u_char *)(np + 1);
   1111 		if (qdcount) {
   1112 			cp = ns_qprint(ndo, cp, (const u_char *)np, is_mdns);
   1113 			if (!cp)
   1114 				goto trunc;
   1115 			qdcount--;
   1116 			while (cp < ndo->ndo_snapend && qdcount) {
   1117 				cp = ns_qprint(ndo, (const u_char *)cp,
   1118 					       (const u_char *)np,
   1119 					       is_mdns);
   1120 				if (!cp)
   1121 					goto trunc;
   1122 				qdcount--;
   1123 			}
   1124 		}
   1125 		if (qdcount)
   1126 			goto trunc;
   1127 
   1128 		/* Print remaining sections on -vv */
   1129 		if (ndo->ndo_vflag > 1) {
   1130 			if (ancount) {
   1131 				if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
   1132 					goto trunc;
   1133 				ancount--;
   1134 				while (cp < ndo->ndo_snapend && ancount) {
   1135 					ND_PRINT(",");
   1136 					if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
   1137 						goto trunc;
   1138 					ancount--;
   1139 				}
   1140 			}
   1141 			if (ancount)
   1142 				goto trunc;
   1143 			if (cp < ndo->ndo_snapend && nscount) {
   1144 				ND_PRINT(" ns:");
   1145 				if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
   1146 					goto trunc;
   1147 				nscount--;
   1148 				while (cp < ndo->ndo_snapend && nscount) {
   1149 					ND_PRINT(",");
   1150 					if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
   1151 						goto trunc;
   1152 					nscount--;
   1153 				}
   1154 			}
   1155 			if (nscount > 0)
   1156 				goto trunc;
   1157 			if (cp < ndo->ndo_snapend && arcount) {
   1158 				ND_PRINT(" ar:");
   1159 				if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
   1160 					goto trunc;
   1161 				arcount--;
   1162 				while (cp < ndo->ndo_snapend && arcount) {
   1163 					ND_PRINT(",");
   1164 					if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
   1165 						goto trunc;
   1166 					arcount--;
   1167 				}
   1168 			}
   1169 			if (arcount)
   1170 				goto trunc;
   1171 		}
   1172 	}
   1173 	ND_PRINT(" (%u)", length);
   1174 	return;
   1175 
   1176   trunc:
   1177 	nd_print_trunc(ndo);
   1178 }
   1179