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