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