xref: /titanic_41/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c (revision 4b22b9337f359bfd063322244f5336cc7c6ffcfa)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5f7a9f694Ssm26363  * Common Development and Distribution License (the "License").
6f7a9f694Ssm26363  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22f7a9f694Ssm26363  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <stdio.h>
297c478bd9Sstevel@tonic-gate #include <string.h>
307c478bd9Sstevel@tonic-gate #include <limits.h>
317c478bd9Sstevel@tonic-gate #include <sys/types.h>
327c478bd9Sstevel@tonic-gate #include <sys/errno.h>
337c478bd9Sstevel@tonic-gate #include <sys/tiuser.h>
347c478bd9Sstevel@tonic-gate #include <arpa/nameser.h>
357c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
367c478bd9Sstevel@tonic-gate #include <netinet/in.h>
377c478bd9Sstevel@tonic-gate #include "snoop.h"
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate /* The string used to indent detail lines */
407c478bd9Sstevel@tonic-gate #define	DNS_INDENT	"    "
417c478bd9Sstevel@tonic-gate /*
427c478bd9Sstevel@tonic-gate  * From RFC1035, the maximum size of a character-string is limited by the
437c478bd9Sstevel@tonic-gate  * one octet length field.  We add one character to that to make sure the
447c478bd9Sstevel@tonic-gate  * result is terminated.
457c478bd9Sstevel@tonic-gate  */
467c478bd9Sstevel@tonic-gate #define	MAX_CHAR_STRING_SIZE	UCHAR_MAX + 1
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate /* private functions */
497c478bd9Sstevel@tonic-gate static char *dns_opcode_string(uint_t opcode);
507c478bd9Sstevel@tonic-gate static char *dns_rcode_string(uint_t rcode);
517c478bd9Sstevel@tonic-gate static char *dns_type_string(uint_t type, int detail);
527c478bd9Sstevel@tonic-gate static char *dns_class_string(uint_t cls, int detail);
537c478bd9Sstevel@tonic-gate static size_t skip_question(const uchar_t *header, const uchar_t *data,
547c478bd9Sstevel@tonic-gate     const uchar_t *data_end);
557c478bd9Sstevel@tonic-gate static size_t print_question(char *line, const uchar_t *header,
567c478bd9Sstevel@tonic-gate     const uchar_t *data, const uchar_t *data_end, int detail);
577c478bd9Sstevel@tonic-gate static size_t print_answer(char *line, const uchar_t *header,
587c478bd9Sstevel@tonic-gate     const uchar_t *data, const uchar_t *data_end, int detail);
597c478bd9Sstevel@tonic-gate static char *binary_string(char data);
607c478bd9Sstevel@tonic-gate static void print_ip(int af, char *line, const uchar_t *data, uint16_t len);
617c478bd9Sstevel@tonic-gate static const uchar_t *get_char_string(const uchar_t *data, char *charbuf,
627c478bd9Sstevel@tonic-gate     uint16_t datalen);
637c478bd9Sstevel@tonic-gate static size_t print_char_string(char *line, const uchar_t *data, uint16_t len);
647c478bd9Sstevel@tonic-gate static const uchar_t *get_domain_name(const uchar_t *header,
657c478bd9Sstevel@tonic-gate     const uchar_t *data, const uchar_t *data_end, char *namebuf, char *namend);
667c478bd9Sstevel@tonic-gate static size_t print_domain_name(char *line, const uchar_t *header,
677c478bd9Sstevel@tonic-gate     const uchar_t *data, const uchar_t *data_end);
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate void
interpret_dns(int flags,int proto,const uchar_t * data,int len,int port)70*4b22b933Srs200217 interpret_dns(int flags, int proto, const uchar_t *data, int len, int port)
717c478bd9Sstevel@tonic-gate {
727c478bd9Sstevel@tonic-gate 	typedef HEADER dns_header;
737c478bd9Sstevel@tonic-gate 	dns_header header;
747c478bd9Sstevel@tonic-gate 	char *line;
757c478bd9Sstevel@tonic-gate 	ushort_t id, qdcount, ancount, nscount, arcount;
767c478bd9Sstevel@tonic-gate 	ushort_t count;
77f7a9f694Ssm26363 	const uchar_t *rrp;	/* Resource Record Pointer. */
787c478bd9Sstevel@tonic-gate 	const uchar_t *data_end;
79*4b22b933Srs200217 	const char *protostr;
80*4b22b933Srs200217 	char *protopfxstr;
81*4b22b933Srs200217 	char *protohdrstr;
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate 	if (proto == IPPROTO_TCP) {
847c478bd9Sstevel@tonic-gate 		/* not supported now */
857c478bd9Sstevel@tonic-gate 		return;
867c478bd9Sstevel@tonic-gate 	}
877c478bd9Sstevel@tonic-gate 
88*4b22b933Srs200217 	if (port == IPPORT_DOMAIN) {
89*4b22b933Srs200217 		protostr = "DNS";
90*4b22b933Srs200217 		protopfxstr = "DNS:  ";
91*4b22b933Srs200217 		protohdrstr = "DNS Header";
92*4b22b933Srs200217 	} else {
93*4b22b933Srs200217 		protostr = "MDNS";
94*4b22b933Srs200217 		protopfxstr = "MDNS:  ";
95*4b22b933Srs200217 		protohdrstr = "MDNS Header";
96*4b22b933Srs200217 	}
97*4b22b933Srs200217 
987c478bd9Sstevel@tonic-gate 	/* We need at least the header in order to parse a packet. */
997c478bd9Sstevel@tonic-gate 	if (sizeof (dns_header) > len) {
1007c478bd9Sstevel@tonic-gate 		return;
1017c478bd9Sstevel@tonic-gate 	}
1027c478bd9Sstevel@tonic-gate 	data_end = data + len;
1037c478bd9Sstevel@tonic-gate 	/*
1047c478bd9Sstevel@tonic-gate 	 * Copy the header into a local structure for aligned access to
1057c478bd9Sstevel@tonic-gate 	 * each field.
1067c478bd9Sstevel@tonic-gate 	 */
1077c478bd9Sstevel@tonic-gate 	(void) memcpy(&header, data, sizeof (header));
1087c478bd9Sstevel@tonic-gate 	id = ntohs(header.id);
1097c478bd9Sstevel@tonic-gate 	qdcount = ntohs(header.qdcount);
1107c478bd9Sstevel@tonic-gate 	ancount = ntohs(header.ancount);
1117c478bd9Sstevel@tonic-gate 	nscount = ntohs(header.nscount);
1127c478bd9Sstevel@tonic-gate 	arcount = ntohs(header.arcount);
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate 	if (flags & F_SUM) {
1157c478bd9Sstevel@tonic-gate 		line = get_sum_line();
116*4b22b933Srs200217 		line += sprintf(line, "%s %c ",
117*4b22b933Srs200217 		    protostr, header.qr ? 'R' : 'C');
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 		if (header.qr) {
1207c478bd9Sstevel@tonic-gate 			/* answer */
1217c478bd9Sstevel@tonic-gate 			if (header.rcode == 0) {
1227c478bd9Sstevel@tonic-gate 				/* reply is OK */
123f7a9f694Ssm26363 				rrp = data + sizeof (dns_header);
1247c478bd9Sstevel@tonic-gate 				while (qdcount--) {
125f7a9f694Ssm26363 					if (rrp >= data_end) {
1267c478bd9Sstevel@tonic-gate 						return;
1277c478bd9Sstevel@tonic-gate 					}
128f7a9f694Ssm26363 					rrp += skip_question(data,
129f7a9f694Ssm26363 					    rrp, data_end);
1307c478bd9Sstevel@tonic-gate 				}
131f7a9f694Ssm26363 				/* the answers follow the questions */
1327c478bd9Sstevel@tonic-gate 				if (ancount > 0) {
1337c478bd9Sstevel@tonic-gate 					(void) print_answer(line,
134f7a9f694Ssm26363 					    data, rrp, data_end, FALSE);
1357c478bd9Sstevel@tonic-gate 				}
1367c478bd9Sstevel@tonic-gate 			} else {
1377c478bd9Sstevel@tonic-gate 				(void) sprintf(line, " Error: %d(%s)",
1387c478bd9Sstevel@tonic-gate 				    header.rcode,
1397c478bd9Sstevel@tonic-gate 				    dns_rcode_string(header.rcode));
1407c478bd9Sstevel@tonic-gate 			}
1417c478bd9Sstevel@tonic-gate 		} else {
1427c478bd9Sstevel@tonic-gate 			/* question */
143f7a9f694Ssm26363 			rrp = data + sizeof (dns_header);
144f7a9f694Ssm26363 			if (rrp >= data_end) {
1457c478bd9Sstevel@tonic-gate 				return;
1467c478bd9Sstevel@tonic-gate 			}
147f7a9f694Ssm26363 			(void) print_question(line, data, rrp, data_end,
1487c478bd9Sstevel@tonic-gate 			    FALSE);
1497c478bd9Sstevel@tonic-gate 		}
1507c478bd9Sstevel@tonic-gate 	}
1517c478bd9Sstevel@tonic-gate 	if (flags & F_DTAIL) {
152*4b22b933Srs200217 		show_header(protopfxstr, protohdrstr, sizeof (dns_header));
1537c478bd9Sstevel@tonic-gate 		show_space();
1547c478bd9Sstevel@tonic-gate 		if (header.qr) {
1557c478bd9Sstevel@tonic-gate 			/* answer */
1567c478bd9Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1577c478bd9Sstevel@tonic-gate 			    "Response ID = %d", id);
1587c478bd9Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1597c478bd9Sstevel@tonic-gate 			    "%s%s%s",
1607c478bd9Sstevel@tonic-gate 			    header.aa ? "AA (Authoritative Answer) " : "",
1617c478bd9Sstevel@tonic-gate 			    header.tc ? "TC (TrunCation) " : "",
1627c478bd9Sstevel@tonic-gate 			    header.ra ? "RA (Recursion Available) ": "");
1637c478bd9Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1647c478bd9Sstevel@tonic-gate 			    "Response Code: %d (%s)",
1657c478bd9Sstevel@tonic-gate 			    header.rcode, dns_rcode_string(header.rcode));
1667c478bd9Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1677c478bd9Sstevel@tonic-gate 			    "Reply to %d question(s)", qdcount);
1687c478bd9Sstevel@tonic-gate 		} else {
1697c478bd9Sstevel@tonic-gate 			/* question */
1707c478bd9Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1717c478bd9Sstevel@tonic-gate 			    "Query ID = %d", id);
1727c478bd9Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1737c478bd9Sstevel@tonic-gate 			    "Opcode: %s", dns_opcode_string(header.opcode));
1747c478bd9Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1757c478bd9Sstevel@tonic-gate 			    "%s%s",
1767c478bd9Sstevel@tonic-gate 			    header.tc ? "TC (TrunCation) " : "",
1777c478bd9Sstevel@tonic-gate 			    header.rd ? "RD (Recursion Desired) " : "");
1787c478bd9Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
1797c478bd9Sstevel@tonic-gate 			    "%d question(s)", qdcount);
180f7a9f694Ssm26363 		}
181f7a9f694Ssm26363 		rrp = data + sizeof (dns_header);
1827c478bd9Sstevel@tonic-gate 		count = 0;
183f7a9f694Ssm26363 		while (qdcount--) {
184f7a9f694Ssm26363 			if (rrp >= data_end) {
185f7a9f694Ssm26363 				return;
186f7a9f694Ssm26363 			}
1877c478bd9Sstevel@tonic-gate 			count++;
188f7a9f694Ssm26363 			rrp += print_question(get_line(0, 0),
189f7a9f694Ssm26363 			    data, rrp, data_end, TRUE);
190f7a9f694Ssm26363 			show_space();
191f7a9f694Ssm26363 		}
192f7a9f694Ssm26363 		/* Only answers should hold answers, but just in case */
193f7a9f694Ssm26363 		if (header.qr || ancount > 0) {
194f7a9f694Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
195f7a9f694Ssm26363 			    "%d answer(s)", ancount);
196f7a9f694Ssm26363 			count = 0;
197f7a9f694Ssm26363 			while (ancount--) {
198f7a9f694Ssm26363 				if (rrp >= data_end) {
199f7a9f694Ssm26363 					return;
200f7a9f694Ssm26363 				}
201f7a9f694Ssm26363 				count++;
202f7a9f694Ssm26363 				rrp += print_answer(get_line(0, 0),
203f7a9f694Ssm26363 				    data, rrp, data_end, TRUE);
204f7a9f694Ssm26363 				show_space();
205f7a9f694Ssm26363 			}
206f7a9f694Ssm26363 		}
207f7a9f694Ssm26363 		/* Likewise only answers should hold NS records */
208f7a9f694Ssm26363 		if (header.qr || nscount > 0) {
209f7a9f694Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
210f7a9f694Ssm26363 			    "%d name server resource(s)", nscount);
211f7a9f694Ssm26363 			count = 0;
212f7a9f694Ssm26363 			while (nscount--) {
213f7a9f694Ssm26363 				if (rrp >= data_end) {
214f7a9f694Ssm26363 					return;
215f7a9f694Ssm26363 				}
216f7a9f694Ssm26363 				count++;
217f7a9f694Ssm26363 				rrp += print_answer(get_line(0, 0), data,
218f7a9f694Ssm26363 				    rrp, data_end, TRUE);
219f7a9f694Ssm26363 				show_space();
220f7a9f694Ssm26363 			}
221f7a9f694Ssm26363 		}
222f7a9f694Ssm26363 		/* Additional section may hold an EDNS0 record. */
223f7a9f694Ssm26363 		if (header.qr || arcount > 0) {
224f7a9f694Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
225f7a9f694Ssm26363 			    "%d additional record(s)", arcount);
226f7a9f694Ssm26363 			count = 0;
227f7a9f694Ssm26363 			while (arcount-- && rrp < data_end) {
228f7a9f694Ssm26363 				count++;
229f7a9f694Ssm26363 				rrp += print_answer(get_line(0, 0), data,
230f7a9f694Ssm26363 				    rrp, data_end, TRUE);
2317c478bd9Sstevel@tonic-gate 				show_space();
2327c478bd9Sstevel@tonic-gate 			}
2337c478bd9Sstevel@tonic-gate 		}
2347c478bd9Sstevel@tonic-gate 	}
2357c478bd9Sstevel@tonic-gate }
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate static char *
dns_opcode_string(uint_t opcode)2397c478bd9Sstevel@tonic-gate dns_opcode_string(uint_t opcode)
2407c478bd9Sstevel@tonic-gate {
2417c478bd9Sstevel@tonic-gate 	static char buffer[64];
2427c478bd9Sstevel@tonic-gate 	switch (opcode) {
2437c478bd9Sstevel@tonic-gate 	case ns_o_query:	return ("Query");
2447c478bd9Sstevel@tonic-gate 	case ns_o_iquery:	return ("Inverse Query");
2457c478bd9Sstevel@tonic-gate 	case ns_o_status:	return ("Status");
2467c478bd9Sstevel@tonic-gate 	default:
2477c478bd9Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)",
2487c478bd9Sstevel@tonic-gate 		    opcode);
2497c478bd9Sstevel@tonic-gate 		return (buffer);
2507c478bd9Sstevel@tonic-gate 	}
2517c478bd9Sstevel@tonic-gate }
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate static char *
dns_rcode_string(uint_t rcode)2547c478bd9Sstevel@tonic-gate dns_rcode_string(uint_t rcode)
2557c478bd9Sstevel@tonic-gate {
2567c478bd9Sstevel@tonic-gate 	static char buffer[64];
2577c478bd9Sstevel@tonic-gate 	switch (rcode) {
2587c478bd9Sstevel@tonic-gate 	case ns_r_noerror:	return ("OK");
2597c478bd9Sstevel@tonic-gate 	case ns_r_formerr:	return ("Format Error");
2607c478bd9Sstevel@tonic-gate 	case ns_r_servfail:	return ("Server Fail");
2617c478bd9Sstevel@tonic-gate 	case ns_r_nxdomain:	return ("Name Error");
2627c478bd9Sstevel@tonic-gate 	case ns_r_notimpl:	return ("Unimplemented");
2637c478bd9Sstevel@tonic-gate 	case ns_r_refused:	return ("Refused");
264f7a9f694Ssm26363 	case ns_r_badvers:	return ("Bad Version"); /* EDNS rcode */
2657c478bd9Sstevel@tonic-gate 	default:
2667c478bd9Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", rcode);
2677c478bd9Sstevel@tonic-gate 		return (buffer);
2687c478bd9Sstevel@tonic-gate 	}
2697c478bd9Sstevel@tonic-gate }
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate static char *
dns_type_string(uint_t type,int detail)2727c478bd9Sstevel@tonic-gate dns_type_string(uint_t type, int detail)
2737c478bd9Sstevel@tonic-gate {
2747c478bd9Sstevel@tonic-gate 	static char buffer[64];
2757c478bd9Sstevel@tonic-gate 	switch (type) {
2767c478bd9Sstevel@tonic-gate 	case ns_t_a:	return (detail ? "Address" : "Addr");
2777c478bd9Sstevel@tonic-gate 	case ns_t_ns:	return (detail ? "Authoritative Name Server" : "NS");
2787c478bd9Sstevel@tonic-gate 	case ns_t_cname:	return (detail ? "Canonical Name" : "CNAME");
2797c478bd9Sstevel@tonic-gate 	case ns_t_soa:	return (detail ? "Start Of a zone Authority" : "SOA");
2807c478bd9Sstevel@tonic-gate 	case ns_t_mb:	return (detail ? "Mailbox domain name" : "MB");
2817c478bd9Sstevel@tonic-gate 	case ns_t_mg:	return (detail ? "Mailbox Group member" : "MG");
2827c478bd9Sstevel@tonic-gate 	case ns_t_mr:	return (detail ? "Mail Rename domain name" : "MR");
2837c478bd9Sstevel@tonic-gate 	case ns_t_null:	return ("NULL");
2847c478bd9Sstevel@tonic-gate 	case ns_t_wks:	return (detail ? "Well Known Service" : "WKS");
2857c478bd9Sstevel@tonic-gate 	case ns_t_ptr:	return (detail ? "Domain Name Pointer" : "PTR");
2867c478bd9Sstevel@tonic-gate 	case ns_t_hinfo:	return (detail ? "Host Information": "HINFO");
2877c478bd9Sstevel@tonic-gate 	case ns_t_minfo:
2887c478bd9Sstevel@tonic-gate 		return (detail ? "Mailbox or maillist Info" : "MINFO");
2897c478bd9Sstevel@tonic-gate 	case ns_t_mx:	return (detail ? "Mail Exchange" : "MX");
2907c478bd9Sstevel@tonic-gate 	case ns_t_txt:	return (detail ? "Text strings" : "TXT");
2917c478bd9Sstevel@tonic-gate 	case ns_t_aaaa:	return (detail ? "IPv6 Address" : "AAAA");
292f7a9f694Ssm26363 	case ns_t_opt:	return (detail ? "EDNS0 option" : "OPT");
2937c478bd9Sstevel@tonic-gate 	case ns_t_axfr:	return (detail ? "Transfer of entire zone" : "AXFR");
2947c478bd9Sstevel@tonic-gate 	case ns_t_mailb:
2957c478bd9Sstevel@tonic-gate 		return (detail ? "Mailbox related records" : "MAILB");
2967c478bd9Sstevel@tonic-gate 	case ns_t_maila:	return (detail ? "Mail agent RRs" : "MAILA");
2977c478bd9Sstevel@tonic-gate 	case ns_t_any:	return (detail ? "All records" : "*");
2987c478bd9Sstevel@tonic-gate 	default:
2997c478bd9Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", type);
3007c478bd9Sstevel@tonic-gate 		return (buffer);
3017c478bd9Sstevel@tonic-gate 	}
3027c478bd9Sstevel@tonic-gate }
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate static char *
dns_class_string(uint_t cls,int detail)3057c478bd9Sstevel@tonic-gate dns_class_string(uint_t cls, int detail)
3067c478bd9Sstevel@tonic-gate {
3077c478bd9Sstevel@tonic-gate 	static char buffer[64];
3087c478bd9Sstevel@tonic-gate 	switch (cls) {
3097c478bd9Sstevel@tonic-gate 	case ns_c_in:		return (detail ? "Internet" : "Internet");
3107c478bd9Sstevel@tonic-gate 	case ns_c_chaos: 	return (detail ? "CHAOS" : "CH");
3117c478bd9Sstevel@tonic-gate 	case ns_c_hs:		return (detail ? "Hesiod" : "HS");
3127c478bd9Sstevel@tonic-gate 	case ns_c_any:		return (detail ? "* (Any class)" : "*");
3137c478bd9Sstevel@tonic-gate 	default:
3147c478bd9Sstevel@tonic-gate 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", cls);
3157c478bd9Sstevel@tonic-gate 		return (buffer);
3167c478bd9Sstevel@tonic-gate 	}
3177c478bd9Sstevel@tonic-gate }
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate static size_t
skip_question(const uchar_t * header,const uchar_t * data,const uchar_t * data_end)3207c478bd9Sstevel@tonic-gate skip_question(const uchar_t *header, const uchar_t *data,
3217c478bd9Sstevel@tonic-gate     const uchar_t *data_end)
3227c478bd9Sstevel@tonic-gate {
3237c478bd9Sstevel@tonic-gate 	const uchar_t *data_bak = data;
3247c478bd9Sstevel@tonic-gate 	char dummy_buffer[NS_MAXDNAME];
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	data = get_domain_name(header, data, data_end, dummy_buffer,
3277c478bd9Sstevel@tonic-gate 	    dummy_buffer + sizeof (dummy_buffer));
3287c478bd9Sstevel@tonic-gate 	/* Skip the 32 bits of class and type that follow the domain name */
3297c478bd9Sstevel@tonic-gate 	data += sizeof (uint32_t);
3307c478bd9Sstevel@tonic-gate 	return (data - data_bak);
3317c478bd9Sstevel@tonic-gate }
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate static size_t
print_question(char * line,const uchar_t * header,const uchar_t * data,const uchar_t * data_end,int detail)3347c478bd9Sstevel@tonic-gate print_question(char *line, const uchar_t *header, const uchar_t *data,
3357c478bd9Sstevel@tonic-gate     const uchar_t *data_end, int detail)
3367c478bd9Sstevel@tonic-gate {
3377c478bd9Sstevel@tonic-gate 	const uchar_t *data_bak = data;
3387c478bd9Sstevel@tonic-gate 	uint16_t type;
3397c478bd9Sstevel@tonic-gate 	uint16_t cls;
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	if (detail) {
3427c478bd9Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
3437c478bd9Sstevel@tonic-gate 		    DNS_INDENT "Domain Name: ");
3447c478bd9Sstevel@tonic-gate 	}
3457c478bd9Sstevel@tonic-gate 	data += print_domain_name(line, header, data, data_end);
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	/*
3487c478bd9Sstevel@tonic-gate 	 * Make sure we don't run off the end of the packet by reading the
3497c478bd9Sstevel@tonic-gate 	 * type and class.
3507c478bd9Sstevel@tonic-gate 	 *
3517c478bd9Sstevel@tonic-gate 	 * The pointer subtraction on the left side of the following
3527c478bd9Sstevel@tonic-gate 	 * expression has a signed result of type ptrdiff_t, and the right
3537c478bd9Sstevel@tonic-gate 	 * side has an unsigned result of type size_t.  We therefore need
3547c478bd9Sstevel@tonic-gate 	 * to cast the right side of the expression to be of the same
3557c478bd9Sstevel@tonic-gate 	 * signed type to keep the result of the pointer arithmetic to be
3567c478bd9Sstevel@tonic-gate 	 * automatically cast to an unsigned value.  We do a similar cast
3577c478bd9Sstevel@tonic-gate 	 * in other similar expressions throughout this file.
3587c478bd9Sstevel@tonic-gate 	 */
3597c478bd9Sstevel@tonic-gate 	if ((data_end - data) < (ptrdiff_t)(2 * sizeof (uint16_t)))
3607c478bd9Sstevel@tonic-gate 		return (data_end - data_bak);
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 	GETINT16(type, data);
3637c478bd9Sstevel@tonic-gate 	GETINT16(cls, data);
3647c478bd9Sstevel@tonic-gate 
365*4b22b933Srs200217 	/*
366*4b22b933Srs200217 	 * Multicast DNS re-uses the top bit of the class field
367*4b22b933Srs200217 	 * in the question and answer sections. Unicast DNS only
368*4b22b933Srs200217 	 * uses 1 (Internet), 3 and 4. Hence it is safe. The top
369*4b22b933Srs200217 	 * order bit is always cleared here to display the rrclass in case
370*4b22b933Srs200217 	 * of Multicast DNS packets.
371*4b22b933Srs200217 	 */
372*4b22b933Srs200217 	cls = cls & 0x7fff;
373*4b22b933Srs200217 
3747c478bd9Sstevel@tonic-gate 	if (detail) {
3757c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
3767c478bd9Sstevel@tonic-gate 		    DNS_INDENT "Class: %u (%s)",
3777c478bd9Sstevel@tonic-gate 		    cls, dns_class_string(cls, detail));
3787c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
3797c478bd9Sstevel@tonic-gate 		    DNS_INDENT "Type:  %u (%s)", type,
3807c478bd9Sstevel@tonic-gate 		    dns_type_string(type, detail));
3817c478bd9Sstevel@tonic-gate 	} else {
3827c478bd9Sstevel@tonic-gate 		(void) sprintf(line + strlen(line), " %s %s \?",
3837c478bd9Sstevel@tonic-gate 		    dns_class_string(cls, detail),
3847c478bd9Sstevel@tonic-gate 		    dns_type_string(type, detail));
3857c478bd9Sstevel@tonic-gate 	}
3867c478bd9Sstevel@tonic-gate 	return (data - data_bak);
3877c478bd9Sstevel@tonic-gate }
3887c478bd9Sstevel@tonic-gate 
389f7a9f694Ssm26363 /*
390f7a9f694Ssm26363  * print_answer() is used to display the contents of a single resource
391f7a9f694Ssm26363  * record (RR) from either the answer, name server or additional
392f7a9f694Ssm26363  * section of the DNS packet.
393f7a9f694Ssm26363  *
394f7a9f694Ssm26363  * Input:
395f7a9f694Ssm26363  *	*line: snoops output buffer.
396f7a9f694Ssm26363  *	*header: start of the DNS packet, required for names and rcode.
397f7a9f694Ssm26363  *	*data: location within header from where the RR starts.
398f7a9f694Ssm26363  * 	*data_end: where DNS data ends.
399f7a9f694Ssm26363  * 	detail: simple or verbose output.
400f7a9f694Ssm26363  *
401f7a9f694Ssm26363  * Returns:
402f7a9f694Ssm26363  *	Pointer to next RR or data_end.
403f7a9f694Ssm26363  *
404f7a9f694Ssm26363  * Most RRs have the same top level format as defined in RFC 1035:
405f7a9f694Ssm26363  *
406f7a9f694Ssm26363  *                                     1  1  1  1  1  1
407f7a9f694Ssm26363  *       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
408f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
409f7a9f694Ssm26363  *    |                                               |
410f7a9f694Ssm26363  *    /                      NAME                     /
411f7a9f694Ssm26363  *    |                                               |
412f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
413f7a9f694Ssm26363  *    |                      TYPE                     |
414f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
415f7a9f694Ssm26363  *    |                     CLASS                     |
416f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
417f7a9f694Ssm26363  *    |                      TTL                      |
418f7a9f694Ssm26363  *    |                                               |
419f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
420f7a9f694Ssm26363  *    |                   RDLENGTH                    |
421f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
422f7a9f694Ssm26363  *    /                     RDATA                     /
423f7a9f694Ssm26363  *    /                                               /
424f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
425f7a9f694Ssm26363  *
426f7a9f694Ssm26363  * However RFC 2671 introduced an exception to this rule
427f7a9f694Ssm26363  * with the "Extension Mechanisms for DNS" (EDNS0).
428f7a9f694Ssm26363  * When the type is 41 the remaining resource record format
429f7a9f694Ssm26363  * is:
430f7a9f694Ssm26363  *
431f7a9f694Ssm26363  *                                     1  1  1  1  1  1
432f7a9f694Ssm26363  *       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
433f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
434f7a9f694Ssm26363  *    |                    TYPE = 41                  |
435f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
436f7a9f694Ssm26363  *    |           Sender's UDP payload size           |
437f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
438f7a9f694Ssm26363  *    |    Extended-rcode     |        Version        |
439f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
440f7a9f694Ssm26363  *    |                      Zero                     |
441f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
442f7a9f694Ssm26363  *    |                   RDLENGTH                    |
443f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
444f7a9f694Ssm26363  *    /                     RDATA                     /
445f7a9f694Ssm26363  *    /                                               /
446f7a9f694Ssm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
447f7a9f694Ssm26363  *
448f7a9f694Ssm26363  */
4497c478bd9Sstevel@tonic-gate static size_t
print_answer(char * line,const uchar_t * header,const uchar_t * data,const uchar_t * data_end,int detail)4507c478bd9Sstevel@tonic-gate print_answer(char *line, const uchar_t *header, const uchar_t *data,
4517c478bd9Sstevel@tonic-gate     const uchar_t *data_end, int detail)
4527c478bd9Sstevel@tonic-gate {
4537c478bd9Sstevel@tonic-gate 	const uchar_t *data_bak = data;
4547c478bd9Sstevel@tonic-gate 	const uchar_t *data_next;
4557c478bd9Sstevel@tonic-gate 	uint16_t type;
4567c478bd9Sstevel@tonic-gate 	uint16_t cls;
4577c478bd9Sstevel@tonic-gate 	int32_t ttl;
4587c478bd9Sstevel@tonic-gate 	uint16_t rdlen;
4597c478bd9Sstevel@tonic-gate 	uint32_t serial, refresh, retry, expire, minimum;
4607c478bd9Sstevel@tonic-gate 	uint8_t protocol;
4617c478bd9Sstevel@tonic-gate 	int linepos;
4627c478bd9Sstevel@tonic-gate 	uint16_t preference;
463f7a9f694Ssm26363 	/* declarations for EDNS follow */
464f7a9f694Ssm26363 	uint16_t size;	/* Sender's UDP payload size */
465f7a9f694Ssm26363 	uint8_t xrcode;	/* Extended-rcode */
466f7a9f694Ssm26363 	uint8_t ver;	/* Version */
467f7a9f694Ssm26363 	uint16_t rcode;	/* Extracted from the DNS header */
468f7a9f694Ssm26363 	union {		/* DNS header overlay used for extraction */
469f7a9f694Ssm26363 		HEADER		*head;
470f7a9f694Ssm26363 		const uchar_t	*raw;
471f7a9f694Ssm26363 	} headptr;
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	if (detail) {
4747c478bd9Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
4757c478bd9Sstevel@tonic-gate 		    DNS_INDENT "Domain Name: ");
4767c478bd9Sstevel@tonic-gate 	}
4777c478bd9Sstevel@tonic-gate 	data += print_domain_name(line, header, data, data_end);
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 	/*
480f7a9f694Ssm26363 	 * Next, get the record type, being careful to make sure we
481f7a9f694Ssm26363 	 * don't run off the end of the packet.
4827c478bd9Sstevel@tonic-gate 	 */
483f7a9f694Ssm26363 	if ((data_end - data) < (ptrdiff_t)(sizeof (type))) {
4847c478bd9Sstevel@tonic-gate 		return (data_end - data_bak);
4857c478bd9Sstevel@tonic-gate 	}
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	GETINT16(type, data);
488f7a9f694Ssm26363 
489f7a9f694Ssm26363 	if (type == ns_t_opt) {
490f7a9f694Ssm26363 		/*
491f7a9f694Ssm26363 		 * Make sure we won't run off the end reading size,
492f7a9f694Ssm26363 		 * xrcode, version, zero and rdlen.
493f7a9f694Ssm26363 		 */
494f7a9f694Ssm26363 		if ((data_end - data) <
495f7a9f694Ssm26363 		    ((ptrdiff_t)(sizeof (size)
496f7a9f694Ssm26363 		    + sizeof (xrcode)
497f7a9f694Ssm26363 		    + sizeof (ver)
498f7a9f694Ssm26363 		    + sizeof (cls)	/* zero */
499f7a9f694Ssm26363 		    + sizeof (rdlen)))) {
500f7a9f694Ssm26363 			return (data_end - data_bak);
501f7a9f694Ssm26363 		}
502f7a9f694Ssm26363 
503f7a9f694Ssm26363 		GETINT16(size, data);
504f7a9f694Ssm26363 		GETINT8(xrcode, data);
505f7a9f694Ssm26363 		/*
506f7a9f694Ssm26363 		 * The extended rcode represents the top half of the
507f7a9f694Ssm26363 		 * rcode which must be added to the rcode in the header.
508f7a9f694Ssm26363 		 */
509f7a9f694Ssm26363 		rcode = 0xff & (xrcode << 4);
510f7a9f694Ssm26363 		headptr.raw = header;		/* Overlay the header... */
511f7a9f694Ssm26363 		rcode += headptr.head->rcode;	/* And pluck out the rcode. */
512f7a9f694Ssm26363 
513f7a9f694Ssm26363 		GETINT8(ver, data);
514f7a9f694Ssm26363 		GETINT16(cls, data); /* zero */
515f7a9f694Ssm26363 		GETINT16(rdlen, data);
516f7a9f694Ssm26363 
517f7a9f694Ssm26363 		if (detail) {
518f7a9f694Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
519f7a9f694Ssm26363 			    DNS_INDENT "Type:  %u (%s)", type,
520f7a9f694Ssm26363 			    dns_type_string(type, detail));
521f7a9f694Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
522f7a9f694Ssm26363 			    DNS_INDENT "UDP payload size: %u (0x%.4x)",
523f7a9f694Ssm26363 			    size, size);
524f7a9f694Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
525f7a9f694Ssm26363 			    DNS_INDENT "Extended rcode: %u "
526f7a9f694Ssm26363 			    "(translates to %u (%s))",
527f7a9f694Ssm26363 			    xrcode, rcode, dns_rcode_string(rcode));
528f7a9f694Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
529f7a9f694Ssm26363 			    DNS_INDENT "EDNS0 Version: %u", ver);
530f7a9f694Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
531f7a9f694Ssm26363 			    DNS_INDENT "zero: %u", cls);
532f7a9f694Ssm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
533f7a9f694Ssm26363 			    DNS_INDENT "Data length: %u", rdlen);
534f7a9f694Ssm26363 		} else {
535f7a9f694Ssm26363 			line += strlen(line);
536f7a9f694Ssm26363 			line += sprintf(line, " %s UDP %u rc %d ver %u len %u",
537f7a9f694Ssm26363 			    dns_type_string(type, detail), size, rcode, ver,
538f7a9f694Ssm26363 			    rdlen);
539f7a9f694Ssm26363 		}
540f7a9f694Ssm26363 
541f7a9f694Ssm26363 		/*
542f7a9f694Ssm26363 		 * Make sure that rdlen is within data boundary.
543f7a9f694Ssm26363 		 */
544f7a9f694Ssm26363 		if (rdlen > data_end - data)
545f7a9f694Ssm26363 			return (data_end - data_bak);
546f7a9f694Ssm26363 
547f7a9f694Ssm26363 		/* Future OPT decode code goes here. */
548f7a9f694Ssm26363 
549f7a9f694Ssm26363 		data += rdlen;
550f7a9f694Ssm26363 		return (data - data_bak);
551f7a9f694Ssm26363 	}
552f7a9f694Ssm26363 
553f7a9f694Ssm26363 	/*
554f7a9f694Ssm26363 	 * Make sure we don't run off the end of the packet by reading the
555f7a9f694Ssm26363 	 * class, ttl, and length.
556f7a9f694Ssm26363 	 */
557f7a9f694Ssm26363 	if ((data_end - data) <
558f7a9f694Ssm26363 	    ((ptrdiff_t)(sizeof (cls)
559f7a9f694Ssm26363 	    + sizeof (ttl)
560f7a9f694Ssm26363 	    + sizeof (rdlen)))) {
561f7a9f694Ssm26363 		return (data_end - data_bak);
562f7a9f694Ssm26363 	}
563f7a9f694Ssm26363 
5647c478bd9Sstevel@tonic-gate 	GETINT16(cls, data);
5657c478bd9Sstevel@tonic-gate 
566*4b22b933Srs200217 	/*
567*4b22b933Srs200217 	 * Multicast DNS re-uses the top bit of the class field
568*4b22b933Srs200217 	 * in the question and answer sections. Unicast DNS only
569*4b22b933Srs200217 	 * uses 1 (Internet), 3 and 4. Hence it is safe. The top
570*4b22b933Srs200217 	 * order bit is always cleared here to display the rrclass in case
571*4b22b933Srs200217 	 * of Multicast DNS packets.
572*4b22b933Srs200217 	 */
573*4b22b933Srs200217 	cls = cls & 0x7fff;
574*4b22b933Srs200217 
5757c478bd9Sstevel@tonic-gate 	if (detail) {
5767c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
5777c478bd9Sstevel@tonic-gate 		    DNS_INDENT "Class: %d (%s)", cls,
5787c478bd9Sstevel@tonic-gate 		    dns_class_string(cls, detail));
5797c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
5807c478bd9Sstevel@tonic-gate 		    DNS_INDENT "Type:  %d (%s)", type,
5817c478bd9Sstevel@tonic-gate 		    dns_type_string(type, detail));
5827c478bd9Sstevel@tonic-gate 	} else {
5837c478bd9Sstevel@tonic-gate 		line += strlen(line);
5847c478bd9Sstevel@tonic-gate 		line += sprintf(line, " %s %s ",
5857c478bd9Sstevel@tonic-gate 		    dns_class_string(cls, detail),
5867c478bd9Sstevel@tonic-gate 		    dns_type_string(type, detail));
5877c478bd9Sstevel@tonic-gate 	}
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	GETINT32(ttl, data);
5907c478bd9Sstevel@tonic-gate 	if (detail) {
5917c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
5927c478bd9Sstevel@tonic-gate 		    DNS_INDENT "TTL (Time To Live): %d", ttl);
5937c478bd9Sstevel@tonic-gate 	}
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	GETINT16(rdlen, data);
5967c478bd9Sstevel@tonic-gate 	if (detail) {
5977c478bd9Sstevel@tonic-gate 		line = get_line(0, 0);
5987c478bd9Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(), DNS_INDENT "%s: ",
5997c478bd9Sstevel@tonic-gate 		    dns_type_string(type, detail));
6007c478bd9Sstevel@tonic-gate 	}
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 	if (rdlen > data_end - data)
6037c478bd9Sstevel@tonic-gate 		return (data_end - data_bak);
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	switch (type) {
6067c478bd9Sstevel@tonic-gate 	case ns_t_a:
6077c478bd9Sstevel@tonic-gate 		print_ip(AF_INET, line, data, rdlen);
6087c478bd9Sstevel@tonic-gate 		break;
6097c478bd9Sstevel@tonic-gate 	case ns_t_aaaa:
6107c478bd9Sstevel@tonic-gate 		print_ip(AF_INET6, line, data, rdlen);
6117c478bd9Sstevel@tonic-gate 		break;
6127c478bd9Sstevel@tonic-gate 	case ns_t_hinfo:
6137c478bd9Sstevel@tonic-gate 		line += sprintf(line, "CPU: ");
6147c478bd9Sstevel@tonic-gate 		data_next = data + print_char_string(line, data, rdlen);
6157c478bd9Sstevel@tonic-gate 		if (data_next >= data_end)
6167c478bd9Sstevel@tonic-gate 			break;
6177c478bd9Sstevel@tonic-gate 		line += strlen(line);
6187c478bd9Sstevel@tonic-gate 		line += sprintf(line, "OS: ");
6197c478bd9Sstevel@tonic-gate 		(void) print_char_string(line, data_next,
6207c478bd9Sstevel@tonic-gate 		    rdlen - (data_next - data));
6217c478bd9Sstevel@tonic-gate 		break;
6227c478bd9Sstevel@tonic-gate 	case ns_t_ns:
6237c478bd9Sstevel@tonic-gate 	case ns_t_cname:
6247c478bd9Sstevel@tonic-gate 	case ns_t_mb:
6257c478bd9Sstevel@tonic-gate 	case ns_t_mg:
6267c478bd9Sstevel@tonic-gate 	case ns_t_mr:
6277c478bd9Sstevel@tonic-gate 	case ns_t_ptr:
6287c478bd9Sstevel@tonic-gate 		(void) print_domain_name(line, header, data, data_end);
6297c478bd9Sstevel@tonic-gate 		break;
6307c478bd9Sstevel@tonic-gate 	case ns_t_mx:
6317c478bd9Sstevel@tonic-gate 		data_next = data;
6327c478bd9Sstevel@tonic-gate 		if (rdlen < sizeof (uint16_t))
6337c478bd9Sstevel@tonic-gate 			break;
6347c478bd9Sstevel@tonic-gate 		GETINT16(preference, data_next);
6357c478bd9Sstevel@tonic-gate 		if (detail) {
6367c478bd9Sstevel@tonic-gate 			(void) print_domain_name(line, header, data_next,
6377c478bd9Sstevel@tonic-gate 			    data_end);
6387c478bd9Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
6397c478bd9Sstevel@tonic-gate 			    DNS_INDENT "Preference: %u", preference);
6407c478bd9Sstevel@tonic-gate 		} else {
6417c478bd9Sstevel@tonic-gate 			(void) print_domain_name(line, header, data_next,
6427c478bd9Sstevel@tonic-gate 			    data_end);
6437c478bd9Sstevel@tonic-gate 		}
6447c478bd9Sstevel@tonic-gate 		break;
6457c478bd9Sstevel@tonic-gate 	case ns_t_soa:
6467c478bd9Sstevel@tonic-gate 		if (!detail)
6477c478bd9Sstevel@tonic-gate 			break;
6487c478bd9Sstevel@tonic-gate 		line = get_line(0, 0);
6497c478bd9Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
6507c478bd9Sstevel@tonic-gate 		    DNS_INDENT "MNAME (Server name): ");
6517c478bd9Sstevel@tonic-gate 		data_next = data + print_domain_name(line, header, data,
6527c478bd9Sstevel@tonic-gate 		    data_end);
6537c478bd9Sstevel@tonic-gate 		if (data_next >= data_end)
6547c478bd9Sstevel@tonic-gate 			break;
6557c478bd9Sstevel@tonic-gate 		line = get_line(0, 0);
6567c478bd9Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
6577c478bd9Sstevel@tonic-gate 		    DNS_INDENT "RNAME (Resposible mailbox): ");
6587c478bd9Sstevel@tonic-gate 		data_next = data_next +
6597c478bd9Sstevel@tonic-gate 		    print_domain_name(line, header, data_next, data_end);
6607c478bd9Sstevel@tonic-gate 		if ((data_end - data_next) < (ptrdiff_t)(5 * sizeof (uint32_t)))
6617c478bd9Sstevel@tonic-gate 			break;
6627c478bd9Sstevel@tonic-gate 		GETINT32(serial, data_next);
6637c478bd9Sstevel@tonic-gate 		GETINT32(refresh, data_next);
6647c478bd9Sstevel@tonic-gate 		GETINT32(retry, data_next);
6657c478bd9Sstevel@tonic-gate 		GETINT32(expire, data_next);
6667c478bd9Sstevel@tonic-gate 		GETINT32(minimum, data_next);
6677c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
6687c478bd9Sstevel@tonic-gate 		    DNS_INDENT "Serial: %u", serial);
6697c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
6707c478bd9Sstevel@tonic-gate 		    DNS_INDENT "Refresh: %u  Retry: %u  "
6717c478bd9Sstevel@tonic-gate 		    "Expire: %u Minimum: %u",
6727c478bd9Sstevel@tonic-gate 		    refresh, retry, expire, minimum);
6737c478bd9Sstevel@tonic-gate 		break;
6747c478bd9Sstevel@tonic-gate 	case ns_t_wks:
6757c478bd9Sstevel@tonic-gate 		print_ip(AF_INET, line, data, rdlen);
6767c478bd9Sstevel@tonic-gate 		if (!detail)
6777c478bd9Sstevel@tonic-gate 			break;
6787c478bd9Sstevel@tonic-gate 		data_next = data + sizeof (in_addr_t);
6797c478bd9Sstevel@tonic-gate 		if (data_next >= data_end)
6807c478bd9Sstevel@tonic-gate 			break;
6817c478bd9Sstevel@tonic-gate 		GETINT8(protocol, data_next);
6827c478bd9Sstevel@tonic-gate 		line = get_line(0, 0);
6837c478bd9Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
6847c478bd9Sstevel@tonic-gate 		    DNS_INDENT "Protocol: %u ", protocol);
6857c478bd9Sstevel@tonic-gate 		switch (protocol) {
6867c478bd9Sstevel@tonic-gate 		case IPPROTO_UDP:
6877c478bd9Sstevel@tonic-gate 			(void) snprintf(line, get_line_remain(), "(UDP)");
6887c478bd9Sstevel@tonic-gate 			break;
6897c478bd9Sstevel@tonic-gate 		case IPPROTO_TCP:
6907c478bd9Sstevel@tonic-gate 			(void) snprintf(line, get_line_remain(), "(TCP)");
6917c478bd9Sstevel@tonic-gate 			break;
6927c478bd9Sstevel@tonic-gate 		}
6937c478bd9Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
6947c478bd9Sstevel@tonic-gate 		    DNS_INDENT "Service bitmap:");
6957c478bd9Sstevel@tonic-gate 		(void) snprintf(line, get_line_remain(),
6967c478bd9Sstevel@tonic-gate 		    DNS_INDENT "0       8       16      24");
6977c478bd9Sstevel@tonic-gate 		linepos = 4;
6987c478bd9Sstevel@tonic-gate 		while (data_next < data + rdlen) {
6997c478bd9Sstevel@tonic-gate 			if (linepos == 4) {
7007c478bd9Sstevel@tonic-gate 				line = get_line(0, 0);
7017c478bd9Sstevel@tonic-gate 				line += snprintf(line, get_line_remain(),
7027c478bd9Sstevel@tonic-gate 				    DNS_INDENT);
7037c478bd9Sstevel@tonic-gate 				linepos = 0;
7047c478bd9Sstevel@tonic-gate 			}
7057c478bd9Sstevel@tonic-gate 			line += snprintf(line, get_line_remain(), "%s",
7067c478bd9Sstevel@tonic-gate 			    binary_string(*data_next));
7077c478bd9Sstevel@tonic-gate 			linepos++;
7087c478bd9Sstevel@tonic-gate 			data_next++;
7097c478bd9Sstevel@tonic-gate 		}
7107c478bd9Sstevel@tonic-gate 		break;
7117c478bd9Sstevel@tonic-gate 	case ns_t_minfo:
7127c478bd9Sstevel@tonic-gate 		if (!detail)
7137c478bd9Sstevel@tonic-gate 			break;
7147c478bd9Sstevel@tonic-gate 		line = get_line(0, 0);
7157c478bd9Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
7167c478bd9Sstevel@tonic-gate 		    DNS_INDENT "RMAILBX (Resposible mailbox): ");
7177c478bd9Sstevel@tonic-gate 		data_next = data + print_domain_name(line, header, data,
7187c478bd9Sstevel@tonic-gate 		    data_end);
7197c478bd9Sstevel@tonic-gate 		line = get_line(0, 0);
7207c478bd9Sstevel@tonic-gate 		line += snprintf(line, get_line_remain(),
7217c478bd9Sstevel@tonic-gate 		    DNS_INDENT "EMAILBX (mailbox to receive err message): ");
7227c478bd9Sstevel@tonic-gate 		data_next = data_next + print_domain_name(line, header,
7237c478bd9Sstevel@tonic-gate 		    data_next, data_end);
7247c478bd9Sstevel@tonic-gate 		break;
7257c478bd9Sstevel@tonic-gate 	}
7267c478bd9Sstevel@tonic-gate 	data += rdlen;
7277c478bd9Sstevel@tonic-gate 	return (data - data_bak);
7287c478bd9Sstevel@tonic-gate }
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate static char *
binary_string(char data)7317c478bd9Sstevel@tonic-gate binary_string(char data)
7327c478bd9Sstevel@tonic-gate {
7337c478bd9Sstevel@tonic-gate 	static char bstring[8 + 1];
7347c478bd9Sstevel@tonic-gate 	char *ptr;
7357c478bd9Sstevel@tonic-gate 	int i;
7367c478bd9Sstevel@tonic-gate 	ptr = bstring;
7377c478bd9Sstevel@tonic-gate 	for (i = 0; i < 8; i++) {
7387c478bd9Sstevel@tonic-gate 		*ptr++ = (data & 0x80) ? '1' : '0';
7397c478bd9Sstevel@tonic-gate 		data = data << 1;
7407c478bd9Sstevel@tonic-gate 	}
7417c478bd9Sstevel@tonic-gate 	*ptr = (char)0;
7427c478bd9Sstevel@tonic-gate 	return (bstring);
7437c478bd9Sstevel@tonic-gate }
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate static void
print_ip(int af,char * line,const uchar_t * data,uint16_t len)7467c478bd9Sstevel@tonic-gate print_ip(int af, char *line, const uchar_t *data, uint16_t len)
7477c478bd9Sstevel@tonic-gate {
7487c478bd9Sstevel@tonic-gate 	in6_addr_t	addr6;
7497c478bd9Sstevel@tonic-gate 	in_addr_t	addr4;
7507c478bd9Sstevel@tonic-gate 	void		*addr;
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	switch (af) {
7537c478bd9Sstevel@tonic-gate 	case AF_INET:
7547c478bd9Sstevel@tonic-gate 		if (len != sizeof (in_addr_t))
7557c478bd9Sstevel@tonic-gate 			return;
7567c478bd9Sstevel@tonic-gate 		addr = memcpy(&addr4, data, sizeof (addr4));
7577c478bd9Sstevel@tonic-gate 		break;
7587c478bd9Sstevel@tonic-gate 	case AF_INET6:
7597c478bd9Sstevel@tonic-gate 		if (len != sizeof (in6_addr_t))
7607c478bd9Sstevel@tonic-gate 			return;
7617c478bd9Sstevel@tonic-gate 		addr = memcpy(&addr6, data, sizeof (addr6));
7627c478bd9Sstevel@tonic-gate 		break;
7637c478bd9Sstevel@tonic-gate 	}
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate 	(void) inet_ntop(af, addr, line, INET6_ADDRSTRLEN);
7667c478bd9Sstevel@tonic-gate }
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate /*
7697c478bd9Sstevel@tonic-gate  * charbuf is assumed to be of size MAX_CHAR_STRING_SIZE.
7707c478bd9Sstevel@tonic-gate  */
7717c478bd9Sstevel@tonic-gate static const uchar_t *
get_char_string(const uchar_t * data,char * charbuf,uint16_t datalen)7727c478bd9Sstevel@tonic-gate get_char_string(const uchar_t *data, char *charbuf, uint16_t datalen)
7737c478bd9Sstevel@tonic-gate {
7742e3b6467Skcpoon 	int len;
7757c478bd9Sstevel@tonic-gate 	char *name = charbuf;
7767c478bd9Sstevel@tonic-gate 	int i = 0;
7777c478bd9Sstevel@tonic-gate 
7787c478bd9Sstevel@tonic-gate 	/*
7797c478bd9Sstevel@tonic-gate 	 * From RFC1035, a character-string is a single length octet followed
7807c478bd9Sstevel@tonic-gate 	 * by that number of characters.
7817c478bd9Sstevel@tonic-gate 	 */
7827c478bd9Sstevel@tonic-gate 	if (datalen > 1) {
7837c478bd9Sstevel@tonic-gate 		len = *data;
7847c478bd9Sstevel@tonic-gate 		data++;
7857c478bd9Sstevel@tonic-gate 		if (len > 0 && len < MAX_CHAR_STRING_SIZE) {
7867c478bd9Sstevel@tonic-gate 			for (i = 0; i < len; i++, data++)
7877c478bd9Sstevel@tonic-gate 				name[i] = *data;
7887c478bd9Sstevel@tonic-gate 		}
7897c478bd9Sstevel@tonic-gate 	}
7907c478bd9Sstevel@tonic-gate 	name[i] = '\0';
7917c478bd9Sstevel@tonic-gate 	return (data);
7927c478bd9Sstevel@tonic-gate }
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate static size_t
print_char_string(char * line,const uchar_t * data,uint16_t len)7957c478bd9Sstevel@tonic-gate print_char_string(char *line, const uchar_t *data, uint16_t len)
7967c478bd9Sstevel@tonic-gate {
7977c478bd9Sstevel@tonic-gate 	char charbuf[MAX_CHAR_STRING_SIZE];
7987c478bd9Sstevel@tonic-gate 	const uchar_t *data_bak = data;
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 	data = get_char_string(data, charbuf, len);
8017c478bd9Sstevel@tonic-gate 	(void) sprintf(line, "%s", charbuf);
8027c478bd9Sstevel@tonic-gate 	return (data - data_bak);
8037c478bd9Sstevel@tonic-gate }
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate /*
8067c478bd9Sstevel@tonic-gate  * header: the entire message header, this is where we start to
8077c478bd9Sstevel@tonic-gate  *	   count the offset of the compression scheme
8087c478bd9Sstevel@tonic-gate  * data:   the start of the domain name
8097c478bd9Sstevel@tonic-gate  * namebuf: user supplied buffer
8107c478bd9Sstevel@tonic-gate  * return: the next byte after what we have parsed
8117c478bd9Sstevel@tonic-gate  */
8127c478bd9Sstevel@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)8137c478bd9Sstevel@tonic-gate get_domain_name(const uchar_t *header, const uchar_t *data,
8147c478bd9Sstevel@tonic-gate     const uchar_t *data_end, char *namebuf, char *namend)
8157c478bd9Sstevel@tonic-gate {
8167c478bd9Sstevel@tonic-gate 	uint8_t len;
8177c478bd9Sstevel@tonic-gate 	char *name = namebuf;
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate 	/*
8207c478bd9Sstevel@tonic-gate 	 * From RFC1035, a domain name is a sequence of labels, where each
8217c478bd9Sstevel@tonic-gate 	 * label consists of a length octet followed by that number of
8227c478bd9Sstevel@tonic-gate 	 * octets.  The domain name terminates with the zero length octet
8237c478bd9Sstevel@tonic-gate 	 * for the null label of the root.
8247c478bd9Sstevel@tonic-gate 	 */
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 	while (name < (namend - 1)) {
8277c478bd9Sstevel@tonic-gate 		if ((data_end - data) < (ptrdiff_t)(sizeof (uint8_t))) {
8287c478bd9Sstevel@tonic-gate 			/* The length octet is off the end of the packet. */
8297c478bd9Sstevel@tonic-gate 			break;
8307c478bd9Sstevel@tonic-gate 		}
8317c478bd9Sstevel@tonic-gate 		GETINT8(len, data);
8327c478bd9Sstevel@tonic-gate 		if (len == 0) {
8337c478bd9Sstevel@tonic-gate 			/*
8347c478bd9Sstevel@tonic-gate 			 * Domain names end with a length byte of zero,
8357c478bd9Sstevel@tonic-gate 			 * which represents the null label of the root.
8367c478bd9Sstevel@tonic-gate 			 */
8377c478bd9Sstevel@tonic-gate 			break;
8387c478bd9Sstevel@tonic-gate 		}
8397c478bd9Sstevel@tonic-gate 		/*
8407c478bd9Sstevel@tonic-gate 		 * test if we are using the compression scheme
8417c478bd9Sstevel@tonic-gate 		 */
8427c478bd9Sstevel@tonic-gate 		if ((len & 0xc0) == 0xc0) {
8437c478bd9Sstevel@tonic-gate 			uint16_t offset;
8447c478bd9Sstevel@tonic-gate 			const uchar_t *label_ptr;
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 			/*
8477c478bd9Sstevel@tonic-gate 			 * From RFC1035, message compression allows a
8487c478bd9Sstevel@tonic-gate 			 * domain name or a list of labels at the end of a
8497c478bd9Sstevel@tonic-gate 			 * domain name to be replaced with a pointer to a
8507c478bd9Sstevel@tonic-gate 			 * prior occurance of the same name.  In this
8517c478bd9Sstevel@tonic-gate 			 * scheme, the pointer is a two octet sequence
8527c478bd9Sstevel@tonic-gate 			 * where the most significant two bits are set, and
8537c478bd9Sstevel@tonic-gate 			 * the remaining 14 bits are the offset from the
8547c478bd9Sstevel@tonic-gate 			 * start of the message of the next label.
8557c478bd9Sstevel@tonic-gate 			 */
8567c478bd9Sstevel@tonic-gate 			data--;
8577c478bd9Sstevel@tonic-gate 			if ((data_end - data) <
8587c478bd9Sstevel@tonic-gate 			    (ptrdiff_t)(sizeof (uint16_t))) {
8597c478bd9Sstevel@tonic-gate 				/*
8607c478bd9Sstevel@tonic-gate 				 * The offset octets aren't entirely
8617c478bd9Sstevel@tonic-gate 				 * contained within this pakcet.
8627c478bd9Sstevel@tonic-gate 				 */
8637c478bd9Sstevel@tonic-gate 				data = data_end;
8647c478bd9Sstevel@tonic-gate 				break;
8657c478bd9Sstevel@tonic-gate 			}
8667c478bd9Sstevel@tonic-gate 			GETINT16(offset, data);
8677c478bd9Sstevel@tonic-gate 			label_ptr = header + (offset & 0x3fff);
8687c478bd9Sstevel@tonic-gate 			/*
8697c478bd9Sstevel@tonic-gate 			 * We must verify that the offset is valid by
8707c478bd9Sstevel@tonic-gate 			 * checking that it is less than the current data
8717c478bd9Sstevel@tonic-gate 			 * pointer and that it isn't off the end of the
8727c478bd9Sstevel@tonic-gate 			 * packet.
8737c478bd9Sstevel@tonic-gate 			 */
8747c478bd9Sstevel@tonic-gate 			if (label_ptr > data || label_ptr >= data_end)
8757c478bd9Sstevel@tonic-gate 				break;
8767c478bd9Sstevel@tonic-gate 			(void) get_domain_name(header, label_ptr, data_end,
8777c478bd9Sstevel@tonic-gate 			    name, namend);
8787c478bd9Sstevel@tonic-gate 			return (data);
8797c478bd9Sstevel@tonic-gate 		} else {
8807c478bd9Sstevel@tonic-gate 			if (len > (data_end - data)) {
8817c478bd9Sstevel@tonic-gate 				/*
8827c478bd9Sstevel@tonic-gate 				 * The label isn't entirely contained
8837c478bd9Sstevel@tonic-gate 				 * within the packet.  Don't read it.  The
8847c478bd9Sstevel@tonic-gate 				 * caller checks that the data pointer is
8857c478bd9Sstevel@tonic-gate 				 * not beyond the end after we've
8867c478bd9Sstevel@tonic-gate 				 * incremented it.
8877c478bd9Sstevel@tonic-gate 				 */
8887c478bd9Sstevel@tonic-gate 				data = data_end;
8897c478bd9Sstevel@tonic-gate 				break;
8907c478bd9Sstevel@tonic-gate 			}
8917c478bd9Sstevel@tonic-gate 			while (len > 0 && name < (namend - 2)) {
8927c478bd9Sstevel@tonic-gate 				*name = *data;
8937c478bd9Sstevel@tonic-gate 				name++;
8947c478bd9Sstevel@tonic-gate 				data++;
8957c478bd9Sstevel@tonic-gate 				len--;
8967c478bd9Sstevel@tonic-gate 			}
8977c478bd9Sstevel@tonic-gate 			*name = '.';
8987c478bd9Sstevel@tonic-gate 			name++;
8997c478bd9Sstevel@tonic-gate 		}
9007c478bd9Sstevel@tonic-gate 	}
9017c478bd9Sstevel@tonic-gate 	*name = '\0';
9027c478bd9Sstevel@tonic-gate 	return (data);
9037c478bd9Sstevel@tonic-gate }
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate static size_t
print_domain_name(char * line,const uchar_t * header,const uchar_t * data,const uchar_t * data_end)9067c478bd9Sstevel@tonic-gate print_domain_name(char *line, const uchar_t *header, const uchar_t *data,
9077c478bd9Sstevel@tonic-gate     const uchar_t *data_end)
9087c478bd9Sstevel@tonic-gate {
9097c478bd9Sstevel@tonic-gate 	char name[NS_MAXDNAME];
9107c478bd9Sstevel@tonic-gate 	const uchar_t *new_data;
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 	new_data = get_domain_name(header, data, data_end, name,
9137c478bd9Sstevel@tonic-gate 	    name + sizeof (name));
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	(void) sprintf(line, "%s", name);
9167c478bd9Sstevel@tonic-gate 	return (new_data - data);
9177c478bd9Sstevel@tonic-gate }
918