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