xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c (revision 3851:1c86118c77d6)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*3851Ssm26363  * Common Development and Distribution License (the "License").
6*3851Ssm26363  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*3851Ssm26363  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <stdio.h>
290Sstevel@tonic-gate #include <string.h>
300Sstevel@tonic-gate #include <limits.h>
310Sstevel@tonic-gate #include <sys/types.h>
320Sstevel@tonic-gate #include <sys/errno.h>
330Sstevel@tonic-gate #include <sys/tiuser.h>
340Sstevel@tonic-gate #include <arpa/nameser.h>
350Sstevel@tonic-gate #include <arpa/inet.h>
360Sstevel@tonic-gate #include <netinet/in.h>
370Sstevel@tonic-gate #include "snoop.h"
380Sstevel@tonic-gate 
390Sstevel@tonic-gate /* The string used to indent detail lines */
400Sstevel@tonic-gate #define	DNS_INDENT	"    "
410Sstevel@tonic-gate /*
420Sstevel@tonic-gate  * From RFC1035, the maximum size of a character-string is limited by the
430Sstevel@tonic-gate  * one octet length field.  We add one character to that to make sure the
440Sstevel@tonic-gate  * result is terminated.
450Sstevel@tonic-gate  */
460Sstevel@tonic-gate #define	MAX_CHAR_STRING_SIZE	UCHAR_MAX + 1
470Sstevel@tonic-gate 
480Sstevel@tonic-gate /* private functions */
490Sstevel@tonic-gate static char *dns_opcode_string(uint_t opcode);
500Sstevel@tonic-gate static char *dns_rcode_string(uint_t rcode);
510Sstevel@tonic-gate static char *dns_type_string(uint_t type, int detail);
520Sstevel@tonic-gate static char *dns_class_string(uint_t cls, int detail);
530Sstevel@tonic-gate static size_t skip_question(const uchar_t *header, const uchar_t *data,
540Sstevel@tonic-gate     const uchar_t *data_end);
550Sstevel@tonic-gate static size_t print_question(char *line, const uchar_t *header,
560Sstevel@tonic-gate     const uchar_t *data, const uchar_t *data_end, int detail);
570Sstevel@tonic-gate static size_t print_answer(char *line, const uchar_t *header,
580Sstevel@tonic-gate     const uchar_t *data, const uchar_t *data_end, int detail);
590Sstevel@tonic-gate static char *binary_string(char data);
600Sstevel@tonic-gate static void print_ip(int af, char *line, const uchar_t *data, uint16_t len);
610Sstevel@tonic-gate static const uchar_t *get_char_string(const uchar_t *data, char *charbuf,
620Sstevel@tonic-gate     uint16_t datalen);
630Sstevel@tonic-gate static size_t print_char_string(char *line, const uchar_t *data, uint16_t len);
640Sstevel@tonic-gate static const uchar_t *get_domain_name(const uchar_t *header,
650Sstevel@tonic-gate     const uchar_t *data, const uchar_t *data_end, char *namebuf, char *namend);
660Sstevel@tonic-gate static size_t print_domain_name(char *line, const uchar_t *header,
670Sstevel@tonic-gate     const uchar_t *data, const uchar_t *data_end);
680Sstevel@tonic-gate 
690Sstevel@tonic-gate void
700Sstevel@tonic-gate interpret_dns(int flags, int proto, const uchar_t *data, int len)
710Sstevel@tonic-gate {
720Sstevel@tonic-gate 	typedef HEADER dns_header;
730Sstevel@tonic-gate 	dns_header header;
740Sstevel@tonic-gate 	char *line;
750Sstevel@tonic-gate 	ushort_t id, qdcount, ancount, nscount, arcount;
760Sstevel@tonic-gate 	ushort_t count;
77*3851Ssm26363 	const uchar_t *rrp;	/* Resource Record Pointer. */
780Sstevel@tonic-gate 	const uchar_t *data_end;
790Sstevel@tonic-gate 
800Sstevel@tonic-gate 	if (proto == IPPROTO_TCP) {
810Sstevel@tonic-gate 		/* not supported now */
820Sstevel@tonic-gate 		return;
830Sstevel@tonic-gate 	}
840Sstevel@tonic-gate 
850Sstevel@tonic-gate 	/* We need at least the header in order to parse a packet. */
860Sstevel@tonic-gate 	if (sizeof (dns_header) > len) {
870Sstevel@tonic-gate 		return;
880Sstevel@tonic-gate 	}
890Sstevel@tonic-gate 	data_end = data + len;
900Sstevel@tonic-gate 	/*
910Sstevel@tonic-gate 	 * Copy the header into a local structure for aligned access to
920Sstevel@tonic-gate 	 * each field.
930Sstevel@tonic-gate 	 */
940Sstevel@tonic-gate 	(void) memcpy(&header, data, sizeof (header));
950Sstevel@tonic-gate 	id = ntohs(header.id);
960Sstevel@tonic-gate 	qdcount = ntohs(header.qdcount);
970Sstevel@tonic-gate 	ancount = ntohs(header.ancount);
980Sstevel@tonic-gate 	nscount = ntohs(header.nscount);
990Sstevel@tonic-gate 	arcount = ntohs(header.arcount);
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	if (flags & F_SUM) {
1020Sstevel@tonic-gate 		line = get_sum_line();
1030Sstevel@tonic-gate 		line += sprintf(line, "DNS %c ", header.qr ? 'R' : 'C');
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 		if (header.qr) {
1060Sstevel@tonic-gate 			/* answer */
1070Sstevel@tonic-gate 			if (header.rcode == 0) {
1080Sstevel@tonic-gate 				/* reply is OK */
109*3851Ssm26363 				rrp = data + sizeof (dns_header);
1100Sstevel@tonic-gate 				while (qdcount--) {
111*3851Ssm26363 					if (rrp >= data_end) {
1120Sstevel@tonic-gate 						return;
1130Sstevel@tonic-gate 					}
114*3851Ssm26363 					rrp += skip_question(data,
115*3851Ssm26363 					    rrp, data_end);
1160Sstevel@tonic-gate 				}
117*3851Ssm26363 				/* the answers follow the questions */
1180Sstevel@tonic-gate 				if (ancount > 0) {
1190Sstevel@tonic-gate 					(void) print_answer(line,
120*3851Ssm26363 					    data, rrp, data_end, FALSE);
1210Sstevel@tonic-gate 				}
1220Sstevel@tonic-gate 			} else {
1230Sstevel@tonic-gate 				(void) sprintf(line, " Error: %d(%s)",
1240Sstevel@tonic-gate 				    header.rcode,
1250Sstevel@tonic-gate 				    dns_rcode_string(header.rcode));
1260Sstevel@tonic-gate 			}
1270Sstevel@tonic-gate 		} else {
1280Sstevel@tonic-gate 			/* question */
129*3851Ssm26363 			rrp = data + sizeof (dns_header);
130*3851Ssm26363 			if (rrp >= data_end) {
1310Sstevel@tonic-gate 				return;
1320Sstevel@tonic-gate 			}
133*3851Ssm26363 			(void) print_question(line, data, rrp, data_end,
1340Sstevel@tonic-gate 			    FALSE);
1350Sstevel@tonic-gate 		}
1360Sstevel@tonic-gate 	}
1370Sstevel@tonic-gate 	if (flags & F_DTAIL) {
1380Sstevel@tonic-gate 		show_header("DNS:  ", "DNS Header", sizeof (dns_header));
1390Sstevel@tonic-gate 		show_space();
1400Sstevel@tonic-gate 		if (header.qr) {
1410Sstevel@tonic-gate 			/* answer */
1420Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1430Sstevel@tonic-gate 			    "Response ID = %d", id);
1440Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1450Sstevel@tonic-gate 			    "%s%s%s",
1460Sstevel@tonic-gate 			    header.aa ? "AA (Authoritative Answer) " : "",
1470Sstevel@tonic-gate 			    header.tc ? "TC (TrunCation) " : "",
1480Sstevel@tonic-gate 			    header.ra ? "RA (Recursion Available) ": "");
1490Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1500Sstevel@tonic-gate 			    "Response Code: %d (%s)",
1510Sstevel@tonic-gate 			    header.rcode, dns_rcode_string(header.rcode));
1520Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1530Sstevel@tonic-gate 			    "Reply to %d question(s)", qdcount);
1540Sstevel@tonic-gate 		} else {
1550Sstevel@tonic-gate 			/* question */
1560Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1570Sstevel@tonic-gate 			    "Query ID = %d", id);
1580Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1590Sstevel@tonic-gate 			    "Opcode: %s", dns_opcode_string(header.opcode));
1600Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1610Sstevel@tonic-gate 			    "%s%s",
1620Sstevel@tonic-gate 			    header.tc ? "TC (TrunCation) " : "",
1630Sstevel@tonic-gate 			    header.rd ? "RD (Recursion Desired) " : "");
1640Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1650Sstevel@tonic-gate 			    "%d question(s)", qdcount);
166*3851Ssm26363 		}
167*3851Ssm26363 		rrp = data + sizeof (dns_header);
168*3851Ssm26363 		count = 0;
169*3851Ssm26363 		while (qdcount--) {
170*3851Ssm26363 			if (rrp >= data_end) {
171*3851Ssm26363 				return;
172*3851Ssm26363 			}
173*3851Ssm26363 			count++;
174*3851Ssm26363 			rrp += print_question(get_line(0, 0),
175*3851Ssm26363 			    data, rrp, data_end, TRUE);
176*3851Ssm26363 			show_space();
177*3851Ssm26363 		}
178*3851Ssm26363 		/* Only answers should hold answers, but just in case */
179*3851Ssm26363 		if (header.qr || ancount > 0) {
180*3851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
181*3851Ssm26363 			    "%d answer(s)", ancount);
1820Sstevel@tonic-gate 			count = 0;
183*3851Ssm26363 			while (ancount--) {
184*3851Ssm26363 				if (rrp >= data_end) {
185*3851Ssm26363 					return;
186*3851Ssm26363 				}
1870Sstevel@tonic-gate 				count++;
188*3851Ssm26363 				rrp += print_answer(get_line(0, 0),
189*3851Ssm26363 				    data, rrp, data_end, TRUE);
190*3851Ssm26363 				show_space();
191*3851Ssm26363 			}
192*3851Ssm26363 		}
193*3851Ssm26363 		/* Likewise only answers should hold NS records */
194*3851Ssm26363 		if (header.qr || nscount > 0) {
195*3851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
196*3851Ssm26363 			    "%d name server resource(s)", nscount);
197*3851Ssm26363 			count = 0;
198*3851Ssm26363 			while (nscount--) {
199*3851Ssm26363 				if (rrp >= data_end) {
200*3851Ssm26363 					return;
201*3851Ssm26363 				}
202*3851Ssm26363 				count++;
203*3851Ssm26363 				rrp += print_answer(get_line(0, 0), data,
204*3851Ssm26363 				    rrp, data_end, TRUE);
205*3851Ssm26363 				show_space();
206*3851Ssm26363 			}
207*3851Ssm26363 		}
208*3851Ssm26363 		/* Additional section may hold an EDNS0 record. */
209*3851Ssm26363 		if (header.qr || arcount > 0) {
210*3851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
211*3851Ssm26363 			    "%d additional record(s)", arcount);
212*3851Ssm26363 			count = 0;
213*3851Ssm26363 			while (arcount-- && rrp < data_end) {
214*3851Ssm26363 				count++;
215*3851Ssm26363 				rrp += print_answer(get_line(0, 0), data,
216*3851Ssm26363 				    rrp, data_end, TRUE);
2170Sstevel@tonic-gate 				show_space();
2180Sstevel@tonic-gate 			}
2190Sstevel@tonic-gate 		}
2200Sstevel@tonic-gate 	}
2210Sstevel@tonic-gate }
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate static char *
2250Sstevel@tonic-gate dns_opcode_string(uint_t opcode)
2260Sstevel@tonic-gate {
2270Sstevel@tonic-gate 	static char buffer[64];
2280Sstevel@tonic-gate 	switch (opcode) {
2290Sstevel@tonic-gate 	case ns_o_query:	return ("Query");
2300Sstevel@tonic-gate 	case ns_o_iquery:	return ("Inverse Query");
2310Sstevel@tonic-gate 	case ns_o_status:	return ("Status");
2320Sstevel@tonic-gate 	default:
2330Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)",
2340Sstevel@tonic-gate 		    opcode);
2350Sstevel@tonic-gate 		return (buffer);
2360Sstevel@tonic-gate 	}
2370Sstevel@tonic-gate }
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate static char *
2400Sstevel@tonic-gate dns_rcode_string(uint_t rcode)
2410Sstevel@tonic-gate {
2420Sstevel@tonic-gate 	static char buffer[64];
2430Sstevel@tonic-gate 	switch (rcode) {
2440Sstevel@tonic-gate 	case ns_r_noerror:	return ("OK");
2450Sstevel@tonic-gate 	case ns_r_formerr:	return ("Format Error");
2460Sstevel@tonic-gate 	case ns_r_servfail:	return ("Server Fail");
2470Sstevel@tonic-gate 	case ns_r_nxdomain:	return ("Name Error");
2480Sstevel@tonic-gate 	case ns_r_notimpl:	return ("Unimplemented");
2490Sstevel@tonic-gate 	case ns_r_refused:	return ("Refused");
250*3851Ssm26363 	case ns_r_badvers:	return ("Bad Version"); /* EDNS rcode */
2510Sstevel@tonic-gate 	default:
2520Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", rcode);
2530Sstevel@tonic-gate 		return (buffer);
2540Sstevel@tonic-gate 	}
2550Sstevel@tonic-gate }
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate static char *
2580Sstevel@tonic-gate dns_type_string(uint_t type, int detail)
2590Sstevel@tonic-gate {
2600Sstevel@tonic-gate 	static char buffer[64];
2610Sstevel@tonic-gate 	switch (type) {
2620Sstevel@tonic-gate 	case ns_t_a:	return (detail ? "Address" : "Addr");
2630Sstevel@tonic-gate 	case ns_t_ns:	return (detail ? "Authoritative Name Server" : "NS");
2640Sstevel@tonic-gate 	case ns_t_cname:	return (detail ? "Canonical Name" : "CNAME");
2650Sstevel@tonic-gate 	case ns_t_soa:	return (detail ? "Start Of a zone Authority" : "SOA");
2660Sstevel@tonic-gate 	case ns_t_mb:	return (detail ? "Mailbox domain name" : "MB");
2670Sstevel@tonic-gate 	case ns_t_mg:	return (detail ? "Mailbox Group member" : "MG");
2680Sstevel@tonic-gate 	case ns_t_mr:	return (detail ? "Mail Rename domain name" : "MR");
2690Sstevel@tonic-gate 	case ns_t_null:	return ("NULL");
2700Sstevel@tonic-gate 	case ns_t_wks:	return (detail ? "Well Known Service" : "WKS");
2710Sstevel@tonic-gate 	case ns_t_ptr:	return (detail ? "Domain Name Pointer" : "PTR");
2720Sstevel@tonic-gate 	case ns_t_hinfo:	return (detail ? "Host Information": "HINFO");
2730Sstevel@tonic-gate 	case ns_t_minfo:
2740Sstevel@tonic-gate 		return (detail ? "Mailbox or maillist Info" : "MINFO");
2750Sstevel@tonic-gate 	case ns_t_mx:	return (detail ? "Mail Exchange" : "MX");
2760Sstevel@tonic-gate 	case ns_t_txt:	return (detail ? "Text strings" : "TXT");
2770Sstevel@tonic-gate 	case ns_t_aaaa:	return (detail ? "IPv6 Address" : "AAAA");
278*3851Ssm26363 	case ns_t_opt:	return (detail ? "EDNS0 option" : "OPT");
2790Sstevel@tonic-gate 	case ns_t_axfr:	return (detail ? "Transfer of entire zone" : "AXFR");
2800Sstevel@tonic-gate 	case ns_t_mailb:
2810Sstevel@tonic-gate 		return (detail ? "Mailbox related records" : "MAILB");
2820Sstevel@tonic-gate 	case ns_t_maila:	return (detail ? "Mail agent RRs" : "MAILA");
2830Sstevel@tonic-gate 	case ns_t_any:	return (detail ? "All records" : "*");
2840Sstevel@tonic-gate 	default:
2850Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", type);
2860Sstevel@tonic-gate 		return (buffer);
2870Sstevel@tonic-gate 	}
2880Sstevel@tonic-gate }
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate static char *
2910Sstevel@tonic-gate dns_class_string(uint_t cls, int detail)
2920Sstevel@tonic-gate {
2930Sstevel@tonic-gate 	static char buffer[64];
2940Sstevel@tonic-gate 	switch (cls) {
2950Sstevel@tonic-gate 	case ns_c_in:		return (detail ? "Internet" : "Internet");
2960Sstevel@tonic-gate 	case ns_c_chaos: 	return (detail ? "CHAOS" : "CH");
2970Sstevel@tonic-gate 	case ns_c_hs:		return (detail ? "Hesiod" : "HS");
2980Sstevel@tonic-gate 	case ns_c_any:		return (detail ? "* (Any class)" : "*");
2990Sstevel@tonic-gate 	default:
3000Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", cls);
3010Sstevel@tonic-gate 		return (buffer);
3020Sstevel@tonic-gate 	}
3030Sstevel@tonic-gate }
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate static size_t
3060Sstevel@tonic-gate skip_question(const uchar_t *header, const uchar_t *data,
3070Sstevel@tonic-gate     const uchar_t *data_end)
3080Sstevel@tonic-gate {
3090Sstevel@tonic-gate 	const uchar_t *data_bak = data;
3100Sstevel@tonic-gate 	char dummy_buffer[NS_MAXDNAME];
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	data = get_domain_name(header, data, data_end, dummy_buffer,
3130Sstevel@tonic-gate 	    dummy_buffer + sizeof (dummy_buffer));
3140Sstevel@tonic-gate 	/* Skip the 32 bits of class and type that follow the domain name */
3150Sstevel@tonic-gate 	data += sizeof (uint32_t);
3160Sstevel@tonic-gate 	return (data - data_bak);
3170Sstevel@tonic-gate }
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate static size_t
3200Sstevel@tonic-gate print_question(char *line, const uchar_t *header, const uchar_t *data,
3210Sstevel@tonic-gate     const uchar_t *data_end, int detail)
3220Sstevel@tonic-gate {
3230Sstevel@tonic-gate 	const uchar_t *data_bak = data;
3240Sstevel@tonic-gate 	uint16_t type;
3250Sstevel@tonic-gate 	uint16_t cls;
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 	if (detail) {
3280Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
3290Sstevel@tonic-gate 		    DNS_INDENT "Domain Name: ");
3300Sstevel@tonic-gate 	}
3310Sstevel@tonic-gate 	data += print_domain_name(line, header, data, data_end);
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 	/*
3340Sstevel@tonic-gate 	 * Make sure we don't run off the end of the packet by reading the
3350Sstevel@tonic-gate 	 * type and class.
3360Sstevel@tonic-gate 	 *
3370Sstevel@tonic-gate 	 * The pointer subtraction on the left side of the following
3380Sstevel@tonic-gate 	 * expression has a signed result of type ptrdiff_t, and the right
3390Sstevel@tonic-gate 	 * side has an unsigned result of type size_t.  We therefore need
3400Sstevel@tonic-gate 	 * to cast the right side of the expression to be of the same
3410Sstevel@tonic-gate 	 * signed type to keep the result of the pointer arithmetic to be
3420Sstevel@tonic-gate 	 * automatically cast to an unsigned value.  We do a similar cast
3430Sstevel@tonic-gate 	 * in other similar expressions throughout this file.
3440Sstevel@tonic-gate 	 */
3450Sstevel@tonic-gate 	if ((data_end - data) < (ptrdiff_t)(2 * sizeof (uint16_t)))
3460Sstevel@tonic-gate 		return (data_end - data_bak);
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	GETINT16(type, data);
3490Sstevel@tonic-gate 	GETINT16(cls, data);
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 	if (detail) {
3520Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
3530Sstevel@tonic-gate 		    DNS_INDENT "Class: %u (%s)",
3540Sstevel@tonic-gate 		    cls, dns_class_string(cls, detail));
3550Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
3560Sstevel@tonic-gate 		    DNS_INDENT "Type:  %u (%s)", type,
3570Sstevel@tonic-gate 		    dns_type_string(type, detail));
3580Sstevel@tonic-gate 	} else {
3590Sstevel@tonic-gate 		(void) sprintf(line + strlen(line), " %s %s \?",
3600Sstevel@tonic-gate 		    dns_class_string(cls, detail),
3610Sstevel@tonic-gate 		    dns_type_string(type, detail));
3620Sstevel@tonic-gate 	}
3630Sstevel@tonic-gate 	return (data - data_bak);
3640Sstevel@tonic-gate }
3650Sstevel@tonic-gate 
366*3851Ssm26363 /*
367*3851Ssm26363  * print_answer() is used to display the contents of a single resource
368*3851Ssm26363  * record (RR) from either the answer, name server or additional
369*3851Ssm26363  * section of the DNS packet.
370*3851Ssm26363  *
371*3851Ssm26363  * Input:
372*3851Ssm26363  *	*line: snoops output buffer.
373*3851Ssm26363  *	*header: start of the DNS packet, required for names and rcode.
374*3851Ssm26363  *	*data: location within header from where the RR starts.
375*3851Ssm26363  * 	*data_end: where DNS data ends.
376*3851Ssm26363  * 	detail: simple or verbose output.
377*3851Ssm26363  *
378*3851Ssm26363  * Returns:
379*3851Ssm26363  *	Pointer to next RR or data_end.
380*3851Ssm26363  *
381*3851Ssm26363  * Most RRs have the same top level format as defined in RFC 1035:
382*3851Ssm26363  *
383*3851Ssm26363  *                                     1  1  1  1  1  1
384*3851Ssm26363  *       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
385*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
386*3851Ssm26363  *    |                                               |
387*3851Ssm26363  *    /                      NAME                     /
388*3851Ssm26363  *    |                                               |
389*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
390*3851Ssm26363  *    |                      TYPE                     |
391*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
392*3851Ssm26363  *    |                     CLASS                     |
393*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
394*3851Ssm26363  *    |                      TTL                      |
395*3851Ssm26363  *    |                                               |
396*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
397*3851Ssm26363  *    |                   RDLENGTH                    |
398*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
399*3851Ssm26363  *    /                     RDATA                     /
400*3851Ssm26363  *    /                                               /
401*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
402*3851Ssm26363  *
403*3851Ssm26363  * However RFC 2671 introduced an exception to this rule
404*3851Ssm26363  * with the "Extension Mechanisms for DNS" (EDNS0).
405*3851Ssm26363  * When the type is 41 the remaining resource record format
406*3851Ssm26363  * is:
407*3851Ssm26363  *
408*3851Ssm26363  *                                     1  1  1  1  1  1
409*3851Ssm26363  *       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
410*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
411*3851Ssm26363  *    |                    TYPE = 41                  |
412*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
413*3851Ssm26363  *    |           Sender's UDP payload size           |
414*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
415*3851Ssm26363  *    |    Extended-rcode     |        Version        |
416*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
417*3851Ssm26363  *    |                      Zero                     |
418*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
419*3851Ssm26363  *    |                   RDLENGTH                    |
420*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
421*3851Ssm26363  *    /                     RDATA                     /
422*3851Ssm26363  *    /                                               /
423*3851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
424*3851Ssm26363  *
425*3851Ssm26363  */
4260Sstevel@tonic-gate static size_t
4270Sstevel@tonic-gate print_answer(char *line, const uchar_t *header, const uchar_t *data,
4280Sstevel@tonic-gate     const uchar_t *data_end, int detail)
4290Sstevel@tonic-gate {
4300Sstevel@tonic-gate 	const uchar_t *data_bak = data;
4310Sstevel@tonic-gate 	const uchar_t *data_next;
4320Sstevel@tonic-gate 	uint16_t type;
4330Sstevel@tonic-gate 	uint16_t cls;
4340Sstevel@tonic-gate 	int32_t ttl;
4350Sstevel@tonic-gate 	uint16_t rdlen;
4360Sstevel@tonic-gate 	uint32_t serial, refresh, retry, expire, minimum;
4370Sstevel@tonic-gate 	uint8_t protocol;
4380Sstevel@tonic-gate 	int linepos;
4390Sstevel@tonic-gate 	uint16_t preference;
440*3851Ssm26363 	/* declarations for EDNS follow */
441*3851Ssm26363 	uint16_t size;	/* Sender's UDP payload size */
442*3851Ssm26363 	uint8_t xrcode;	/* Extended-rcode */
443*3851Ssm26363 	uint8_t ver;	/* Version */
444*3851Ssm26363 	uint16_t rcode;	/* Extracted from the DNS header */
445*3851Ssm26363 	union {		/* DNS header overlay used for extraction */
446*3851Ssm26363 		HEADER		*head;
447*3851Ssm26363 		const uchar_t	*raw;
448*3851Ssm26363 	} headptr;
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 	if (detail) {
4510Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
4520Sstevel@tonic-gate 		    DNS_INDENT "Domain Name: ");
4530Sstevel@tonic-gate 	}
4540Sstevel@tonic-gate 	data += print_domain_name(line, header, data, data_end);
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	/*
457*3851Ssm26363 	 * Next, get the record type, being careful to make sure we
458*3851Ssm26363 	 * don't run off the end of the packet.
4590Sstevel@tonic-gate 	 */
460*3851Ssm26363 	if ((data_end - data) < (ptrdiff_t)(sizeof (type))) {
4610Sstevel@tonic-gate 		return (data_end - data_bak);
4620Sstevel@tonic-gate 	}
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	GETINT16(type, data);
465*3851Ssm26363 
466*3851Ssm26363 	if (type == ns_t_opt) {
467*3851Ssm26363 		/*
468*3851Ssm26363 		 * Make sure we won't run off the end reading size,
469*3851Ssm26363 		 * xrcode, version, zero and rdlen.
470*3851Ssm26363 		 */
471*3851Ssm26363 		if ((data_end - data) <
472*3851Ssm26363 		    ((ptrdiff_t)(sizeof (size)
473*3851Ssm26363 			+ sizeof (xrcode)
474*3851Ssm26363 			+ sizeof (ver)
475*3851Ssm26363 			+ sizeof (cls)	/* zero */
476*3851Ssm26363 			+ sizeof (rdlen)))) {
477*3851Ssm26363 			return (data_end - data_bak);
478*3851Ssm26363 		}
479*3851Ssm26363 
480*3851Ssm26363 		GETINT16(size, data);
481*3851Ssm26363 		GETINT8(xrcode, data);
482*3851Ssm26363 		/*
483*3851Ssm26363 		 * The extended rcode represents the top half of the
484*3851Ssm26363 		 * rcode which must be added to the rcode in the header.
485*3851Ssm26363 		 */
486*3851Ssm26363 		rcode = 0xff & (xrcode << 4);
487*3851Ssm26363 		headptr.raw = header;		/* Overlay the header... */
488*3851Ssm26363 		rcode += headptr.head->rcode;	/* And pluck out the rcode. */
489*3851Ssm26363 
490*3851Ssm26363 		GETINT8(ver, data);
491*3851Ssm26363 		GETINT16(cls, data); /* zero */
492*3851Ssm26363 		GETINT16(rdlen, data);
493*3851Ssm26363 
494*3851Ssm26363 		if (detail) {
495*3851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
496*3851Ssm26363 			    DNS_INDENT "Type:  %u (%s)", type,
497*3851Ssm26363 			    dns_type_string(type, detail));
498*3851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
499*3851Ssm26363 			    DNS_INDENT "UDP payload size: %u (0x%.4x)",
500*3851Ssm26363 			    size, size);
501*3851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
502*3851Ssm26363 			    DNS_INDENT "Extended rcode: %u "
503*3851Ssm26363 			    "(translates to %u (%s))",
504*3851Ssm26363 			    xrcode, rcode, dns_rcode_string(rcode));
505*3851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
506*3851Ssm26363 			    DNS_INDENT "EDNS0 Version: %u", ver);
507*3851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
508*3851Ssm26363 			    DNS_INDENT "zero: %u", cls);
509*3851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
510*3851Ssm26363 			    DNS_INDENT "Data length: %u", rdlen);
511*3851Ssm26363 		} else {
512*3851Ssm26363 			line += strlen(line);
513*3851Ssm26363 			line += sprintf(line, " %s UDP %u rc %d ver %u len %u",
514*3851Ssm26363 			    dns_type_string(type, detail), size, rcode, ver,
515*3851Ssm26363 			    rdlen);
516*3851Ssm26363 		}
517*3851Ssm26363 
518*3851Ssm26363 		/*
519*3851Ssm26363 		 * Make sure that rdlen is within data boundary.
520*3851Ssm26363 		 */
521*3851Ssm26363 		if (rdlen > data_end - data)
522*3851Ssm26363 			return (data_end - data_bak);
523*3851Ssm26363 
524*3851Ssm26363 		/* Future OPT decode code goes here. */
525*3851Ssm26363 
526*3851Ssm26363 		data += rdlen;
527*3851Ssm26363 		return (data - data_bak);
528*3851Ssm26363 	}
529*3851Ssm26363 
530*3851Ssm26363 	/*
531*3851Ssm26363 	 * Make sure we don't run off the end of the packet by reading the
532*3851Ssm26363 	 * class, ttl, and length.
533*3851Ssm26363 	 */
534*3851Ssm26363 	if ((data_end - data) <
535*3851Ssm26363 	    ((ptrdiff_t)(sizeof (cls)
536*3851Ssm26363 		+ sizeof (ttl)
537*3851Ssm26363 		+ sizeof (rdlen)))) {
538*3851Ssm26363 		return (data_end - data_bak);
539*3851Ssm26363 	}
540*3851Ssm26363 
5410Sstevel@tonic-gate 	GETINT16(cls, data);
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 	if (detail) {
5440Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
5450Sstevel@tonic-gate 		    DNS_INDENT "Class: %d (%s)", cls,
5460Sstevel@tonic-gate 		    dns_class_string(cls, detail));
5470Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
5480Sstevel@tonic-gate 		    DNS_INDENT "Type:  %d (%s)", type,
5490Sstevel@tonic-gate 		    dns_type_string(type, detail));
5500Sstevel@tonic-gate 	} else {
5510Sstevel@tonic-gate 		line += strlen(line);
5520Sstevel@tonic-gate 		line += sprintf(line, " %s %s ",
5530Sstevel@tonic-gate 		    dns_class_string(cls, detail),
5540Sstevel@tonic-gate 		    dns_type_string(type, detail));
5550Sstevel@tonic-gate 	}
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	GETINT32(ttl, data);
5580Sstevel@tonic-gate 	if (detail) {
5590Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
5600Sstevel@tonic-gate 		    DNS_INDENT "TTL (Time To Live): %d", ttl);
5610Sstevel@tonic-gate 	}
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	GETINT16(rdlen, data);
5640Sstevel@tonic-gate 	if (detail) {
5650Sstevel@tonic-gate 		line = get_line(0, 0);
5660Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(), DNS_INDENT "%s: ",
5670Sstevel@tonic-gate 		    dns_type_string(type, detail));
5680Sstevel@tonic-gate 	}
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate 	if (rdlen > data_end - data)
5710Sstevel@tonic-gate 		return (data_end - data_bak);
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 	switch (type) {
5740Sstevel@tonic-gate 	case ns_t_a:
5750Sstevel@tonic-gate 		print_ip(AF_INET, line, data, rdlen);
5760Sstevel@tonic-gate 		break;
5770Sstevel@tonic-gate 	case ns_t_aaaa:
5780Sstevel@tonic-gate 		print_ip(AF_INET6, line, data, rdlen);
5790Sstevel@tonic-gate 		break;
5800Sstevel@tonic-gate 	case ns_t_hinfo:
5810Sstevel@tonic-gate 		line += sprintf(line, "CPU: ");
5820Sstevel@tonic-gate 		data_next = data + print_char_string(line, data, rdlen);
5830Sstevel@tonic-gate 		if (data_next >= data_end)
5840Sstevel@tonic-gate 			break;
5850Sstevel@tonic-gate 		line += strlen(line);
5860Sstevel@tonic-gate 		line += sprintf(line, "OS: ");
5870Sstevel@tonic-gate 		(void) print_char_string(line, data_next,
5880Sstevel@tonic-gate 		    rdlen - (data_next - data));
5890Sstevel@tonic-gate 		break;
5900Sstevel@tonic-gate 	case ns_t_ns:
5910Sstevel@tonic-gate 	case ns_t_cname:
5920Sstevel@tonic-gate 	case ns_t_mb:
5930Sstevel@tonic-gate 	case ns_t_mg:
5940Sstevel@tonic-gate 	case ns_t_mr:
5950Sstevel@tonic-gate 	case ns_t_ptr:
5960Sstevel@tonic-gate 		(void) print_domain_name(line, header, data, data_end);
5970Sstevel@tonic-gate 		break;
5980Sstevel@tonic-gate 	case ns_t_mx:
5990Sstevel@tonic-gate 		data_next = data;
6000Sstevel@tonic-gate 		if (rdlen < sizeof (uint16_t))
6010Sstevel@tonic-gate 			break;
6020Sstevel@tonic-gate 		GETINT16(preference, data_next);
6030Sstevel@tonic-gate 		if (detail) {
6040Sstevel@tonic-gate 			(void) print_domain_name(line, header, data_next,
6050Sstevel@tonic-gate 			    data_end);
6060Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
6070Sstevel@tonic-gate 			    DNS_INDENT "Preference: %u", preference);
6080Sstevel@tonic-gate 		} else {
6090Sstevel@tonic-gate 			(void) print_domain_name(line, header, data_next,
6100Sstevel@tonic-gate 			    data_end);
6110Sstevel@tonic-gate 		}
6120Sstevel@tonic-gate 		break;
6130Sstevel@tonic-gate 	case ns_t_soa:
6140Sstevel@tonic-gate 		if (!detail)
6150Sstevel@tonic-gate 			break;
6160Sstevel@tonic-gate 		line = get_line(0, 0);
6170Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
6180Sstevel@tonic-gate 		    DNS_INDENT "MNAME (Server name): ");
6190Sstevel@tonic-gate 		data_next = data + print_domain_name(line, header, data,
6200Sstevel@tonic-gate 		    data_end);
6210Sstevel@tonic-gate 		if (data_next >= data_end)
6220Sstevel@tonic-gate 			break;
6230Sstevel@tonic-gate 		line = get_line(0, 0);
6240Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
6250Sstevel@tonic-gate 		    DNS_INDENT "RNAME (Resposible mailbox): ");
6260Sstevel@tonic-gate 		data_next = data_next +
6270Sstevel@tonic-gate 		    print_domain_name(line, header, data_next, data_end);
6280Sstevel@tonic-gate 		if ((data_end - data_next) < (ptrdiff_t)(5 * sizeof (uint32_t)))
6290Sstevel@tonic-gate 			break;
6300Sstevel@tonic-gate 		GETINT32(serial, data_next);
6310Sstevel@tonic-gate 		GETINT32(refresh, data_next);
6320Sstevel@tonic-gate 		GETINT32(retry, data_next);
6330Sstevel@tonic-gate 		GETINT32(expire, data_next);
6340Sstevel@tonic-gate 		GETINT32(minimum, data_next);
6350Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
6360Sstevel@tonic-gate 		    DNS_INDENT "Serial: %u", serial);
6370Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
6380Sstevel@tonic-gate 		    DNS_INDENT "Refresh: %u  Retry: %u  "
6390Sstevel@tonic-gate 		    "Expire: %u Minimum: %u",
6400Sstevel@tonic-gate 		    refresh, retry, expire, minimum);
6410Sstevel@tonic-gate 		break;
6420Sstevel@tonic-gate 	case ns_t_wks:
6430Sstevel@tonic-gate 		print_ip(AF_INET, line, data, rdlen);
6440Sstevel@tonic-gate 		if (!detail)
6450Sstevel@tonic-gate 			break;
6460Sstevel@tonic-gate 		data_next = data + sizeof (in_addr_t);
6470Sstevel@tonic-gate 		if (data_next >= data_end)
6480Sstevel@tonic-gate 			break;
6490Sstevel@tonic-gate 		GETINT8(protocol, data_next);
6500Sstevel@tonic-gate 		line = get_line(0, 0);
6510Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
6520Sstevel@tonic-gate 		    DNS_INDENT "Protocol: %u ", protocol);
6530Sstevel@tonic-gate 		switch (protocol) {
6540Sstevel@tonic-gate 		case IPPROTO_UDP:
6550Sstevel@tonic-gate 			(void) snprintf(line, get_line_remain(), "(UDP)");
6560Sstevel@tonic-gate 			break;
6570Sstevel@tonic-gate 		case IPPROTO_TCP:
6580Sstevel@tonic-gate 			(void) snprintf(line, get_line_remain(), "(TCP)");
6590Sstevel@tonic-gate 			break;
6600Sstevel@tonic-gate 		}
6610Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
6620Sstevel@tonic-gate 		    DNS_INDENT "Service bitmap:");
6630Sstevel@tonic-gate 		(void) snprintf(line, get_line_remain(),
6640Sstevel@tonic-gate 		    DNS_INDENT "0       8       16      24");
6650Sstevel@tonic-gate 		linepos = 4;
6660Sstevel@tonic-gate 		while (data_next < data + rdlen) {
6670Sstevel@tonic-gate 			if (linepos == 4) {
6680Sstevel@tonic-gate 				line = get_line(0, 0);
6690Sstevel@tonic-gate 				line += snprintf(line, get_line_remain(),
6700Sstevel@tonic-gate 				    DNS_INDENT);
6710Sstevel@tonic-gate 				linepos = 0;
6720Sstevel@tonic-gate 			}
6730Sstevel@tonic-gate 			line += snprintf(line, get_line_remain(), "%s",
6740Sstevel@tonic-gate 			    binary_string(*data_next));
6750Sstevel@tonic-gate 			linepos++;
6760Sstevel@tonic-gate 			data_next++;
6770Sstevel@tonic-gate 		}
6780Sstevel@tonic-gate 		break;
6790Sstevel@tonic-gate 	case ns_t_minfo:
6800Sstevel@tonic-gate 		if (!detail)
6810Sstevel@tonic-gate 			break;
6820Sstevel@tonic-gate 		line = get_line(0, 0);
6830Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
6840Sstevel@tonic-gate 		    DNS_INDENT "RMAILBX (Resposible mailbox): ");
6850Sstevel@tonic-gate 		data_next = data + print_domain_name(line, header, data,
6860Sstevel@tonic-gate 		    data_end);
6870Sstevel@tonic-gate 		line = get_line(0, 0);
6880Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
6890Sstevel@tonic-gate 		    DNS_INDENT "EMAILBX (mailbox to receive err message): ");
6900Sstevel@tonic-gate 		data_next = data_next + print_domain_name(line, header,
6910Sstevel@tonic-gate 		    data_next, data_end);
6920Sstevel@tonic-gate 		break;
6930Sstevel@tonic-gate 	}
6940Sstevel@tonic-gate 	data += rdlen;
6950Sstevel@tonic-gate 	return (data - data_bak);
6960Sstevel@tonic-gate }
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate static char *
6990Sstevel@tonic-gate binary_string(char data)
7000Sstevel@tonic-gate {
7010Sstevel@tonic-gate 	static char bstring[8 + 1];
7020Sstevel@tonic-gate 	char *ptr;
7030Sstevel@tonic-gate 	int i;
7040Sstevel@tonic-gate 	ptr = bstring;
7050Sstevel@tonic-gate 	for (i = 0; i < 8; i++) {
7060Sstevel@tonic-gate 		*ptr++ = (data & 0x80) ? '1' : '0';
7070Sstevel@tonic-gate 		data = data << 1;
7080Sstevel@tonic-gate 	}
7090Sstevel@tonic-gate 	*ptr = (char)0;
7100Sstevel@tonic-gate 	return (bstring);
7110Sstevel@tonic-gate }
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate static void
7140Sstevel@tonic-gate print_ip(int af, char *line, const uchar_t *data, uint16_t len)
7150Sstevel@tonic-gate {
7160Sstevel@tonic-gate 	in6_addr_t	addr6;
7170Sstevel@tonic-gate 	in_addr_t	addr4;
7180Sstevel@tonic-gate 	void		*addr;
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 	switch (af) {
7210Sstevel@tonic-gate 	case AF_INET:
7220Sstevel@tonic-gate 		if (len != sizeof (in_addr_t))
7230Sstevel@tonic-gate 			return;
7240Sstevel@tonic-gate 		addr = memcpy(&addr4, data, sizeof (addr4));
7250Sstevel@tonic-gate 		break;
7260Sstevel@tonic-gate 	case AF_INET6:
7270Sstevel@tonic-gate 		if (len != sizeof (in6_addr_t))
7280Sstevel@tonic-gate 			return;
7290Sstevel@tonic-gate 		addr = memcpy(&addr6, data, sizeof (addr6));
7300Sstevel@tonic-gate 		break;
7310Sstevel@tonic-gate 	}
7320Sstevel@tonic-gate 
7330Sstevel@tonic-gate 	(void) inet_ntop(af, addr, line, INET6_ADDRSTRLEN);
7340Sstevel@tonic-gate }
7350Sstevel@tonic-gate 
7360Sstevel@tonic-gate /*
7370Sstevel@tonic-gate  * charbuf is assumed to be of size MAX_CHAR_STRING_SIZE.
7380Sstevel@tonic-gate  */
7390Sstevel@tonic-gate static const uchar_t *
7400Sstevel@tonic-gate get_char_string(const uchar_t *data, char *charbuf, uint16_t datalen)
7410Sstevel@tonic-gate {
742410Skcpoon 	int len;
7430Sstevel@tonic-gate 	char *name = charbuf;
7440Sstevel@tonic-gate 	int i = 0;
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	/*
7470Sstevel@tonic-gate 	 * From RFC1035, a character-string is a single length octet followed
7480Sstevel@tonic-gate 	 * by that number of characters.
7490Sstevel@tonic-gate 	 */
7500Sstevel@tonic-gate 	if (datalen > 1) {
7510Sstevel@tonic-gate 		len = *data;
7520Sstevel@tonic-gate 		data++;
7530Sstevel@tonic-gate 		if (len > 0 && len < MAX_CHAR_STRING_SIZE) {
7540Sstevel@tonic-gate 			for (i = 0; i < len; i++, data++)
7550Sstevel@tonic-gate 				name[i] = *data;
7560Sstevel@tonic-gate 		}
7570Sstevel@tonic-gate 	}
7580Sstevel@tonic-gate 	name[i] = '\0';
7590Sstevel@tonic-gate 	return (data);
7600Sstevel@tonic-gate }
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate static size_t
7630Sstevel@tonic-gate print_char_string(char *line, const uchar_t *data, uint16_t len)
7640Sstevel@tonic-gate {
7650Sstevel@tonic-gate 	char charbuf[MAX_CHAR_STRING_SIZE];
7660Sstevel@tonic-gate 	const uchar_t *data_bak = data;
7670Sstevel@tonic-gate 
7680Sstevel@tonic-gate 	data = get_char_string(data, charbuf, len);
7690Sstevel@tonic-gate 	(void) sprintf(line, "%s", charbuf);
7700Sstevel@tonic-gate 	return (data - data_bak);
7710Sstevel@tonic-gate }
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate /*
7740Sstevel@tonic-gate  * header: the entire message header, this is where we start to
7750Sstevel@tonic-gate  *	   count the offset of the compression scheme
7760Sstevel@tonic-gate  * data:   the start of the domain name
7770Sstevel@tonic-gate  * namebuf: user supplied buffer
7780Sstevel@tonic-gate  * return: the next byte after what we have parsed
7790Sstevel@tonic-gate  */
7800Sstevel@tonic-gate static const uchar_t *
7810Sstevel@tonic-gate get_domain_name(const uchar_t *header, const uchar_t *data,
7820Sstevel@tonic-gate     const uchar_t *data_end, char *namebuf, char *namend)
7830Sstevel@tonic-gate {
7840Sstevel@tonic-gate 	uint8_t len;
7850Sstevel@tonic-gate 	char *name = namebuf;
7860Sstevel@tonic-gate 
7870Sstevel@tonic-gate 	/*
7880Sstevel@tonic-gate 	 * From RFC1035, a domain name is a sequence of labels, where each
7890Sstevel@tonic-gate 	 * label consists of a length octet followed by that number of
7900Sstevel@tonic-gate 	 * octets.  The domain name terminates with the zero length octet
7910Sstevel@tonic-gate 	 * for the null label of the root.
7920Sstevel@tonic-gate 	 */
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate 	while (name < (namend - 1)) {
7950Sstevel@tonic-gate 		if ((data_end - data) < (ptrdiff_t)(sizeof (uint8_t))) {
7960Sstevel@tonic-gate 			/* The length octet is off the end of the packet. */
7970Sstevel@tonic-gate 			break;
7980Sstevel@tonic-gate 		}
7990Sstevel@tonic-gate 		GETINT8(len, data);
8000Sstevel@tonic-gate 		if (len == 0) {
8010Sstevel@tonic-gate 			/*
8020Sstevel@tonic-gate 			 * Domain names end with a length byte of zero,
8030Sstevel@tonic-gate 			 * which represents the null label of the root.
8040Sstevel@tonic-gate 			 */
8050Sstevel@tonic-gate 			break;
8060Sstevel@tonic-gate 		}
8070Sstevel@tonic-gate 		/*
8080Sstevel@tonic-gate 		 * test if we are using the compression scheme
8090Sstevel@tonic-gate 		 */
8100Sstevel@tonic-gate 		if ((len & 0xc0) == 0xc0) {
8110Sstevel@tonic-gate 			uint16_t offset;
8120Sstevel@tonic-gate 			const uchar_t *label_ptr;
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 			/*
8150Sstevel@tonic-gate 			 * From RFC1035, message compression allows a
8160Sstevel@tonic-gate 			 * domain name or a list of labels at the end of a
8170Sstevel@tonic-gate 			 * domain name to be replaced with a pointer to a
8180Sstevel@tonic-gate 			 * prior occurance of the same name.  In this
8190Sstevel@tonic-gate 			 * scheme, the pointer is a two octet sequence
8200Sstevel@tonic-gate 			 * where the most significant two bits are set, and
8210Sstevel@tonic-gate 			 * the remaining 14 bits are the offset from the
8220Sstevel@tonic-gate 			 * start of the message of the next label.
8230Sstevel@tonic-gate 			 */
8240Sstevel@tonic-gate 			data--;
8250Sstevel@tonic-gate 			if ((data_end - data) <
8260Sstevel@tonic-gate 			    (ptrdiff_t)(sizeof (uint16_t))) {
8270Sstevel@tonic-gate 				/*
8280Sstevel@tonic-gate 				 * The offset octets aren't entirely
8290Sstevel@tonic-gate 				 * contained within this pakcet.
8300Sstevel@tonic-gate 				 */
8310Sstevel@tonic-gate 				data = data_end;
8320Sstevel@tonic-gate 				break;
8330Sstevel@tonic-gate 			}
8340Sstevel@tonic-gate 			GETINT16(offset, data);
8350Sstevel@tonic-gate 			label_ptr = header + (offset & 0x3fff);
8360Sstevel@tonic-gate 			/*
8370Sstevel@tonic-gate 			 * We must verify that the offset is valid by
8380Sstevel@tonic-gate 			 * checking that it is less than the current data
8390Sstevel@tonic-gate 			 * pointer and that it isn't off the end of the
8400Sstevel@tonic-gate 			 * packet.
8410Sstevel@tonic-gate 			 */
8420Sstevel@tonic-gate 			if (label_ptr > data || label_ptr >= data_end)
8430Sstevel@tonic-gate 				break;
8440Sstevel@tonic-gate 			(void) get_domain_name(header, label_ptr, data_end,
8450Sstevel@tonic-gate 			    name, namend);
8460Sstevel@tonic-gate 			return (data);
8470Sstevel@tonic-gate 		} else {
8480Sstevel@tonic-gate 			if (len > (data_end - data)) {
8490Sstevel@tonic-gate 				/*
8500Sstevel@tonic-gate 				 * The label isn't entirely contained
8510Sstevel@tonic-gate 				 * within the packet.  Don't read it.  The
8520Sstevel@tonic-gate 				 * caller checks that the data pointer is
8530Sstevel@tonic-gate 				 * not beyond the end after we've
8540Sstevel@tonic-gate 				 * incremented it.
8550Sstevel@tonic-gate 				 */
8560Sstevel@tonic-gate 				data = data_end;
8570Sstevel@tonic-gate 				break;
8580Sstevel@tonic-gate 			}
8590Sstevel@tonic-gate 			while (len > 0 && name < (namend - 2)) {
8600Sstevel@tonic-gate 				*name = *data;
8610Sstevel@tonic-gate 				name++;
8620Sstevel@tonic-gate 				data++;
8630Sstevel@tonic-gate 				len--;
8640Sstevel@tonic-gate 			}
8650Sstevel@tonic-gate 			*name = '.';
8660Sstevel@tonic-gate 			name++;
8670Sstevel@tonic-gate 		}
8680Sstevel@tonic-gate 	}
8690Sstevel@tonic-gate 	*name = '\0';
8700Sstevel@tonic-gate 	return (data);
8710Sstevel@tonic-gate }
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate static size_t
8740Sstevel@tonic-gate print_domain_name(char *line, const uchar_t *header, const uchar_t *data,
8750Sstevel@tonic-gate     const uchar_t *data_end)
8760Sstevel@tonic-gate {
8770Sstevel@tonic-gate 	char name[NS_MAXDNAME];
8780Sstevel@tonic-gate 	const uchar_t *new_data;
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	new_data = get_domain_name(header, data, data_end, name,
8810Sstevel@tonic-gate 	    name + sizeof (name));
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 	(void) sprintf(line, "%s", name);
8840Sstevel@tonic-gate 	return (new_data - data);
8850Sstevel@tonic-gate }
886