Home | History | Annotate | Download | only in snoop
      1     0    stevel /*
      2     0    stevel  * CDDL HEADER START
      3     0    stevel  *
      4     0    stevel  * The contents of this file are subject to the terms of the
      5  3851   sm26363  * Common Development and Distribution License (the "License").
      6  3851   sm26363  * You may not use this file except in compliance with the License.
      7     0    stevel  *
      8     0    stevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9     0    stevel  * or http://www.opensolaris.org/os/licensing.
     10     0    stevel  * See the License for the specific language governing permissions
     11     0    stevel  * and limitations under the License.
     12     0    stevel  *
     13     0    stevel  * When distributing Covered Code, include this CDDL HEADER in each
     14     0    stevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15     0    stevel  * If applicable, add the following below this CDDL HEADER, with the
     16     0    stevel  * fields enclosed by brackets "[]" replaced with your own identifying
     17     0    stevel  * information: Portions Copyright [yyyy] [name of copyright owner]
     18     0    stevel  *
     19     0    stevel  * CDDL HEADER END
     20     0    stevel  */
     21     0    stevel /*
     22  3851   sm26363  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     23     0    stevel  * Use is subject to license terms.
     24     0    stevel  */
     25     0    stevel 
     26     0    stevel #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27     0    stevel 
     28     0    stevel #include <stdio.h>
     29     0    stevel #include <string.h>
     30     0    stevel #include <limits.h>
     31     0    stevel #include <sys/types.h>
     32     0    stevel #include <sys/errno.h>
     33     0    stevel #include <sys/tiuser.h>
     34     0    stevel #include <arpa/nameser.h>
     35     0    stevel #include <arpa/inet.h>
     36     0    stevel #include <netinet/in.h>
     37     0    stevel #include "snoop.h"
     38     0    stevel 
     39     0    stevel /* The string used to indent detail lines */
     40     0    stevel #define	DNS_INDENT	"    "
     41     0    stevel /*
     42     0    stevel  * From RFC1035, the maximum size of a character-string is limited by the
     43     0    stevel  * one octet length field.  We add one character to that to make sure the
     44     0    stevel  * result is terminated.
     45     0    stevel  */
     46     0    stevel #define	MAX_CHAR_STRING_SIZE	UCHAR_MAX + 1
     47     0    stevel 
     48     0    stevel /* private functions */
     49     0    stevel static char *dns_opcode_string(uint_t opcode);
     50     0    stevel static char *dns_rcode_string(uint_t rcode);
     51     0    stevel static char *dns_type_string(uint_t type, int detail);
     52     0    stevel static char *dns_class_string(uint_t cls, int detail);
     53     0    stevel static size_t skip_question(const uchar_t *header, const uchar_t *data,
     54     0    stevel     const uchar_t *data_end);
     55     0    stevel static size_t print_question(char *line, const uchar_t *header,
     56     0    stevel     const uchar_t *data, const uchar_t *data_end, int detail);
     57     0    stevel static size_t print_answer(char *line, const uchar_t *header,
     58     0    stevel     const uchar_t *data, const uchar_t *data_end, int detail);
     59     0    stevel static char *binary_string(char data);
     60     0    stevel static void print_ip(int af, char *line, const uchar_t *data, uint16_t len);
     61     0    stevel static const uchar_t *get_char_string(const uchar_t *data, char *charbuf,
     62     0    stevel     uint16_t datalen);
     63     0    stevel static size_t print_char_string(char *line, const uchar_t *data, uint16_t len);
     64     0    stevel static const uchar_t *get_domain_name(const uchar_t *header,
     65     0    stevel     const uchar_t *data, const uchar_t *data_end, char *namebuf, char *namend);
     66     0    stevel static size_t print_domain_name(char *line, const uchar_t *header,
     67     0    stevel     const uchar_t *data, const uchar_t *data_end);
     68     0    stevel 
     69     0    stevel void
     70  4904  rs200217 interpret_dns(int flags, int proto, const uchar_t *data, int len, int port)
     71     0    stevel {
     72     0    stevel 	typedef HEADER dns_header;
     73     0    stevel 	dns_header header;
     74     0    stevel 	char *line;
     75     0    stevel 	ushort_t id, qdcount, ancount, nscount, arcount;
     76     0    stevel 	ushort_t count;
     77  3851   sm26363 	const uchar_t *rrp;	/* Resource Record Pointer. */
     78     0    stevel 	const uchar_t *data_end;
     79  4904  rs200217 	const char *protostr;
     80  4904  rs200217 	char *protopfxstr;
     81  4904  rs200217 	char *protohdrstr;
     82     0    stevel 
     83     0    stevel 	if (proto == IPPROTO_TCP) {
     84     0    stevel 		/* not supported now */
     85     0    stevel 		return;
     86  4904  rs200217 	}
     87  4904  rs200217 
     88  4904  rs200217 	if (port == IPPORT_DOMAIN) {
     89  4904  rs200217 		protostr = "DNS";
     90  4904  rs200217 		protopfxstr = "DNS:  ";
     91  4904  rs200217 		protohdrstr = "DNS Header";
     92  4904  rs200217 	} else {
     93  4904  rs200217 		protostr = "MDNS";
     94  4904  rs200217 		protopfxstr = "MDNS:  ";
     95  4904  rs200217 		protohdrstr = "MDNS Header";
     96     0    stevel 	}
     97     0    stevel 
     98     0    stevel 	/* We need at least the header in order to parse a packet. */
     99     0    stevel 	if (sizeof (dns_header) > len) {
    100     0    stevel 		return;
    101     0    stevel 	}
    102     0    stevel 	data_end = data + len;
    103     0    stevel 	/*
    104     0    stevel 	 * Copy the header into a local structure for aligned access to
    105     0    stevel 	 * each field.
    106     0    stevel 	 */
    107     0    stevel 	(void) memcpy(&header, data, sizeof (header));
    108     0    stevel 	id = ntohs(header.id);
    109     0    stevel 	qdcount = ntohs(header.qdcount);
    110     0    stevel 	ancount = ntohs(header.ancount);
    111     0    stevel 	nscount = ntohs(header.nscount);
    112     0    stevel 	arcount = ntohs(header.arcount);
    113     0    stevel 
    114     0    stevel 	if (flags & F_SUM) {
    115     0    stevel 		line = get_sum_line();
    116  4904  rs200217 		line += sprintf(line, "%s %c ",
    117  4904  rs200217 		    protostr, header.qr ? 'R' : 'C');
    118     0    stevel 
    119     0    stevel 		if (header.qr) {
    120     0    stevel 			/* answer */
    121     0    stevel 			if (header.rcode == 0) {
    122     0    stevel 				/* reply is OK */
    123  3851   sm26363 				rrp = data + sizeof (dns_header);
    124     0    stevel 				while (qdcount--) {
    125  3851   sm26363 					if (rrp >= data_end) {
    126     0    stevel 						return;
    127     0    stevel 					}
    128  3851   sm26363 					rrp += skip_question(data,
    129  3851   sm26363 					    rrp, data_end);
    130     0    stevel 				}
    131  3851   sm26363 				/* the answers follow the questions */
    132     0    stevel 				if (ancount > 0) {
    133     0    stevel 					(void) print_answer(line,
    134  3851   sm26363 					    data, rrp, data_end, FALSE);
    135     0    stevel 				}
    136     0    stevel 			} else {
    137     0    stevel 				(void) sprintf(line, " Error: %d(%s)",
    138     0    stevel 				    header.rcode,
    139     0    stevel 				    dns_rcode_string(header.rcode));
    140     0    stevel 			}
    141     0    stevel 		} else {
    142     0    stevel 			/* question */
    143  3851   sm26363 			rrp = data + sizeof (dns_header);
    144  3851   sm26363 			if (rrp >= data_end) {
    145     0    stevel 				return;
    146     0    stevel 			}
    147  3851   sm26363 			(void) print_question(line, data, rrp, data_end,
    148     0    stevel 			    FALSE);
    149     0    stevel 		}
    150     0    stevel 	}
    151     0    stevel 	if (flags & F_DTAIL) {
    152  4904  rs200217 		show_header(protopfxstr, protohdrstr, sizeof (dns_header));
    153     0    stevel 		show_space();
    154     0    stevel 		if (header.qr) {
    155     0    stevel 			/* answer */
    156     0    stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    157     0    stevel 			    "Response ID = %d", id);
    158     0    stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    159     0    stevel 			    "%s%s%s",
    160     0    stevel 			    header.aa ? "AA (Authoritative Answer) " : "",
    161     0    stevel 			    header.tc ? "TC (TrunCation) " : "",
    162     0    stevel 			    header.ra ? "RA (Recursion Available) ": "");
    163     0    stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    164     0    stevel 			    "Response Code: %d (%s)",
    165     0    stevel 			    header.rcode, dns_rcode_string(header.rcode));
    166     0    stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    167     0    stevel 			    "Reply to %d question(s)", qdcount);
    168     0    stevel 		} else {
    169     0    stevel 			/* question */
    170     0    stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    171     0    stevel 			    "Query ID = %d", id);
    172     0    stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    173     0    stevel 			    "Opcode: %s", dns_opcode_string(header.opcode));
    174     0    stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    175     0    stevel 			    "%s%s",
    176     0    stevel 			    header.tc ? "TC (TrunCation) " : "",
    177     0    stevel 			    header.rd ? "RD (Recursion Desired) " : "");
    178     0    stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    179     0    stevel 			    "%d question(s)", qdcount);
    180  3851   sm26363 		}
    181  3851   sm26363 		rrp = data + sizeof (dns_header);
    182  3851   sm26363 		count = 0;
    183  3851   sm26363 		while (qdcount--) {
    184  3851   sm26363 			if (rrp >= data_end) {
    185  3851   sm26363 				return;
    186  3851   sm26363 			}
    187  3851   sm26363 			count++;
    188  3851   sm26363 			rrp += print_question(get_line(0, 0),
    189  3851   sm26363 			    data, rrp, data_end, TRUE);
    190  3851   sm26363 			show_space();
    191  3851   sm26363 		}
    192  3851   sm26363 		/* Only answers should hold answers, but just in case */
    193  3851   sm26363 		if (header.qr || ancount > 0) {
    194  3851   sm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
    195  3851   sm26363 			    "%d answer(s)", ancount);
    196     0    stevel 			count = 0;
    197  3851   sm26363 			while (ancount--) {
    198  3851   sm26363 				if (rrp >= data_end) {
    199  3851   sm26363 					return;
    200  3851   sm26363 				}
    201     0    stevel 				count++;
    202  3851   sm26363 				rrp += print_answer(get_line(0, 0),
    203  3851   sm26363 				    data, rrp, data_end, TRUE);
    204  3851   sm26363 				show_space();
    205  3851   sm26363 			}
    206  3851   sm26363 		}
    207  3851   sm26363 		/* Likewise only answers should hold NS records */
    208  3851   sm26363 		if (header.qr || nscount > 0) {
    209  3851   sm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
    210  3851   sm26363 			    "%d name server resource(s)", nscount);
    211  3851   sm26363 			count = 0;
    212  3851   sm26363 			while (nscount--) {
    213  3851   sm26363 				if (rrp >= data_end) {
    214  3851   sm26363 					return;
    215  3851   sm26363 				}
    216  3851   sm26363 				count++;
    217  3851   sm26363 				rrp += print_answer(get_line(0, 0), data,
    218  3851   sm26363 				    rrp, data_end, TRUE);
    219  3851   sm26363 				show_space();
    220  3851   sm26363 			}
    221  3851   sm26363 		}
    222  3851   sm26363 		/* Additional section may hold an EDNS0 record. */
    223  3851   sm26363 		if (header.qr || arcount > 0) {
    224  3851   sm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
    225  3851   sm26363 			    "%d additional record(s)", arcount);
    226  3851   sm26363 			count = 0;
    227  3851   sm26363 			while (arcount-- && rrp < data_end) {
    228  3851   sm26363 				count++;
    229  3851   sm26363 				rrp += print_answer(get_line(0, 0), data,
    230  3851   sm26363 				    rrp, data_end, TRUE);
    231     0    stevel 				show_space();
    232     0    stevel 			}
    233     0    stevel 		}
    234     0    stevel 	}
    235     0    stevel }
    236     0    stevel 
    237     0    stevel 
    238     0    stevel static char *
    239     0    stevel dns_opcode_string(uint_t opcode)
    240     0    stevel {
    241     0    stevel 	static char buffer[64];
    242     0    stevel 	switch (opcode) {
    243     0    stevel 	case ns_o_query:	return ("Query");
    244     0    stevel 	case ns_o_iquery:	return ("Inverse Query");
    245     0    stevel 	case ns_o_status:	return ("Status");
    246     0    stevel 	default:
    247     0    stevel 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)",
    248     0    stevel 		    opcode);
    249     0    stevel 		return (buffer);
    250     0    stevel 	}
    251     0    stevel }
    252     0    stevel 
    253     0    stevel static char *
    254     0    stevel dns_rcode_string(uint_t rcode)
    255     0    stevel {
    256     0    stevel 	static char buffer[64];
    257     0    stevel 	switch (rcode) {
    258     0    stevel 	case ns_r_noerror:	return ("OK");
    259     0    stevel 	case ns_r_formerr:	return ("Format Error");
    260     0    stevel 	case ns_r_servfail:	return ("Server Fail");
    261     0    stevel 	case ns_r_nxdomain:	return ("Name Error");
    262     0    stevel 	case ns_r_notimpl:	return ("Unimplemented");
    263     0    stevel 	case ns_r_refused:	return ("Refused");
    264  3851   sm26363 	case ns_r_badvers:	return ("Bad Version"); /* EDNS rcode */
    265     0    stevel 	default:
    266     0    stevel 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", rcode);
    267     0    stevel 		return (buffer);
    268     0    stevel 	}
    269     0    stevel }
    270     0    stevel 
    271     0    stevel static char *
    272     0    stevel dns_type_string(uint_t type, int detail)
    273     0    stevel {
    274     0    stevel 	static char buffer[64];
    275     0    stevel 	switch (type) {
    276     0    stevel 	case ns_t_a:	return (detail ? "Address" : "Addr");
    277     0    stevel 	case ns_t_ns:	return (detail ? "Authoritative Name Server" : "NS");
    278     0    stevel 	case ns_t_cname:	return (detail ? "Canonical Name" : "CNAME");
    279     0    stevel 	case ns_t_soa:	return (detail ? "Start Of a zone Authority" : "SOA");
    280     0    stevel 	case ns_t_mb:	return (detail ? "Mailbox domain name" : "MB");
    281     0    stevel 	case ns_t_mg:	return (detail ? "Mailbox Group member" : "MG");
    282     0    stevel 	case ns_t_mr:	return (detail ? "Mail Rename domain name" : "MR");
    283     0    stevel 	case ns_t_null:	return ("NULL");
    284     0    stevel 	case ns_t_wks:	return (detail ? "Well Known Service" : "WKS");
    285     0    stevel 	case ns_t_ptr:	return (detail ? "Domain Name Pointer" : "PTR");
    286     0    stevel 	case ns_t_hinfo:	return (detail ? "Host Information": "HINFO");
    287     0    stevel 	case ns_t_minfo:
    288     0    stevel 		return (detail ? "Mailbox or maillist Info" : "MINFO");
    289     0    stevel 	case ns_t_mx:	return (detail ? "Mail Exchange" : "MX");
    290     0    stevel 	case ns_t_txt:	return (detail ? "Text strings" : "TXT");
    291     0    stevel 	case ns_t_aaaa:	return (detail ? "IPv6 Address" : "AAAA");
    292  3851   sm26363 	case ns_t_opt:	return (detail ? "EDNS0 option" : "OPT");
    293     0    stevel 	case ns_t_axfr:	return (detail ? "Transfer of entire zone" : "AXFR");
    294     0    stevel 	case ns_t_mailb:
    295     0    stevel 		return (detail ? "Mailbox related records" : "MAILB");
    296     0    stevel 	case ns_t_maila:	return (detail ? "Mail agent RRs" : "MAILA");
    297     0    stevel 	case ns_t_any:	return (detail ? "All records" : "*");
    298     0    stevel 	default:
    299     0    stevel 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", type);
    300     0    stevel 		return (buffer);
    301     0    stevel 	}
    302     0    stevel }
    303     0    stevel 
    304     0    stevel static char *
    305     0    stevel dns_class_string(uint_t cls, int detail)
    306     0    stevel {
    307     0    stevel 	static char buffer[64];
    308     0    stevel 	switch (cls) {
    309     0    stevel 	case ns_c_in:		return (detail ? "Internet" : "Internet");
    310     0    stevel 	case ns_c_chaos: 	return (detail ? "CHAOS" : "CH");
    311     0    stevel 	case ns_c_hs:		return (detail ? "Hesiod" : "HS");
    312     0    stevel 	case ns_c_any:		return (detail ? "* (Any class)" : "*");
    313     0    stevel 	default:
    314     0    stevel 		(void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", cls);
    315     0    stevel 		return (buffer);
    316     0    stevel 	}
    317     0    stevel }
    318     0    stevel 
    319     0    stevel static size_t
    320     0    stevel skip_question(const uchar_t *header, const uchar_t *data,
    321     0    stevel     const uchar_t *data_end)
    322     0    stevel {
    323     0    stevel 	const uchar_t *data_bak = data;
    324     0    stevel 	char dummy_buffer[NS_MAXDNAME];
    325     0    stevel 
    326     0    stevel 	data = get_domain_name(header, data, data_end, dummy_buffer,
    327     0    stevel 	    dummy_buffer + sizeof (dummy_buffer));
    328     0    stevel 	/* Skip the 32 bits of class and type that follow the domain name */
    329     0    stevel 	data += sizeof (uint32_t);
    330     0    stevel 	return (data - data_bak);
    331     0    stevel }
    332     0    stevel 
    333     0    stevel static size_t
    334     0    stevel print_question(char *line, const uchar_t *header, const uchar_t *data,
    335     0    stevel     const uchar_t *data_end, int detail)
    336     0    stevel {
    337     0    stevel 	const uchar_t *data_bak = data;
    338     0    stevel 	uint16_t type;
    339     0    stevel 	uint16_t cls;
    340     0    stevel 
    341     0    stevel 	if (detail) {
    342     0    stevel 		line += snprintf(line, get_line_remain(),
    343     0    stevel 		    DNS_INDENT "Domain Name: ");
    344     0    stevel 	}
    345     0    stevel 	data += print_domain_name(line, header, data, data_end);
    346     0    stevel 
    347     0    stevel 	/*
    348     0    stevel 	 * Make sure we don't run off the end of the packet by reading the
    349     0    stevel 	 * type and class.
    350     0    stevel 	 *
    351     0    stevel 	 * The pointer subtraction on the left side of the following
    352     0    stevel 	 * expression has a signed result of type ptrdiff_t, and the right
    353     0    stevel 	 * side has an unsigned result of type size_t.  We therefore need
    354     0    stevel 	 * to cast the right side of the expression to be of the same
    355     0    stevel 	 * signed type to keep the result of the pointer arithmetic to be
    356     0    stevel 	 * automatically cast to an unsigned value.  We do a similar cast
    357     0    stevel 	 * in other similar expressions throughout this file.
    358     0    stevel 	 */
    359     0    stevel 	if ((data_end - data) < (ptrdiff_t)(2 * sizeof (uint16_t)))
    360     0    stevel 		return (data_end - data_bak);
    361     0    stevel 
    362     0    stevel 	GETINT16(type, data);
    363     0    stevel 	GETINT16(cls, data);
    364     0    stevel 
    365  4904  rs200217 	/*
    366  4904  rs200217 	 * Multicast DNS re-uses the top bit of the class field
    367  4904  rs200217 	 * in the question and answer sections. Unicast DNS only
    368  4904  rs200217 	 * uses 1 (Internet), 3 and 4. Hence it is safe. The top
    369  4904  rs200217 	 * order bit is always cleared here to display the rrclass in case
    370  4904  rs200217 	 * of Multicast DNS packets.
    371  4904  rs200217 	 */
    372  4904  rs200217 	cls = cls & 0x7fff;
    373  4904  rs200217 
    374     0    stevel 	if (detail) {
    375     0    stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    376     0    stevel 		    DNS_INDENT "Class: %u (%s)",
    377     0    stevel 		    cls, dns_class_string(cls, detail));
    378     0    stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    379     0    stevel 		    DNS_INDENT "Type:  %u (%s)", type,
    380     0    stevel 		    dns_type_string(type, detail));
    381     0    stevel 	} else {
    382     0    stevel 		(void) sprintf(line + strlen(line), " %s %s \?",
    383     0    stevel 		    dns_class_string(cls, detail),
    384     0    stevel 		    dns_type_string(type, detail));
    385     0    stevel 	}
    386     0    stevel 	return (data - data_bak);
    387     0    stevel }
    388     0    stevel 
    389  3851   sm26363 /*
    390  3851   sm26363  * print_answer() is used to display the contents of a single resource
    391  3851   sm26363  * record (RR) from either the answer, name server or additional
    392  3851   sm26363  * section of the DNS packet.
    393  3851   sm26363  *
    394  3851   sm26363  * Input:
    395  3851   sm26363  *	*line: snoops output buffer.
    396  3851   sm26363  *	*header: start of the DNS packet, required for names and rcode.
    397  3851   sm26363  *	*data: location within header from where the RR starts.
    398  3851   sm26363  * 	*data_end: where DNS data ends.
    399  3851   sm26363  * 	detail: simple or verbose output.
    400  3851   sm26363  *
    401  3851   sm26363  * Returns:
    402  3851   sm26363  *	Pointer to next RR or data_end.
    403  3851   sm26363  *
    404  3851   sm26363  * Most RRs have the same top level format as defined in RFC 1035:
    405  3851   sm26363  *
    406  3851   sm26363  *                                     1  1  1  1  1  1
    407  3851   sm26363  *       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    408  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    409  3851   sm26363  *    |                                               |
    410  3851   sm26363  *    /                      NAME                     /
    411  3851   sm26363  *    |                                               |
    412  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    413  3851   sm26363  *    |                      TYPE                     |
    414  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    415  3851   sm26363  *    |                     CLASS                     |
    416  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    417  3851   sm26363  *    |                      TTL                      |
    418  3851   sm26363  *    |                                               |
    419  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    420  3851   sm26363  *    |                   RDLENGTH                    |
    421  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
    422  3851   sm26363  *    /                     RDATA                     /
    423  3851   sm26363  *    /                                               /
    424  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    425  3851   sm26363  *
    426  3851   sm26363  * However RFC 2671 introduced an exception to this rule
    427  3851   sm26363  * with the "Extension Mechanisms for DNS" (EDNS0).
    428  3851   sm26363  * When the type is 41 the remaining resource record format
    429  3851   sm26363  * is:
    430  3851   sm26363  *
    431  3851   sm26363  *                                     1  1  1  1  1  1
    432  3851   sm26363  *       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    433  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    434  3851   sm26363  *    |                    TYPE = 41                  |
    435  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    436  3851   sm26363  *    |           Sender's UDP payload size           |
    437  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    438  3851   sm26363  *    |    Extended-rcode     |        Version        |
    439  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    440  3851   sm26363  *    |                      Zero                     |
    441  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    442  3851   sm26363  *    |                   RDLENGTH                    |
    443  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
    444  3851   sm26363  *    /                     RDATA                     /
    445  3851   sm26363  *    /                                               /
    446  3851   sm26363  *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    447  3851   sm26363  *
    448  3851   sm26363  */
    449     0    stevel static size_t
    450     0    stevel print_answer(char *line, const uchar_t *header, const uchar_t *data,
    451     0    stevel     const uchar_t *data_end, int detail)
    452     0    stevel {
    453     0    stevel 	const uchar_t *data_bak = data;
    454     0    stevel 	const uchar_t *data_next;
    455     0    stevel 	uint16_t type;
    456     0    stevel 	uint16_t cls;
    457     0    stevel 	int32_t ttl;
    458     0    stevel 	uint16_t rdlen;
    459     0    stevel 	uint32_t serial, refresh, retry, expire, minimum;
    460     0    stevel 	uint8_t protocol;
    461     0    stevel 	int linepos;
    462     0    stevel 	uint16_t preference;
    463  3851   sm26363 	/* declarations for EDNS follow */
    464  3851   sm26363 	uint16_t size;	/* Sender's UDP payload size */
    465  3851   sm26363 	uint8_t xrcode;	/* Extended-rcode */
    466  3851   sm26363 	uint8_t ver;	/* Version */
    467  3851   sm26363 	uint16_t rcode;	/* Extracted from the DNS header */
    468  3851   sm26363 	union {		/* DNS header overlay used for extraction */
    469  3851   sm26363 		HEADER		*head;
    470  3851   sm26363 		const uchar_t	*raw;
    471  3851   sm26363 	} headptr;
    472     0    stevel 
    473     0    stevel 	if (detail) {
    474     0    stevel 		line += snprintf(line, get_line_remain(),
    475     0    stevel 		    DNS_INDENT "Domain Name: ");
    476     0    stevel 	}
    477     0    stevel 	data += print_domain_name(line, header, data, data_end);
    478     0    stevel 
    479     0    stevel 	/*
    480  3851   sm26363 	 * Next, get the record type, being careful to make sure we
    481  3851   sm26363 	 * don't run off the end of the packet.
    482     0    stevel 	 */
    483  3851   sm26363 	if ((data_end - data) < (ptrdiff_t)(sizeof (type))) {
    484     0    stevel 		return (data_end - data_bak);
    485     0    stevel 	}
    486     0    stevel 
    487     0    stevel 	GETINT16(type, data);
    488  3851   sm26363 
    489  3851   sm26363 	if (type == ns_t_opt) {
    490  3851   sm26363 		/*
    491  3851   sm26363 		 * Make sure we won't run off the end reading size,
    492  3851   sm26363 		 * xrcode, version, zero and rdlen.
    493  3851   sm26363 		 */
    494  3851   sm26363 		if ((data_end - data) <
    495  3851   sm26363 		    ((ptrdiff_t)(sizeof (size)
    496  4904  rs200217 		    + sizeof (xrcode)
    497  4904  rs200217 		    + sizeof (ver)
    498  4904  rs200217 		    + sizeof (cls)	/* zero */
    499  4904  rs200217 		    + sizeof (rdlen)))) {
    500  3851   sm26363 			return (data_end - data_bak);
    501  3851   sm26363 		}
    502  3851   sm26363 
    503  3851   sm26363 		GETINT16(size, data);
    504  3851   sm26363 		GETINT8(xrcode, data);
    505  3851   sm26363 		/*
    506  3851   sm26363 		 * The extended rcode represents the top half of the
    507  3851   sm26363 		 * rcode which must be added to the rcode in the header.
    508  3851   sm26363 		 */
    509  3851   sm26363 		rcode = 0xff & (xrcode << 4);
    510  3851   sm26363 		headptr.raw = header;		/* Overlay the header... */
    511  3851   sm26363 		rcode += headptr.head->rcode;	/* And pluck out the rcode. */
    512  3851   sm26363 
    513  3851   sm26363 		GETINT8(ver, data);
    514  3851   sm26363 		GETINT16(cls, data); /* zero */
    515  3851   sm26363 		GETINT16(rdlen, data);
    516  3851   sm26363 
    517  3851   sm26363 		if (detail) {
    518  3851   sm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
    519  3851   sm26363 			    DNS_INDENT "Type:  %u (%s)", type,
    520  3851   sm26363 			    dns_type_string(type, detail));
    521  3851   sm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
    522  3851   sm26363 			    DNS_INDENT "UDP payload size: %u (0x%.4x)",
    523  3851   sm26363 			    size, size);
    524  3851   sm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
    525  3851   sm26363 			    DNS_INDENT "Extended rcode: %u "
    526  3851   sm26363 			    "(translates to %u (%s))",
    527  3851   sm26363 			    xrcode, rcode, dns_rcode_string(rcode));
    528  3851   sm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
    529  3851   sm26363 			    DNS_INDENT "EDNS0 Version: %u", ver);
    530  3851   sm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
    531  3851   sm26363 			    DNS_INDENT "zero: %u", cls);
    532  3851   sm26363 			(void) snprintf(get_line(0, 0), get_line_remain(),
    533  3851   sm26363 			    DNS_INDENT "Data length: %u", rdlen);
    534  3851   sm26363 		} else {
    535  3851   sm26363 			line += strlen(line);
    536  3851   sm26363 			line += sprintf(line, " %s UDP %u rc %d ver %u len %u",
    537  3851   sm26363 			    dns_type_string(type, detail), size, rcode, ver,
    538  3851   sm26363 			    rdlen);
    539  3851   sm26363 		}
    540  3851   sm26363 
    541  3851   sm26363 		/*
    542  3851   sm26363 		 * Make sure that rdlen is within data boundary.
    543  3851   sm26363 		 */
    544  3851   sm26363 		if (rdlen > data_end - data)
    545  3851   sm26363 			return (data_end - data_bak);
    546  3851   sm26363 
    547  3851   sm26363 		/* Future OPT decode code goes here. */
    548  3851   sm26363 
    549  3851   sm26363 		data += rdlen;
    550  3851   sm26363 		return (data - data_bak);
    551  3851   sm26363 	}
    552  3851   sm26363 
    553  3851   sm26363 	/*
    554  3851   sm26363 	 * Make sure we don't run off the end of the packet by reading the
    555  3851   sm26363 	 * class, ttl, and length.
    556  3851   sm26363 	 */
    557  3851   sm26363 	if ((data_end - data) <
    558  3851   sm26363 	    ((ptrdiff_t)(sizeof (cls)
    559  4904  rs200217 	    + sizeof (ttl)
    560  4904  rs200217 	    + sizeof (rdlen)))) {
    561  3851   sm26363 		return (data_end - data_bak);
    562  3851   sm26363 	}
    563  3851   sm26363 
    564     0    stevel 	GETINT16(cls, data);
    565  4904  rs200217 
    566  4904  rs200217 	/*
    567  4904  rs200217 	 * Multicast DNS re-uses the top bit of the class field
    568  4904  rs200217 	 * in the question and answer sections. Unicast DNS only
    569  4904  rs200217 	 * uses 1 (Internet), 3 and 4. Hence it is safe. The top
    570  4904  rs200217 	 * order bit is always cleared here to display the rrclass in case
    571  4904  rs200217 	 * of Multicast DNS packets.
    572  4904  rs200217 	 */
    573  4904  rs200217 	cls = cls & 0x7fff;
    574     0    stevel 
    575     0    stevel 	if (detail) {
    576     0    stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    577     0    stevel 		    DNS_INDENT "Class: %d (%s)", cls,
    578     0    stevel 		    dns_class_string(cls, detail));
    579     0    stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    580     0    stevel 		    DNS_INDENT "Type:  %d (%s)", type,
    581     0    stevel 		    dns_type_string(type, detail));
    582     0    stevel 	} else {
    583     0    stevel 		line += strlen(line);
    584     0    stevel 		line += sprintf(line, " %s %s ",
    585     0    stevel 		    dns_class_string(cls, detail),
    586     0    stevel 		    dns_type_string(type, detail));
    587     0    stevel 	}
    588     0    stevel 
    589     0    stevel 	GETINT32(ttl, data);
    590     0    stevel 	if (detail) {
    591     0    stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    592     0    stevel 		    DNS_INDENT "TTL (Time To Live): %d", ttl);
    593     0    stevel 	}
    594     0    stevel 
    595     0    stevel 	GETINT16(rdlen, data);
    596     0    stevel 	if (detail) {
    597     0    stevel 		line = get_line(0, 0);
    598     0    stevel 		line += snprintf(line, get_line_remain(), DNS_INDENT "%s: ",
    599     0    stevel 		    dns_type_string(type, detail));
    600     0    stevel 	}
    601     0    stevel 
    602     0    stevel 	if (rdlen > data_end - data)
    603     0    stevel 		return (data_end - data_bak);
    604     0    stevel 
    605     0    stevel 	switch (type) {
    606     0    stevel 	case ns_t_a:
    607     0    stevel 		print_ip(AF_INET, line, data, rdlen);
    608     0    stevel 		break;
    609     0    stevel 	case ns_t_aaaa:
    610     0    stevel 		print_ip(AF_INET6, line, data, rdlen);
    611     0    stevel 		break;
    612     0    stevel 	case ns_t_hinfo:
    613     0    stevel 		line += sprintf(line, "CPU: ");
    614     0    stevel 		data_next = data + print_char_string(line, data, rdlen);
    615     0    stevel 		if (data_next >= data_end)
    616     0    stevel 			break;
    617     0    stevel 		line += strlen(line);
    618     0    stevel 		line += sprintf(line, "OS: ");
    619     0    stevel 		(void) print_char_string(line, data_next,
    620     0    stevel 		    rdlen - (data_next - data));
    621     0    stevel 		break;
    622     0    stevel 	case ns_t_ns:
    623     0    stevel 	case ns_t_cname:
    624     0    stevel 	case ns_t_mb:
    625     0    stevel 	case ns_t_mg:
    626     0    stevel 	case ns_t_mr:
    627     0    stevel 	case ns_t_ptr:
    628     0    stevel 		(void) print_domain_name(line, header, data, data_end);
    629     0    stevel 		break;
    630     0    stevel 	case ns_t_mx:
    631     0    stevel 		data_next = data;
    632     0    stevel 		if (rdlen < sizeof (uint16_t))
    633     0    stevel 			break;
    634     0    stevel 		GETINT16(preference, data_next);
    635     0    stevel 		if (detail) {
    636     0    stevel 			(void) print_domain_name(line, header, data_next,
    637     0    stevel 			    data_end);
    638     0    stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    639     0    stevel 			    DNS_INDENT "Preference: %u", preference);
    640     0    stevel 		} else {
    641     0    stevel 			(void) print_domain_name(line, header, data_next,
    642     0    stevel 			    data_end);
    643     0    stevel 		}
    644     0    stevel 		break;
    645     0    stevel 	case ns_t_soa:
    646     0    stevel 		if (!detail)
    647     0    stevel 			break;
    648     0    stevel 		line = get_line(0, 0);
    649     0    stevel 		line += snprintf(line, get_line_remain(),
    650     0    stevel 		    DNS_INDENT "MNAME (Server name): ");
    651     0    stevel 		data_next = data + print_domain_name(line, header, data,
    652     0    stevel 		    data_end);
    653     0    stevel 		if (data_next >= data_end)
    654     0    stevel 			break;
    655     0    stevel 		line = get_line(0, 0);
    656     0    stevel 		line += snprintf(line, get_line_remain(),
    657     0    stevel 		    DNS_INDENT "RNAME (Resposible mailbox): ");
    658     0    stevel 		data_next = data_next +
    659     0    stevel 		    print_domain_name(line, header, data_next, data_end);
    660     0    stevel 		if ((data_end - data_next) < (ptrdiff_t)(5 * sizeof (uint32_t)))
    661     0    stevel 			break;
    662     0    stevel 		GETINT32(serial, data_next);
    663     0    stevel 		GETINT32(refresh, data_next);
    664     0    stevel 		GETINT32(retry, data_next);
    665     0    stevel 		GETINT32(expire, data_next);
    666     0    stevel 		GETINT32(minimum, data_next);
    667     0    stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    668     0    stevel 		    DNS_INDENT "Serial: %u", serial);
    669     0    stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    670     0    stevel 		    DNS_INDENT "Refresh: %u  Retry: %u  "
    671     0    stevel 		    "Expire: %u Minimum: %u",
    672     0    stevel 		    refresh, retry, expire, minimum);
    673     0    stevel 		break;
    674     0    stevel 	case ns_t_wks:
    675     0    stevel 		print_ip(AF_INET, line, data, rdlen);
    676     0    stevel 		if (!detail)
    677     0    stevel 			break;
    678     0    stevel 		data_next = data + sizeof (in_addr_t);
    679     0    stevel 		if (data_next >= data_end)
    680     0    stevel 			break;
    681     0    stevel 		GETINT8(protocol, data_next);
    682     0    stevel 		line = get_line(0, 0);
    683     0    stevel 		line += snprintf(line, get_line_remain(),
    684     0    stevel 		    DNS_INDENT "Protocol: %u ", protocol);
    685     0    stevel 		switch (protocol) {
    686     0    stevel 		case IPPROTO_UDP:
    687     0    stevel 			(void) snprintf(line, get_line_remain(), "(UDP)");
    688     0    stevel 			break;
    689     0    stevel 		case IPPROTO_TCP:
    690     0    stevel 			(void) snprintf(line, get_line_remain(), "(TCP)");
    691     0    stevel 			break;
    692     0    stevel 		}
    693     0    stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    694     0    stevel 		    DNS_INDENT "Service bitmap:");
    695     0    stevel 		(void) snprintf(line, get_line_remain(),
    696     0    stevel 		    DNS_INDENT "0       8       16      24");
    697     0    stevel 		linepos = 4;
    698     0    stevel 		while (data_next < data + rdlen) {
    699     0    stevel 			if (linepos == 4) {
    700     0    stevel 				line = get_line(0, 0);
    701     0    stevel 				line += snprintf(line, get_line_remain(),
    702     0    stevel 				    DNS_INDENT);
    703     0    stevel 				linepos = 0;
    704     0    stevel 			}
    705     0    stevel 			line += snprintf(line, get_line_remain(), "%s",
    706     0    stevel 			    binary_string(*data_next));
    707     0    stevel 			linepos++;
    708     0    stevel 			data_next++;
    709     0    stevel 		}
    710     0    stevel 		break;
    711     0    stevel 	case ns_t_minfo:
    712     0    stevel 		if (!detail)
    713     0    stevel 			break;
    714     0    stevel 		line = get_line(0, 0);
    715     0    stevel 		line += snprintf(line, get_line_remain(),
    716     0    stevel 		    DNS_INDENT "RMAILBX (Resposible mailbox): ");
    717     0    stevel 		data_next = data + print_domain_name(line, header, data,
    718     0    stevel 		    data_end);
    719     0    stevel 		line = get_line(0, 0);
    720     0    stevel 		line += snprintf(line, get_line_remain(),
    721     0    stevel 		    DNS_INDENT "EMAILBX (mailbox to receive err message): ");
    722     0    stevel 		data_next = data_next + print_domain_name(line, header,
    723     0    stevel 		    data_next, data_end);
    724     0    stevel 		break;
    725     0    stevel 	}
    726     0    stevel 	data += rdlen;
    727     0    stevel 	return (data - data_bak);
    728     0    stevel }
    729     0    stevel 
    730     0    stevel static char *
    731     0    stevel binary_string(char data)
    732     0    stevel {
    733     0    stevel 	static char bstring[8 + 1];
    734     0    stevel 	char *ptr;
    735     0    stevel 	int i;
    736     0    stevel 	ptr = bstring;
    737     0    stevel 	for (i = 0; i < 8; i++) {
    738     0    stevel 		*ptr++ = (data & 0x80) ? '1' : '0';
    739     0    stevel 		data = data << 1;
    740     0    stevel 	}
    741     0    stevel 	*ptr = (char)0;
    742     0    stevel 	return (bstring);
    743     0    stevel }
    744     0    stevel 
    745     0    stevel static void
    746     0    stevel print_ip(int af, char *line, const uchar_t *data, uint16_t len)
    747     0    stevel {
    748     0    stevel 	in6_addr_t	addr6;
    749     0    stevel 	in_addr_t	addr4;
    750     0    stevel 	void		*addr;
    751     0    stevel 
    752     0    stevel 	switch (af) {
    753     0    stevel 	case AF_INET:
    754     0    stevel 		if (len != sizeof (in_addr_t))
    755     0    stevel 			return;
    756     0    stevel 		addr = memcpy(&addr4, data, sizeof (addr4));
    757     0    stevel 		break;
    758     0    stevel 	case AF_INET6:
    759     0    stevel 		if (len != sizeof (in6_addr_t))
    760     0    stevel 			return;
    761     0    stevel 		addr = memcpy(&addr6, data, sizeof (addr6));
    762     0    stevel 		break;
    763     0    stevel 	}
    764     0    stevel 
    765     0    stevel 	(void) inet_ntop(af, addr, line, INET6_ADDRSTRLEN);
    766     0    stevel }
    767     0    stevel 
    768     0    stevel /*
    769     0    stevel  * charbuf is assumed to be of size MAX_CHAR_STRING_SIZE.
    770     0    stevel  */
    771     0    stevel static const uchar_t *
    772     0    stevel get_char_string(const uchar_t *data, char *charbuf, uint16_t datalen)
    773     0    stevel {
    774   410    kcpoon 	int len;
    775     0    stevel 	char *name = charbuf;
    776     0    stevel 	int i = 0;
    777     0    stevel 
    778     0    stevel 	/*
    779     0    stevel 	 * From RFC1035, a character-string is a single length octet followed
    780     0    stevel 	 * by that number of characters.
    781     0    stevel 	 */
    782     0    stevel 	if (datalen > 1) {
    783     0    stevel 		len = *data;
    784     0    stevel 		data++;
    785     0    stevel 		if (len > 0 && len < MAX_CHAR_STRING_SIZE) {
    786     0    stevel 			for (i = 0; i < len; i++, data++)
    787     0    stevel 				name[i] = *data;
    788     0    stevel 		}
    789     0    stevel 	}
    790     0    stevel 	name[i] = '\0';
    791     0    stevel 	return (data);
    792     0    stevel }
    793     0    stevel 
    794     0    stevel static size_t
    795     0    stevel print_char_string(char *line, const uchar_t *data, uint16_t len)
    796     0    stevel {
    797     0    stevel 	char charbuf[MAX_CHAR_STRING_SIZE];
    798     0    stevel 	const uchar_t *data_bak = data;
    799     0    stevel 
    800     0    stevel 	data = get_char_string(data, charbuf, len);
    801     0    stevel 	(void) sprintf(line, "%s", charbuf);
    802     0    stevel 	return (data - data_bak);
    803     0    stevel }
    804     0    stevel 
    805     0    stevel /*
    806     0    stevel  * header: the entire message header, this is where we start to
    807     0    stevel  *	   count the offset of the compression scheme
    808     0    stevel  * data:   the start of the domain name
    809     0    stevel  * namebuf: user supplied buffer
    810     0    stevel  * return: the next byte after what we have parsed
    811     0    stevel  */
    812     0    stevel static const uchar_t *
    813     0    stevel get_domain_name(const uchar_t *header, const uchar_t *data,
    814     0    stevel     const uchar_t *data_end, char *namebuf, char *namend)
    815     0    stevel {
    816     0    stevel 	uint8_t len;
    817     0    stevel 	char *name = namebuf;
    818     0    stevel 
    819     0    stevel 	/*
    820     0    stevel 	 * From RFC1035, a domain name is a sequence of labels, where each
    821     0    stevel 	 * label consists of a length octet followed by that number of
    822     0    stevel 	 * octets.  The domain name terminates with the zero length octet
    823     0    stevel 	 * for the null label of the root.
    824     0    stevel 	 */
    825     0    stevel 
    826     0    stevel 	while (name < (namend - 1)) {
    827     0    stevel 		if ((data_end - data) < (ptrdiff_t)(sizeof (uint8_t))) {
    828     0    stevel 			/* The length octet is off the end of the packet. */
    829     0    stevel 			break;
    830     0    stevel 		}
    831     0    stevel 		GETINT8(len, data);
    832     0    stevel 		if (len == 0) {
    833     0    stevel 			/*
    834     0    stevel 			 * Domain names end with a length byte of zero,
    835     0    stevel 			 * which represents the null label of the root.
    836     0    stevel 			 */
    837     0    stevel 			break;
    838     0    stevel 		}
    839     0    stevel 		/*
    840     0    stevel 		 * test if we are using the compression scheme
    841     0    stevel 		 */
    842     0    stevel 		if ((len & 0xc0) == 0xc0) {
    843     0    stevel 			uint16_t offset;
    844     0    stevel 			const uchar_t *label_ptr;
    845     0    stevel 
    846     0    stevel 			/*
    847     0    stevel 			 * From RFC1035, message compression allows a
    848     0    stevel 			 * domain name or a list of labels at the end of a
    849     0    stevel 			 * domain name to be replaced with a pointer to a
    850     0    stevel 			 * prior occurance of the same name.  In this
    851     0    stevel 			 * scheme, the pointer is a two octet sequence
    852     0    stevel 			 * where the most significant two bits are set, and
    853     0    stevel 			 * the remaining 14 bits are the offset from the
    854     0    stevel 			 * start of the message of the next label.
    855     0    stevel 			 */
    856     0    stevel 			data--;
    857     0    stevel 			if ((data_end - data) <
    858     0    stevel 			    (ptrdiff_t)(sizeof (uint16_t))) {
    859     0    stevel 				/*
    860     0    stevel 				 * The offset octets aren't entirely
    861     0    stevel 				 * contained within this pakcet.
    862     0    stevel 				 */
    863     0    stevel 				data = data_end;
    864     0    stevel 				break;
    865     0    stevel 			}
    866     0    stevel 			GETINT16(offset, data);
    867     0    stevel 			label_ptr = header + (offset & 0x3fff);
    868     0    stevel 			/*
    869     0    stevel 			 * We must verify that the offset is valid by
    870     0    stevel 			 * checking that it is less than the current data
    871     0    stevel 			 * pointer and that it isn't off the end of the
    872     0    stevel 			 * packet.
    873     0    stevel 			 */
    874     0    stevel 			if (label_ptr > data || label_ptr >= data_end)
    875     0    stevel 				break;
    876     0    stevel 			(void) get_domain_name(header, label_ptr, data_end,
    877     0    stevel 			    name, namend);
    878     0    stevel 			return (data);
    879     0    stevel 		} else {
    880     0    stevel 			if (len > (data_end - data)) {
    881     0    stevel 				/*
    882     0    stevel 				 * The label isn't entirely contained
    883     0    stevel 				 * within the packet.  Don't read it.  The
    884     0    stevel 				 * caller checks that the data pointer is
    885     0    stevel 				 * not beyond the end after we've
    886     0    stevel 				 * incremented it.
    887     0    stevel 				 */
    888     0    stevel 				data = data_end;
    889     0    stevel 				break;
    890     0    stevel 			}
    891     0    stevel 			while (len > 0 && name < (namend - 2)) {
    892     0    stevel 				*name = *data;
    893     0    stevel 				name++;
    894     0    stevel 				data++;
    895     0    stevel 				len--;
    896     0    stevel 			}
    897     0    stevel 			*name = '.';
    898     0    stevel 			name++;
    899     0    stevel 		}
    900     0    stevel 	}
    901     0    stevel 	*name = '\0';
    902     0    stevel 	return (data);
    903     0    stevel }
    904     0    stevel 
    905     0    stevel static size_t
    906     0    stevel print_domain_name(char *line, const uchar_t *header, const uchar_t *data,
    907     0    stevel     const uchar_t *data_end)
    908     0    stevel {
    909     0    stevel 	char name[NS_MAXDNAME];
    910     0    stevel 	const uchar_t *new_data;
    911     0    stevel 
    912     0    stevel 	new_data = get_domain_name(header, data, data_end, name,
    913     0    stevel 	    name + sizeof (name));
    914     0    stevel 
    915     0    stevel 	(void) sprintf(line, "%s", name);
    916     0    stevel 	return (new_data - data);
    917     0    stevel }
    918