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