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