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 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 * 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 * 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 * 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 * 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 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 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 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 * 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 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 * 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 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 * 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 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