xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c (revision 4904:cd464a980538)
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
53851Ssm26363  * Common Development and Distribution License (the "License").
63851Ssm26363  * 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 /*
223851Ssm26363  * 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
interpret_dns(int flags,int proto,const uchar_t * data,int len,int port)70*4904Srs200217 interpret_dns(int flags, int proto, const uchar_t *data, int len, int port)
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;
773851Ssm26363 	const uchar_t *rrp;	/* Resource Record Pointer. */
780Sstevel@tonic-gate 	const uchar_t *data_end;
79*4904Srs200217 	const char *protostr;
80*4904Srs200217 	char *protopfxstr;
81*4904Srs200217 	char *protohdrstr;
820Sstevel@tonic-gate 
830Sstevel@tonic-gate 	if (proto == IPPROTO_TCP) {
840Sstevel@tonic-gate 		/* not supported now */
850Sstevel@tonic-gate 		return;
860Sstevel@tonic-gate 	}
870Sstevel@tonic-gate 
88*4904Srs200217 	if (port == IPPORT_DOMAIN) {
89*4904Srs200217 		protostr = "DNS";
90*4904Srs200217 		protopfxstr = "DNS:  ";
91*4904Srs200217 		protohdrstr = "DNS Header";
92*4904Srs200217 	} else {
93*4904Srs200217 		protostr = "MDNS";
94*4904Srs200217 		protopfxstr = "MDNS:  ";
95*4904Srs200217 		protohdrstr = "MDNS Header";
96*4904Srs200217 	}
97*4904Srs200217 
980Sstevel@tonic-gate 	/* We need at least the header in order to parse a packet. */
990Sstevel@tonic-gate 	if (sizeof (dns_header) > len) {
1000Sstevel@tonic-gate 		return;
1010Sstevel@tonic-gate 	}
1020Sstevel@tonic-gate 	data_end = data + len;
1030Sstevel@tonic-gate 	/*
1040Sstevel@tonic-gate 	 * Copy the header into a local structure for aligned access to
1050Sstevel@tonic-gate 	 * each field.
1060Sstevel@tonic-gate 	 */
1070Sstevel@tonic-gate 	(void) memcpy(&header, data, sizeof (header));
1080Sstevel@tonic-gate 	id = ntohs(header.id);
1090Sstevel@tonic-gate 	qdcount = ntohs(header.qdcount);
1100Sstevel@tonic-gate 	ancount = ntohs(header.ancount);
1110Sstevel@tonic-gate 	nscount = ntohs(header.nscount);
1120Sstevel@tonic-gate 	arcount = ntohs(header.arcount);
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate 	if (flags & F_SUM) {
1150Sstevel@tonic-gate 		line = get_sum_line();
116*4904Srs200217 		line += sprintf(line, "%s %c ",
117*4904Srs200217 		    protostr, header.qr ? 'R' : 'C');
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate 		if (header.qr) {
1200Sstevel@tonic-gate 			/* answer */
1210Sstevel@tonic-gate 			if (header.rcode == 0) {
1220Sstevel@tonic-gate 				/* reply is OK */
1233851Ssm26363 				rrp = data + sizeof (dns_header);
1240Sstevel@tonic-gate 				while (qdcount--) {
1253851Ssm26363 					if (rrp >= data_end) {
1260Sstevel@tonic-gate 						return;
1270Sstevel@tonic-gate 					}
1283851Ssm26363 					rrp += skip_question(data,
1293851Ssm26363 					    rrp, data_end);
1300Sstevel@tonic-gate 				}
1313851Ssm26363 				/* the answers follow the questions */
1320Sstevel@tonic-gate 				if (ancount > 0) {
1330Sstevel@tonic-gate 					(void) print_answer(line,
1343851Ssm26363 					    data, rrp, data_end, FALSE);
1350Sstevel@tonic-gate 				}
1360Sstevel@tonic-gate 			} else {
1370Sstevel@tonic-gate 				(void) sprintf(line, " Error: %d(%s)",
1380Sstevel@tonic-gate 				    header.rcode,
1390Sstevel@tonic-gate 				    dns_rcode_string(header.rcode));
1400Sstevel@tonic-gate 			}
1410Sstevel@tonic-gate 		} else {
1420Sstevel@tonic-gate 			/* question */
1433851Ssm26363 			rrp = data + sizeof (dns_header);
1443851Ssm26363 			if (rrp >= data_end) {
1450Sstevel@tonic-gate 				return;
1460Sstevel@tonic-gate 			}
1473851Ssm26363 			(void) print_question(line, data, rrp, data_end,
1480Sstevel@tonic-gate 			    FALSE);
1490Sstevel@tonic-gate 		}
1500Sstevel@tonic-gate 	}
1510Sstevel@tonic-gate 	if (flags & F_DTAIL) {
152*4904Srs200217 		show_header(protopfxstr, protohdrstr, sizeof (dns_header));
1530Sstevel@tonic-gate 		show_space();
1540Sstevel@tonic-gate 		if (header.qr) {
1550Sstevel@tonic-gate 			/* answer */
1560Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1570Sstevel@tonic-gate 			    "Response ID = %d", id);
1580Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1590Sstevel@tonic-gate 			    "%s%s%s",
1600Sstevel@tonic-gate 			    header.aa ? "AA (Authoritative Answer) " : "",
1610Sstevel@tonic-gate 			    header.tc ? "TC (TrunCation) " : "",
1620Sstevel@tonic-gate 			    header.ra ? "RA (Recursion Available) ": "");
1630Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1640Sstevel@tonic-gate 			    "Response Code: %d (%s)",
1650Sstevel@tonic-gate 			    header.rcode, dns_rcode_string(header.rcode));
1660Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1670Sstevel@tonic-gate 			    "Reply to %d question(s)", qdcount);
1680Sstevel@tonic-gate 		} else {
1690Sstevel@tonic-gate 			/* question */
1700Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1710Sstevel@tonic-gate 			    "Query ID = %d", id);
1720Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1730Sstevel@tonic-gate 			    "Opcode: %s", dns_opcode_string(header.opcode));
1740Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1750Sstevel@tonic-gate 			    "%s%s",
1760Sstevel@tonic-gate 			    header.tc ? "TC (TrunCation) " : "",
1770Sstevel@tonic-gate 			    header.rd ? "RD (Recursion Desired) " : "");
1780Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1790Sstevel@tonic-gate 			    "%d question(s)", qdcount);
1803851Ssm26363 		}
1813851Ssm26363 		rrp = data + sizeof (dns_header);
1823851Ssm26363 		count = 0;
1833851Ssm26363 		while (qdcount--) {
1843851Ssm26363 			if (rrp >= data_end) {
1853851Ssm26363 				return;
1863851Ssm26363 			}
1873851Ssm26363 			count++;
1883851Ssm26363 			rrp += print_question(get_line(0, 0),
1893851Ssm26363 			    data, rrp, data_end, TRUE);
1903851Ssm26363 			show_space();
1913851Ssm26363 		}
1923851Ssm26363 		/* Only answers should hold answers, but just in case */
1933851Ssm26363 		if (header.qr || ancount > 0) {
1943851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
1953851Ssm26363 			    "%d answer(s)", ancount);
1960Sstevel@tonic-gate 			count = 0;
1973851Ssm26363 			while (ancount--) {
1983851Ssm26363 				if (rrp >= data_end) {
1993851Ssm26363 					return;
2003851Ssm26363 				}
2010Sstevel@tonic-gate 				count++;
2023851Ssm26363 				rrp += print_answer(get_line(0, 0),
2033851Ssm26363 				    data, rrp, data_end, TRUE);
2043851Ssm26363 				show_space();
2053851Ssm26363 			}
2063851Ssm26363 		}
2073851Ssm26363 		/* Likewise only answers should hold NS records */
2083851Ssm26363 		if (header.qr || nscount > 0) {
2093851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
2103851Ssm26363 			    "%d name server resource(s)", nscount);
2113851Ssm26363 			count = 0;
2123851Ssm26363 			while (nscount--) {
2133851Ssm26363 				if (rrp >= data_end) {
2143851Ssm26363 					return;
2153851Ssm26363 				}
2163851Ssm26363 				count++;
2173851Ssm26363 				rrp += print_answer(get_line(0, 0), data,
2183851Ssm26363 				    rrp, data_end, TRUE);
2193851Ssm26363 				show_space();
2203851Ssm26363 			}
2213851Ssm26363 		}
2223851Ssm26363 		/* Additional section may hold an EDNS0 record. */
2233851Ssm26363 		if (header.qr || arcount > 0) {
2243851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
2253851Ssm26363 			    "%d additional record(s)", arcount);
2263851Ssm26363 			count = 0;
2273851Ssm26363 			while (arcount-- && rrp < data_end) {
2283851Ssm26363 				count++;
2293851Ssm26363 				rrp += print_answer(get_line(0, 0), data,
2303851Ssm26363 				    rrp, data_end, TRUE);
2310Sstevel@tonic-gate 				show_space();
2320Sstevel@tonic-gate 			}
2330Sstevel@tonic-gate 		}
2340Sstevel@tonic-gate 	}
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate static char *
dns_opcode_string(uint_t opcode)2390Sstevel@tonic-gate dns_opcode_string(uint_t opcode)
2400Sstevel@tonic-gate {
2410Sstevel@tonic-gate 	static char buffer[64];
2420Sstevel@tonic-gate 	switch (opcode) {
2430Sstevel@tonic-gate 	case ns_o_query:	return ("Query");
2440Sstevel@tonic-gate 	case ns_o_iquery:	return ("Inverse Query");
2450Sstevel@tonic-gate 	case ns_o_status:	return ("Status");
2460Sstevel@tonic-gate 	default:
2470Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)",
2480Sstevel@tonic-gate 		    opcode);
2490Sstevel@tonic-gate 		return (buffer);
2500Sstevel@tonic-gate 	}
2510Sstevel@tonic-gate }
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate static char *
dns_rcode_string(uint_t rcode)2540Sstevel@tonic-gate dns_rcode_string(uint_t rcode)
2550Sstevel@tonic-gate {
2560Sstevel@tonic-gate 	static char buffer[64];
2570Sstevel@tonic-gate 	switch (rcode) {
2580Sstevel@tonic-gate 	case ns_r_noerror:	return ("OK");
2590Sstevel@tonic-gate 	case ns_r_formerr:	return ("Format Error");
2600Sstevel@tonic-gate 	case ns_r_servfail:	return ("Server Fail");
2610Sstevel@tonic-gate 	case ns_r_nxdomain:	return ("Name Error");
2620Sstevel@tonic-gate 	case ns_r_notimpl:	return ("Unimplemented");
2630Sstevel@tonic-gate 	case ns_r_refused:	return ("Refused");
2643851Ssm26363 	case ns_r_badvers:	return ("Bad Version"); /* EDNS rcode */
2650Sstevel@tonic-gate 	default:
2660Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", rcode);
2670Sstevel@tonic-gate 		return (buffer);
2680Sstevel@tonic-gate 	}
2690Sstevel@tonic-gate }
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate static char *
dns_type_string(uint_t type,int detail)2720Sstevel@tonic-gate dns_type_string(uint_t type, int detail)
2730Sstevel@tonic-gate {
2740Sstevel@tonic-gate 	static char buffer[64];
2750Sstevel@tonic-gate 	switch (type) {
2760Sstevel@tonic-gate 	case ns_t_a:	return (detail ? "Address" : "Addr");
2770Sstevel@tonic-gate 	case ns_t_ns:	return (detail ? "Authoritative Name Server" : "NS");
2780Sstevel@tonic-gate 	case ns_t_cname:	return (detail ? "Canonical Name" : "CNAME");
2790Sstevel@tonic-gate 	case ns_t_soa:	return (detail ? "Start Of a zone Authority" : "SOA");
2800Sstevel@tonic-gate 	case ns_t_mb:	return (detail ? "Mailbox domain name" : "MB");
2810Sstevel@tonic-gate 	case ns_t_mg:	return (detail ? "Mailbox Group member" : "MG");
2820Sstevel@tonic-gate 	case ns_t_mr:	return (detail ? "Mail Rename domain name" : "MR");
2830Sstevel@tonic-gate 	case ns_t_null:	return ("NULL");
2840Sstevel@tonic-gate 	case ns_t_wks:	return (detail ? "Well Known Service" : "WKS");
2850Sstevel@tonic-gate 	case ns_t_ptr:	return (detail ? "Domain Name Pointer" : "PTR");
2860Sstevel@tonic-gate 	case ns_t_hinfo:	return (detail ? "Host Information": "HINFO");
2870Sstevel@tonic-gate 	case ns_t_minfo:
2880Sstevel@tonic-gate 		return (detail ? "Mailbox or maillist Info" : "MINFO");
2890Sstevel@tonic-gate 	case ns_t_mx:	return (detail ? "Mail Exchange" : "MX");
2900Sstevel@tonic-gate 	case ns_t_txt:	return (detail ? "Text strings" : "TXT");
2910Sstevel@tonic-gate 	case ns_t_aaaa:	return (detail ? "IPv6 Address" : "AAAA");
2923851Ssm26363 	case ns_t_opt:	return (detail ? "EDNS0 option" : "OPT");
2930Sstevel@tonic-gate 	case ns_t_axfr:	return (detail ? "Transfer of entire zone" : "AXFR");
2940Sstevel@tonic-gate 	case ns_t_mailb:
2950Sstevel@tonic-gate 		return (detail ? "Mailbox related records" : "MAILB");
2960Sstevel@tonic-gate 	case ns_t_maila:	return (detail ? "Mail agent RRs" : "MAILA");
2970Sstevel@tonic-gate 	case ns_t_any:	return (detail ? "All records" : "*");
2980Sstevel@tonic-gate 	default:
2990Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", type);
3000Sstevel@tonic-gate 		return (buffer);
3010Sstevel@tonic-gate 	}
3020Sstevel@tonic-gate }
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate static char *
dns_class_string(uint_t cls,int detail)3050Sstevel@tonic-gate dns_class_string(uint_t cls, int detail)
3060Sstevel@tonic-gate {
3070Sstevel@tonic-gate 	static char buffer[64];
3080Sstevel@tonic-gate 	switch (cls) {
3090Sstevel@tonic-gate 	case ns_c_in:		return (detail ? "Internet" : "Internet");
3100Sstevel@tonic-gate 	case ns_c_chaos: 	return (detail ? "CHAOS" : "CH");
3110Sstevel@tonic-gate 	case ns_c_hs:		return (detail ? "Hesiod" : "HS");
3120Sstevel@tonic-gate 	case ns_c_any:		return (detail ? "* (Any class)" : "*");
3130Sstevel@tonic-gate 	default:
3140Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", cls);
3150Sstevel@tonic-gate 		return (buffer);
3160Sstevel@tonic-gate 	}
3170Sstevel@tonic-gate }
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate static size_t
skip_question(const uchar_t * header,const uchar_t * data,const uchar_t * data_end)3200Sstevel@tonic-gate skip_question(const uchar_t *header, const uchar_t *data,
3210Sstevel@tonic-gate     const uchar_t *data_end)
3220Sstevel@tonic-gate {
3230Sstevel@tonic-gate 	const uchar_t *data_bak = data;
3240Sstevel@tonic-gate 	char dummy_buffer[NS_MAXDNAME];
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	data = get_domain_name(header, data, data_end, dummy_buffer,
3270Sstevel@tonic-gate 	    dummy_buffer + sizeof (dummy_buffer));
3280Sstevel@tonic-gate 	/* Skip the 32 bits of class and type that follow the domain name */
3290Sstevel@tonic-gate 	data += sizeof (uint32_t);
3300Sstevel@tonic-gate 	return (data - data_bak);
3310Sstevel@tonic-gate }
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate static size_t
print_question(char * line,const uchar_t * header,const uchar_t * data,const uchar_t * data_end,int detail)3340Sstevel@tonic-gate print_question(char *line, const uchar_t *header, const uchar_t *data,
3350Sstevel@tonic-gate     const uchar_t *data_end, int detail)
3360Sstevel@tonic-gate {
3370Sstevel@tonic-gate 	const uchar_t *data_bak = data;
3380Sstevel@tonic-gate 	uint16_t type;
3390Sstevel@tonic-gate 	uint16_t cls;
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	if (detail) {
3420Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
3430Sstevel@tonic-gate 		    DNS_INDENT "Domain Name: ");
3440Sstevel@tonic-gate 	}
3450Sstevel@tonic-gate 	data += print_domain_name(line, header, data, data_end);
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	/*
3480Sstevel@tonic-gate 	 * Make sure we don't run off the end of the packet by reading the
3490Sstevel@tonic-gate 	 * type and class.
3500Sstevel@tonic-gate 	 *
3510Sstevel@tonic-gate 	 * The pointer subtraction on the left side of the following
3520Sstevel@tonic-gate 	 * expression has a signed result of type ptrdiff_t, and the right
3530Sstevel@tonic-gate 	 * side has an unsigned result of type size_t.  We therefore need
3540Sstevel@tonic-gate 	 * to cast the right side of the expression to be of the same
3550Sstevel@tonic-gate 	 * signed type to keep the result of the pointer arithmetic to be
3560Sstevel@tonic-gate 	 * automatically cast to an unsigned value.  We do a similar cast
3570Sstevel@tonic-gate 	 * in other similar expressions throughout this file.
3580Sstevel@tonic-gate 	 */
3590Sstevel@tonic-gate 	if ((data_end - data) < (ptrdiff_t)(2 * sizeof (uint16_t)))
3600Sstevel@tonic-gate 		return (data_end - data_bak);
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	GETINT16(type, data);
3630Sstevel@tonic-gate 	GETINT16(cls, data);
3640Sstevel@tonic-gate 
365*4904Srs200217 	/*
366*4904Srs200217 	 * Multicast DNS re-uses the top bit of the class field
367*4904Srs200217 	 * in the question and answer sections. Unicast DNS only
368*4904Srs200217 	 * uses 1 (Internet), 3 and 4. Hence it is safe. The top
369*4904Srs200217 	 * order bit is always cleared here to display the rrclass in case
370*4904Srs200217 	 * of Multicast DNS packets.
371*4904Srs200217 	 */
372*4904Srs200217 	cls = cls & 0x7fff;
373*4904Srs200217 
3740Sstevel@tonic-gate 	if (detail) {
3750Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
3760Sstevel@tonic-gate 		    DNS_INDENT "Class: %u (%s)",
3770Sstevel@tonic-gate 		    cls, dns_class_string(cls, detail));
3780Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
3790Sstevel@tonic-gate 		    DNS_INDENT "Type:  %u (%s)", type,
3800Sstevel@tonic-gate 		    dns_type_string(type, detail));
3810Sstevel@tonic-gate 	} else {
3820Sstevel@tonic-gate 		(void) sprintf(line + strlen(line), " %s %s \?",
3830Sstevel@tonic-gate 		    dns_class_string(cls, detail),
3840Sstevel@tonic-gate 		    dns_type_string(type, detail));
3850Sstevel@tonic-gate 	}
3860Sstevel@tonic-gate 	return (data - data_bak);
3870Sstevel@tonic-gate }
3880Sstevel@tonic-gate 
3893851Ssm26363 /*
3903851Ssm26363  * print_answer() is used to display the contents of a single resource
3913851Ssm26363  * record (RR) from either the answer, name server or additional
3923851Ssm26363  * section of the DNS packet.
3933851Ssm26363  *
3943851Ssm26363  * Input:
3953851Ssm26363  *	*line: snoops output buffer.
3963851Ssm26363  *	*header: start of the DNS packet, required for names and rcode.
3973851Ssm26363  *	*data: location within header from where the RR starts.
3983851Ssm26363  * 	*data_end: where DNS data ends.
3993851Ssm26363  * 	detail: simple or verbose output.
4003851Ssm26363  *
4013851Ssm26363  * Returns:
4023851Ssm26363  *	Pointer to next RR or data_end.
4033851Ssm26363  *
4043851Ssm26363  * Most RRs have the same top level format as defined in RFC 1035:
4053851Ssm26363  *
4063851Ssm26363  *                                     1  1  1  1  1  1
4073851Ssm26363  *       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
4083851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4093851Ssm26363  *    |                                               |
4103851Ssm26363  *    /                      NAME                     /
4113851Ssm26363  *    |                                               |
4123851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4133851Ssm26363  *    |                      TYPE                     |
4143851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4153851Ssm26363  *    |                     CLASS                     |
4163851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4173851Ssm26363  *    |                      TTL                      |
4183851Ssm26363  *    |                                               |
4193851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4203851Ssm26363  *    |                   RDLENGTH                    |
4213851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
4223851Ssm26363  *    /                     RDATA                     /
4233851Ssm26363  *    /                                               /
4243851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4253851Ssm26363  *
4263851Ssm26363  * However RFC 2671 introduced an exception to this rule
4273851Ssm26363  * with the "Extension Mechanisms for DNS" (EDNS0).
4283851Ssm26363  * When the type is 41 the remaining resource record format
4293851Ssm26363  * is:
4303851Ssm26363  *
4313851Ssm26363  *                                     1  1  1  1  1  1
4323851Ssm26363  *       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
4333851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4343851Ssm26363  *    |                    TYPE = 41                  |
4353851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4363851Ssm26363  *    |           Sender's UDP payload size           |
4373851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4383851Ssm26363  *    |    Extended-rcode     |        Version        |
4393851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4403851Ssm26363  *    |                      Zero                     |
4413851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4423851Ssm26363  *    |                   RDLENGTH                    |
4433851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
4443851Ssm26363  *    /                     RDATA                     /
4453851Ssm26363  *    /                                               /
4463851Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4473851Ssm26363  *
4483851Ssm26363  */
4490Sstevel@tonic-gate static size_t
print_answer(char * line,const uchar_t * header,const uchar_t * data,const uchar_t * data_end,int detail)4500Sstevel@tonic-gate print_answer(char *line, const uchar_t *header, const uchar_t *data,
4510Sstevel@tonic-gate     const uchar_t *data_end, int detail)
4520Sstevel@tonic-gate {
4530Sstevel@tonic-gate 	const uchar_t *data_bak = data;
4540Sstevel@tonic-gate 	const uchar_t *data_next;
4550Sstevel@tonic-gate 	uint16_t type;
4560Sstevel@tonic-gate 	uint16_t cls;
4570Sstevel@tonic-gate 	int32_t ttl;
4580Sstevel@tonic-gate 	uint16_t rdlen;
4590Sstevel@tonic-gate 	uint32_t serial, refresh, retry, expire, minimum;
4600Sstevel@tonic-gate 	uint8_t protocol;
4610Sstevel@tonic-gate 	int linepos;
4620Sstevel@tonic-gate 	uint16_t preference;
4633851Ssm26363 	/* declarations for EDNS follow */
4643851Ssm26363 	uint16_t size;	/* Sender's UDP payload size */
4653851Ssm26363 	uint8_t xrcode;	/* Extended-rcode */
4663851Ssm26363 	uint8_t ver;	/* Version */
4673851Ssm26363 	uint16_t rcode;	/* Extracted from the DNS header */
4683851Ssm26363 	union {		/* DNS header overlay used for extraction */
4693851Ssm26363 		HEADER		*head;
4703851Ssm26363 		const uchar_t	*raw;
4713851Ssm26363 	} headptr;
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 	if (detail) {
4740Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
4750Sstevel@tonic-gate 		    DNS_INDENT "Domain Name: ");
4760Sstevel@tonic-gate 	}
4770Sstevel@tonic-gate 	data += print_domain_name(line, header, data, data_end);
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 	/*
4803851Ssm26363 	 * Next, get the record type, being careful to make sure we
4813851Ssm26363 	 * don't run off the end of the packet.
4820Sstevel@tonic-gate 	 */
4833851Ssm26363 	if ((data_end - data) < (ptrdiff_t)(sizeof (type))) {
4840Sstevel@tonic-gate 		return (data_end - data_bak);
4850Sstevel@tonic-gate 	}
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate 	GETINT16(type, data);
4883851Ssm26363 
4893851Ssm26363 	if (type == ns_t_opt) {
4903851Ssm26363 		/*
4913851Ssm26363 		 * Make sure we won't run off the end reading size,
4923851Ssm26363 		 * xrcode, version, zero and rdlen.
4933851Ssm26363 		 */
4943851Ssm26363 		if ((data_end - data) <
4953851Ssm26363 		    ((ptrdiff_t)(sizeof (size)
496*4904Srs200217 		    + sizeof (xrcode)
497*4904Srs200217 		    + sizeof (ver)
498*4904Srs200217 		    + sizeof (cls)	/* zero */
499*4904Srs200217 		    + sizeof (rdlen)))) {
5003851Ssm26363 			return (data_end - data_bak);
5013851Ssm26363 		}
5023851Ssm26363 
5033851Ssm26363 		GETINT16(size, data);
5043851Ssm26363 		GETINT8(xrcode, data);
5053851Ssm26363 		/*
5063851Ssm26363 		 * The extended rcode represents the top half of the
5073851Ssm26363 		 * rcode which must be added to the rcode in the header.
5083851Ssm26363 		 */
5093851Ssm26363 		rcode = 0xff & (xrcode << 4);
5103851Ssm26363 		headptr.raw = header;		/* Overlay the header... */
5113851Ssm26363 		rcode += headptr.head->rcode;	/* And pluck out the rcode. */
5123851Ssm26363 
5133851Ssm26363 		GETINT8(ver, data);
5143851Ssm26363 		GETINT16(cls, data); /* zero */
5153851Ssm26363 		GETINT16(rdlen, data);
5163851Ssm26363 
5173851Ssm26363 		if (detail) {
5183851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
5193851Ssm26363 			    DNS_INDENT "Type:  %u (%s)", type,
5203851Ssm26363 			    dns_type_string(type, detail));
5213851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
5223851Ssm26363 			    DNS_INDENT "UDP payload size: %u (0x%.4x)",
5233851Ssm26363 			    size, size);
5243851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
5253851Ssm26363 			    DNS_INDENT "Extended rcode: %u "
5263851Ssm26363 			    "(translates to %u (%s))",
5273851Ssm26363 			    xrcode, rcode, dns_rcode_string(rcode));
5283851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
5293851Ssm26363 			    DNS_INDENT "EDNS0 Version: %u", ver);
5303851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
5313851Ssm26363 			    DNS_INDENT "zero: %u", cls);
5323851Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
5333851Ssm26363 			    DNS_INDENT "Data length: %u", rdlen);
5343851Ssm26363 		} else {
5353851Ssm26363 			line += strlen(line);
5363851Ssm26363 			line += sprintf(line, " %s UDP %u rc %d ver %u len %u",
5373851Ssm26363 			    dns_type_string(type, detail), size, rcode, ver,
5383851Ssm26363 			    rdlen);
5393851Ssm26363 		}
5403851Ssm26363 
5413851Ssm26363 		/*
5423851Ssm26363 		 * Make sure that rdlen is within data boundary.
5433851Ssm26363 		 */
5443851Ssm26363 		if (rdlen > data_end - data)
5453851Ssm26363 			return (data_end - data_bak);
5463851Ssm26363 
5473851Ssm26363 		/* Future OPT decode code goes here. */
5483851Ssm26363 
5493851Ssm26363 		data += rdlen;
5503851Ssm26363 		return (data - data_bak);
5513851Ssm26363 	}
5523851Ssm26363 
5533851Ssm26363 	/*
5543851Ssm26363 	 * Make sure we don't run off the end of the packet by reading the
5553851Ssm26363 	 * class, ttl, and length.
5563851Ssm26363 	 */
5573851Ssm26363 	if ((data_end - data) <
5583851Ssm26363 	    ((ptrdiff_t)(sizeof (cls)
559*4904Srs200217 	    + sizeof (ttl)
560*4904Srs200217 	    + sizeof (rdlen)))) {
5613851Ssm26363 		return (data_end - data_bak);
5623851Ssm26363 	}
5633851Ssm26363 
5640Sstevel@tonic-gate 	GETINT16(cls, data);
5650Sstevel@tonic-gate 
566*4904Srs200217 	/*
567*4904Srs200217 	 * Multicast DNS re-uses the top bit of the class field
568*4904Srs200217 	 * in the question and answer sections. Unicast DNS only
569*4904Srs200217 	 * uses 1 (Internet), 3 and 4. Hence it is safe. The top
570*4904Srs200217 	 * order bit is always cleared here to display the rrclass in case
571*4904Srs200217 	 * of Multicast DNS packets.
572*4904Srs200217 	 */
573*4904Srs200217 	cls = cls & 0x7fff;
574*4904Srs200217 
5750Sstevel@tonic-gate 	if (detail) {
5760Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
5770Sstevel@tonic-gate 		    DNS_INDENT "Class: %d (%s)", cls,
5780Sstevel@tonic-gate 		    dns_class_string(cls, detail));
5790Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
5800Sstevel@tonic-gate 		    DNS_INDENT "Type:  %d (%s)", type,
5810Sstevel@tonic-gate 		    dns_type_string(type, detail));
5820Sstevel@tonic-gate 	} else {
5830Sstevel@tonic-gate 		line += strlen(line);
5840Sstevel@tonic-gate 		line += sprintf(line, " %s %s ",
5850Sstevel@tonic-gate 		    dns_class_string(cls, detail),
5860Sstevel@tonic-gate 		    dns_type_string(type, detail));
5870Sstevel@tonic-gate 	}
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	GETINT32(ttl, data);
5900Sstevel@tonic-gate 	if (detail) {
5910Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
5920Sstevel@tonic-gate 		    DNS_INDENT "TTL (Time To Live): %d", ttl);
5930Sstevel@tonic-gate 	}
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 	GETINT16(rdlen, data);
5960Sstevel@tonic-gate 	if (detail) {
5970Sstevel@tonic-gate 		line = get_line(0, 0);
5980Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(), DNS_INDENT "%s: ",
5990Sstevel@tonic-gate 		    dns_type_string(type, detail));
6000Sstevel@tonic-gate 	}
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 	if (rdlen > data_end - data)
6030Sstevel@tonic-gate 		return (data_end - data_bak);
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	switch (type) {
6060Sstevel@tonic-gate 	case ns_t_a:
6070Sstevel@tonic-gate 		print_ip(AF_INET, line, data, rdlen);
6080Sstevel@tonic-gate 		break;
6090Sstevel@tonic-gate 	case ns_t_aaaa:
6100Sstevel@tonic-gate 		print_ip(AF_INET6, line, data, rdlen);
6110Sstevel@tonic-gate 		break;
6120Sstevel@tonic-gate 	case ns_t_hinfo:
6130Sstevel@tonic-gate 		line += sprintf(line, "CPU: ");
6140Sstevel@tonic-gate 		data_next = data + print_char_string(line, data, rdlen);
6150Sstevel@tonic-gate 		if (data_next >= data_end)
6160Sstevel@tonic-gate 			break;
6170Sstevel@tonic-gate 		line += strlen(line);
6180Sstevel@tonic-gate 		line += sprintf(line, "OS: ");
6190Sstevel@tonic-gate 		(void) print_char_string(line, data_next,
6200Sstevel@tonic-gate 		    rdlen - (data_next - data));
6210Sstevel@tonic-gate 		break;
6220Sstevel@tonic-gate 	case ns_t_ns:
6230Sstevel@tonic-gate 	case ns_t_cname:
6240Sstevel@tonic-gate 	case ns_t_mb:
6250Sstevel@tonic-gate 	case ns_t_mg:
6260Sstevel@tonic-gate 	case ns_t_mr:
6270Sstevel@tonic-gate 	case ns_t_ptr:
6280Sstevel@tonic-gate 		(void) print_domain_name(line, header, data, data_end);
6290Sstevel@tonic-gate 		break;
6300Sstevel@tonic-gate 	case ns_t_mx:
6310Sstevel@tonic-gate 		data_next = data;
6320Sstevel@tonic-gate 		if (rdlen < sizeof (uint16_t))
6330Sstevel@tonic-gate 			break;
6340Sstevel@tonic-gate 		GETINT16(preference, data_next);
6350Sstevel@tonic-gate 		if (detail) {
6360Sstevel@tonic-gate 			(void) print_domain_name(line, header, data_next,
6370Sstevel@tonic-gate 			    data_end);
6380Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
6390Sstevel@tonic-gate 			    DNS_INDENT "Preference: %u", preference);
6400Sstevel@tonic-gate 		} else {
6410Sstevel@tonic-gate 			(void) print_domain_name(line, header, data_next,
6420Sstevel@tonic-gate 			    data_end);
6430Sstevel@tonic-gate 		}
6440Sstevel@tonic-gate 		break;
6450Sstevel@tonic-gate 	case ns_t_soa:
6460Sstevel@tonic-gate 		if (!detail)
6470Sstevel@tonic-gate 			break;
6480Sstevel@tonic-gate 		line = get_line(0, 0);
6490Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
6500Sstevel@tonic-gate 		    DNS_INDENT "MNAME (Server name): ");
6510Sstevel@tonic-gate 		data_next = data + print_domain_name(line, header, data,
6520Sstevel@tonic-gate 		    data_end);
6530Sstevel@tonic-gate 		if (data_next >= data_end)
6540Sstevel@tonic-gate 			break;
6550Sstevel@tonic-gate 		line = get_line(0, 0);
6560Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
6570Sstevel@tonic-gate 		    DNS_INDENT "RNAME (Resposible mailbox): ");
6580Sstevel@tonic-gate 		data_next = data_next +
6590Sstevel@tonic-gate 		    print_domain_name(line, header, data_next, data_end);
6600Sstevel@tonic-gate 		if ((data_end - data_next) < (ptrdiff_t)(5 * sizeof (uint32_t)))
6610Sstevel@tonic-gate 			break;
6620Sstevel@tonic-gate 		GETINT32(serial, data_next);
6630Sstevel@tonic-gate 		GETINT32(refresh, data_next);
6640Sstevel@tonic-gate 		GETINT32(retry, data_next);
6650Sstevel@tonic-gate 		GETINT32(expire, data_next);
6660Sstevel@tonic-gate 		GETINT32(minimum, data_next);
6670Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
6680Sstevel@tonic-gate 		    DNS_INDENT "Serial: %u", serial);
6690Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
6700Sstevel@tonic-gate 		    DNS_INDENT "Refresh: %u  Retry: %u  "
6710Sstevel@tonic-gate 		    "Expire: %u Minimum: %u",
6720Sstevel@tonic-gate 		    refresh, retry, expire, minimum);
6730Sstevel@tonic-gate 		break;
6740Sstevel@tonic-gate 	case ns_t_wks:
6750Sstevel@tonic-gate 		print_ip(AF_INET, line, data, rdlen);
6760Sstevel@tonic-gate 		if (!detail)
6770Sstevel@tonic-gate 			break;
6780Sstevel@tonic-gate 		data_next = data + sizeof (in_addr_t);
6790Sstevel@tonic-gate 		if (data_next >= data_end)
6800Sstevel@tonic-gate 			break;
6810Sstevel@tonic-gate 		GETINT8(protocol, data_next);
6820Sstevel@tonic-gate 		line = get_line(0, 0);
6830Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
6840Sstevel@tonic-gate 		    DNS_INDENT "Protocol: %u ", protocol);
6850Sstevel@tonic-gate 		switch (protocol) {
6860Sstevel@tonic-gate 		case IPPROTO_UDP:
6870Sstevel@tonic-gate 			(void) snprintf(line, get_line_remain(), "(UDP)");
6880Sstevel@tonic-gate 			break;
6890Sstevel@tonic-gate 		case IPPROTO_TCP:
6900Sstevel@tonic-gate 			(void) snprintf(line, get_line_remain(), "(TCP)");
6910Sstevel@tonic-gate 			break;
6920Sstevel@tonic-gate 		}
6930Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
6940Sstevel@tonic-gate 		    DNS_INDENT "Service bitmap:");
6950Sstevel@tonic-gate 		(void) snprintf(line, get_line_remain(),
6960Sstevel@tonic-gate 		    DNS_INDENT "0       8       16      24");
6970Sstevel@tonic-gate 		linepos = 4;
6980Sstevel@tonic-gate 		while (data_next < data + rdlen) {
6990Sstevel@tonic-gate 			if (linepos == 4) {
7000Sstevel@tonic-gate 				line = get_line(0, 0);
7010Sstevel@tonic-gate 				line += snprintf(line, get_line_remain(),
7020Sstevel@tonic-gate 				    DNS_INDENT);
7030Sstevel@tonic-gate 				linepos = 0;
7040Sstevel@tonic-gate 			}
7050Sstevel@tonic-gate 			line += snprintf(line, get_line_remain(), "%s",
7060Sstevel@tonic-gate 			    binary_string(*data_next));
7070Sstevel@tonic-gate 			linepos++;
7080Sstevel@tonic-gate 			data_next++;
7090Sstevel@tonic-gate 		}
7100Sstevel@tonic-gate 		break;
7110Sstevel@tonic-gate 	case ns_t_minfo:
7120Sstevel@tonic-gate 		if (!detail)
7130Sstevel@tonic-gate 			break;
7140Sstevel@tonic-gate 		line = get_line(0, 0);
7150Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
7160Sstevel@tonic-gate 		    DNS_INDENT "RMAILBX (Resposible mailbox): ");
7170Sstevel@tonic-gate 		data_next = data + print_domain_name(line, header, data,
7180Sstevel@tonic-gate 		    data_end);
7190Sstevel@tonic-gate 		line = get_line(0, 0);
7200Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
7210Sstevel@tonic-gate 		    DNS_INDENT "EMAILBX (mailbox to receive err message): ");
7220Sstevel@tonic-gate 		data_next = data_next + print_domain_name(line, header,
7230Sstevel@tonic-gate 		    data_next, data_end);
7240Sstevel@tonic-gate 		break;
7250Sstevel@tonic-gate 	}
7260Sstevel@tonic-gate 	data += rdlen;
7270Sstevel@tonic-gate 	return (data - data_bak);
7280Sstevel@tonic-gate }
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate static char *
binary_string(char data)7310Sstevel@tonic-gate binary_string(char data)
7320Sstevel@tonic-gate {
7330Sstevel@tonic-gate 	static char bstring[8 + 1];
7340Sstevel@tonic-gate 	char *ptr;
7350Sstevel@tonic-gate 	int i;
7360Sstevel@tonic-gate 	ptr = bstring;
7370Sstevel@tonic-gate 	for (i = 0; i < 8; i++) {
7380Sstevel@tonic-gate 		*ptr++ = (data & 0x80) ? '1' : '0';
7390Sstevel@tonic-gate 		data = data << 1;
7400Sstevel@tonic-gate 	}
7410Sstevel@tonic-gate 	*ptr = (char)0;
7420Sstevel@tonic-gate 	return (bstring);
7430Sstevel@tonic-gate }
7440Sstevel@tonic-gate 
7450Sstevel@tonic-gate static void
print_ip(int af,char * line,const uchar_t * data,uint16_t len)7460Sstevel@tonic-gate print_ip(int af, char *line, const uchar_t *data, uint16_t len)
7470Sstevel@tonic-gate {
7480Sstevel@tonic-gate 	in6_addr_t	addr6;
7490Sstevel@tonic-gate 	in_addr_t	addr4;
7500Sstevel@tonic-gate 	void		*addr;
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate 	switch (af) {
7530Sstevel@tonic-gate 	case AF_INET:
7540Sstevel@tonic-gate 		if (len != sizeof (in_addr_t))
7550Sstevel@tonic-gate 			return;
7560Sstevel@tonic-gate 		addr = memcpy(&addr4, data, sizeof (addr4));
7570Sstevel@tonic-gate 		break;
7580Sstevel@tonic-gate 	case AF_INET6:
7590Sstevel@tonic-gate 		if (len != sizeof (in6_addr_t))
7600Sstevel@tonic-gate 			return;
7610Sstevel@tonic-gate 		addr = memcpy(&addr6, data, sizeof (addr6));
7620Sstevel@tonic-gate 		break;
7630Sstevel@tonic-gate 	}
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate 	(void) inet_ntop(af, addr, line, INET6_ADDRSTRLEN);
7660Sstevel@tonic-gate }
7670Sstevel@tonic-gate 
7680Sstevel@tonic-gate /*
7690Sstevel@tonic-gate  * charbuf is assumed to be of size MAX_CHAR_STRING_SIZE.
7700Sstevel@tonic-gate  */
7710Sstevel@tonic-gate static const uchar_t *
get_char_string(const uchar_t * data,char * charbuf,uint16_t datalen)7720Sstevel@tonic-gate get_char_string(const uchar_t *data, char *charbuf, uint16_t datalen)
7730Sstevel@tonic-gate {
774410Skcpoon 	int len;
7750Sstevel@tonic-gate 	char *name = charbuf;
7760Sstevel@tonic-gate 	int i = 0;
7770Sstevel@tonic-gate 
7780Sstevel@tonic-gate 	/*
7790Sstevel@tonic-gate 	 * From RFC1035, a character-string is a single length octet followed
7800Sstevel@tonic-gate 	 * by that number of characters.
7810Sstevel@tonic-gate 	 */
7820Sstevel@tonic-gate 	if (datalen > 1) {
7830Sstevel@tonic-gate 		len = *data;
7840Sstevel@tonic-gate 		data++;
7850Sstevel@tonic-gate 		if (len > 0 && len < MAX_CHAR_STRING_SIZE) {
7860Sstevel@tonic-gate 			for (i = 0; i < len; i++, data++)
7870Sstevel@tonic-gate 				name[i] = *data;
7880Sstevel@tonic-gate 		}
7890Sstevel@tonic-gate 	}
7900Sstevel@tonic-gate 	name[i] = '\0';
7910Sstevel@tonic-gate 	return (data);
7920Sstevel@tonic-gate }
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate static size_t
print_char_string(char * line,const uchar_t * data,uint16_t len)7950Sstevel@tonic-gate print_char_string(char *line, const uchar_t *data, uint16_t len)
7960Sstevel@tonic-gate {
7970Sstevel@tonic-gate 	char charbuf[MAX_CHAR_STRING_SIZE];
7980Sstevel@tonic-gate 	const uchar_t *data_bak = data;
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 	data = get_char_string(data, charbuf, len);
8010Sstevel@tonic-gate 	(void) sprintf(line, "%s", charbuf);
8020Sstevel@tonic-gate 	return (data - data_bak);
8030Sstevel@tonic-gate }
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate /*
8060Sstevel@tonic-gate  * header: the entire message header, this is where we start to
8070Sstevel@tonic-gate  *	   count the offset of the compression scheme
8080Sstevel@tonic-gate  * data:   the start of the domain name
8090Sstevel@tonic-gate  * namebuf: user supplied buffer
8100Sstevel@tonic-gate  * return: the next byte after what we have parsed
8110Sstevel@tonic-gate  */
8120Sstevel@tonic-gate static const uchar_t *
get_domain_name(const uchar_t * header,const uchar_t * data,const uchar_t * data_end,char * namebuf,char * namend)8130Sstevel@tonic-gate get_domain_name(const uchar_t *header, const uchar_t *data,
8140Sstevel@tonic-gate     const uchar_t *data_end, char *namebuf, char *namend)
8150Sstevel@tonic-gate {
8160Sstevel@tonic-gate 	uint8_t len;
8170Sstevel@tonic-gate 	char *name = namebuf;
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate 	/*
8200Sstevel@tonic-gate 	 * From RFC1035, a domain name is a sequence of labels, where each
8210Sstevel@tonic-gate 	 * label consists of a length octet followed by that number of
8220Sstevel@tonic-gate 	 * octets.  The domain name terminates with the zero length octet
8230Sstevel@tonic-gate 	 * for the null label of the root.
8240Sstevel@tonic-gate 	 */
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate 	while (name < (namend - 1)) {
8270Sstevel@tonic-gate 		if ((data_end - data) < (ptrdiff_t)(sizeof (uint8_t))) {
8280Sstevel@tonic-gate 			/* The length octet is off the end of the packet. */
8290Sstevel@tonic-gate 			break;
8300Sstevel@tonic-gate 		}
8310Sstevel@tonic-gate 		GETINT8(len, data);
8320Sstevel@tonic-gate 		if (len == 0) {
8330Sstevel@tonic-gate 			/*
8340Sstevel@tonic-gate 			 * Domain names end with a length byte of zero,
8350Sstevel@tonic-gate 			 * which represents the null label of the root.
8360Sstevel@tonic-gate 			 */
8370Sstevel@tonic-gate 			break;
8380Sstevel@tonic-gate 		}
8390Sstevel@tonic-gate 		/*
8400Sstevel@tonic-gate 		 * test if we are using the compression scheme
8410Sstevel@tonic-gate 		 */
8420Sstevel@tonic-gate 		if ((len & 0xc0) == 0xc0) {
8430Sstevel@tonic-gate 			uint16_t offset;
8440Sstevel@tonic-gate 			const uchar_t *label_ptr;
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate 			/*
8470Sstevel@tonic-gate 			 * From RFC1035, message compression allows a
8480Sstevel@tonic-gate 			 * domain name or a list of labels at the end of a
8490Sstevel@tonic-gate 			 * domain name to be replaced with a pointer to a
8500Sstevel@tonic-gate 			 * prior occurance of the same name.  In this
8510Sstevel@tonic-gate 			 * scheme, the pointer is a two octet sequence
8520Sstevel@tonic-gate 			 * where the most significant two bits are set, and
8530Sstevel@tonic-gate 			 * the remaining 14 bits are the offset from the
8540Sstevel@tonic-gate 			 * start of the message of the next label.
8550Sstevel@tonic-gate 			 */
8560Sstevel@tonic-gate 			data--;
8570Sstevel@tonic-gate 			if ((data_end - data) <
8580Sstevel@tonic-gate 			    (ptrdiff_t)(sizeof (uint16_t))) {
8590Sstevel@tonic-gate 				/*
8600Sstevel@tonic-gate 				 * The offset octets aren't entirely
8610Sstevel@tonic-gate 				 * contained within this pakcet.
8620Sstevel@tonic-gate 				 */
8630Sstevel@tonic-gate 				data = data_end;
8640Sstevel@tonic-gate 				break;
8650Sstevel@tonic-gate 			}
8660Sstevel@tonic-gate 			GETINT16(offset, data);
8670Sstevel@tonic-gate 			label_ptr = header + (offset & 0x3fff);
8680Sstevel@tonic-gate 			/*
8690Sstevel@tonic-gate 			 * We must verify that the offset is valid by
8700Sstevel@tonic-gate 			 * checking that it is less than the current data
8710Sstevel@tonic-gate 			 * pointer and that it isn't off the end of the
8720Sstevel@tonic-gate 			 * packet.
8730Sstevel@tonic-gate 			 */
8740Sstevel@tonic-gate 			if (label_ptr > data || label_ptr >= data_end)
8750Sstevel@tonic-gate 				break;
8760Sstevel@tonic-gate 			(void) get_domain_name(header, label_ptr, data_end,
8770Sstevel@tonic-gate 			    name, namend);
8780Sstevel@tonic-gate 			return (data);
8790Sstevel@tonic-gate 		} else {
8800Sstevel@tonic-gate 			if (len > (data_end - data)) {
8810Sstevel@tonic-gate 				/*
8820Sstevel@tonic-gate 				 * The label isn't entirely contained
8830Sstevel@tonic-gate 				 * within the packet.  Don't read it.  The
8840Sstevel@tonic-gate 				 * caller checks that the data pointer is
8850Sstevel@tonic-gate 				 * not beyond the end after we've
8860Sstevel@tonic-gate 				 * incremented it.
8870Sstevel@tonic-gate 				 */
8880Sstevel@tonic-gate 				data = data_end;
8890Sstevel@tonic-gate 				break;
8900Sstevel@tonic-gate 			}
8910Sstevel@tonic-gate 			while (len > 0 && name < (namend - 2)) {
8920Sstevel@tonic-gate 				*name = *data;
8930Sstevel@tonic-gate 				name++;
8940Sstevel@tonic-gate 				data++;
8950Sstevel@tonic-gate 				len--;
8960Sstevel@tonic-gate 			}
8970Sstevel@tonic-gate 			*name = '.';
8980Sstevel@tonic-gate 			name++;
8990Sstevel@tonic-gate 		}
9000Sstevel@tonic-gate 	}
9010Sstevel@tonic-gate 	*name = '\0';
9020Sstevel@tonic-gate 	return (data);
9030Sstevel@tonic-gate }
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate static size_t
print_domain_name(char * line,const uchar_t * header,const uchar_t * data,const uchar_t * data_end)9060Sstevel@tonic-gate print_domain_name(char *line, const uchar_t *header, const uchar_t *data,
9070Sstevel@tonic-gate     const uchar_t *data_end)
9080Sstevel@tonic-gate {
9090Sstevel@tonic-gate 	char name[NS_MAXDNAME];
9100Sstevel@tonic-gate 	const uchar_t *new_data;
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 	new_data = get_domain_name(header, data, data_end, name,
9130Sstevel@tonic-gate 	    name + sizeof (name));
9140Sstevel@tonic-gate 
9150Sstevel@tonic-gate 	(void) sprintf(line, "%s", name);
9160Sstevel@tonic-gate 	return (new_data - data);
9170Sstevel@tonic-gate }
918