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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <string.h> 31 #include <limits.h> 32 #include <sys/types.h> 33 #include <sys/errno.h> 34 #include <sys/tiuser.h> 35 #include <arpa/nameser.h> 36 #include <arpa/inet.h> 37 #include <netinet/in.h> 38 #include "snoop.h" 39 40 /* The string used to indent detail lines */ 41 #define DNS_INDENT " " 42 /* 43 * From RFC1035, the maximum size of a character-string is limited by the 44 * one octet length field. We add one character to that to make sure the 45 * result is terminated. 46 */ 47 #define MAX_CHAR_STRING_SIZE UCHAR_MAX + 1 48 49 /* private functions */ 50 static char *dns_opcode_string(uint_t opcode); 51 static char *dns_rcode_string(uint_t rcode); 52 static char *dns_type_string(uint_t type, int detail); 53 static char *dns_class_string(uint_t cls, int detail); 54 static size_t skip_question(const uchar_t *header, const uchar_t *data, 55 const uchar_t *data_end); 56 static size_t print_question(char *line, const uchar_t *header, 57 const uchar_t *data, const uchar_t *data_end, int detail); 58 static size_t print_answer(char *line, const uchar_t *header, 59 const uchar_t *data, const uchar_t *data_end, int detail); 60 static char *binary_string(char data); 61 static void print_ip(int af, char *line, const uchar_t *data, uint16_t len); 62 static const uchar_t *get_char_string(const uchar_t *data, char *charbuf, 63 uint16_t datalen); 64 static size_t print_char_string(char *line, const uchar_t *data, uint16_t len); 65 static const uchar_t *get_domain_name(const uchar_t *header, 66 const uchar_t *data, const uchar_t *data_end, char *namebuf, char *namend); 67 static size_t print_domain_name(char *line, const uchar_t *header, 68 const uchar_t *data, const uchar_t *data_end); 69 70 void 71 interpret_dns(int flags, int proto, const uchar_t *data, int len) 72 { 73 typedef HEADER dns_header; 74 dns_header header; 75 char *line; 76 ushort_t id, qdcount, ancount, nscount, arcount; 77 ushort_t count; 78 const uchar_t *questions; 79 const uchar_t *answers; 80 const uchar_t *nservers; 81 const uchar_t *additions; 82 const uchar_t *data_end; 83 84 if (proto == IPPROTO_TCP) { 85 /* not supported now */ 86 return; 87 } 88 89 /* We need at least the header in order to parse a packet. */ 90 if (sizeof (dns_header) > len) { 91 return; 92 } 93 data_end = data + len; 94 /* 95 * Copy the header into a local structure for aligned access to 96 * each field. 97 */ 98 (void) memcpy(&header, data, sizeof (header)); 99 id = ntohs(header.id); 100 qdcount = ntohs(header.qdcount); 101 ancount = ntohs(header.ancount); 102 nscount = ntohs(header.nscount); 103 arcount = ntohs(header.arcount); 104 105 if (flags & F_SUM) { 106 line = get_sum_line(); 107 line += sprintf(line, "DNS %c ", header.qr ? 'R' : 'C'); 108 109 if (header.qr) { 110 /* answer */ 111 if (header.rcode == 0) { 112 /* reply is OK */ 113 questions = data + sizeof (dns_header); 114 while (qdcount--) { 115 if (questions >= data_end) { 116 return; 117 } 118 questions += skip_question(data, 119 questions, data_end); 120 } 121 /* the answers are following the questions */ 122 answers = questions; 123 if (ancount > 0) { 124 (void) print_answer(line, 125 data, answers, data_end, FALSE); 126 } 127 } else { 128 (void) sprintf(line, " Error: %d(%s)", 129 header.rcode, 130 dns_rcode_string(header.rcode)); 131 } 132 } else { 133 /* question */ 134 questions = data + sizeof (dns_header); 135 if (questions >= data_end) { 136 return; 137 } 138 (void) print_question(line, data, questions, data_end, 139 FALSE); 140 } 141 } 142 if (flags & F_DTAIL) { 143 show_header("DNS: ", "DNS Header", sizeof (dns_header)); 144 show_space(); 145 if (header.qr) { 146 /* answer */ 147 (void) snprintf(get_line(0, 0), get_line_remain(), 148 "Response ID = %d", id); 149 (void) snprintf(get_line(0, 0), get_line_remain(), 150 "%s%s%s", 151 header.aa ? "AA (Authoritative Answer) " : "", 152 header.tc ? "TC (TrunCation) " : "", 153 header.ra ? "RA (Recursion Available) ": ""); 154 (void) snprintf(get_line(0, 0), get_line_remain(), 155 "Response Code: %d (%s)", 156 header.rcode, dns_rcode_string(header.rcode)); 157 (void) snprintf(get_line(0, 0), get_line_remain(), 158 "Reply to %d question(s)", qdcount); 159 questions = data + sizeof (dns_header); 160 count = 0; 161 while (qdcount--) { 162 if (questions >= data_end) { 163 return; 164 } 165 count++; 166 questions += print_question(get_line(0, 0), 167 data, questions, data_end, TRUE); 168 show_space(); 169 } 170 (void) snprintf(get_line(0, 0), get_line_remain(), 171 "%d answer(s)", ancount); 172 answers = questions; 173 count = 0; 174 while (ancount--) { 175 if (answers >= data_end) { 176 return; 177 } 178 count++; 179 answers += print_answer(get_line(0, 0), 180 data, answers, data_end, TRUE); 181 show_space(); 182 } 183 (void) snprintf(get_line(0, 0), get_line_remain(), 184 "%d name server resource(s)", nscount); 185 nservers = answers; 186 count = 0; 187 while (nscount--) { 188 if (nservers >= data_end) { 189 return; 190 } 191 count++; 192 nservers += print_answer(get_line(0, 0), data, 193 nservers, data_end, TRUE); 194 show_space(); 195 } 196 (void) snprintf(get_line(0, 0), get_line_remain(), 197 "%d additional record(s)", arcount); 198 additions = nservers; 199 count = 0; 200 while (arcount-- && additions < data_end) { 201 count++; 202 additions += print_answer(get_line(0, 0), data, 203 additions, data_end, TRUE); 204 show_space(); 205 } 206 } else { 207 /* question */ 208 (void) snprintf(get_line(0, 0), get_line_remain(), 209 "Query ID = %d", id); 210 (void) snprintf(get_line(0, 0), get_line_remain(), 211 "Opcode: %s", dns_opcode_string(header.opcode)); 212 (void) snprintf(get_line(0, 0), get_line_remain(), 213 "%s%s", 214 header.tc ? "TC (TrunCation) " : "", 215 header.rd ? "RD (Recursion Desired) " : ""); 216 (void) snprintf(get_line(0, 0), get_line_remain(), 217 "%d question(s)", qdcount); 218 questions = data + sizeof (dns_header); 219 count = 0; 220 while (qdcount-- && questions < data_end) { 221 count++; 222 questions += print_question(get_line(0, 0), 223 data, questions, data_end, TRUE); 224 show_space(); 225 } 226 } 227 } 228 } 229 230 231 static char * 232 dns_opcode_string(uint_t opcode) 233 { 234 static char buffer[64]; 235 switch (opcode) { 236 case ns_o_query: return ("Query"); 237 case ns_o_iquery: return ("Inverse Query"); 238 case ns_o_status: return ("Status"); 239 default: 240 (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", 241 opcode); 242 return (buffer); 243 } 244 } 245 246 static char * 247 dns_rcode_string(uint_t rcode) 248 { 249 static char buffer[64]; 250 switch (rcode) { 251 case ns_r_noerror: return ("OK"); 252 case ns_r_formerr: return ("Format Error"); 253 case ns_r_servfail: return ("Server Fail"); 254 case ns_r_nxdomain: return ("Name Error"); 255 case ns_r_notimpl: return ("Unimplemented"); 256 case ns_r_refused: return ("Refused"); 257 default: 258 (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", rcode); 259 return (buffer); 260 } 261 } 262 263 static char * 264 dns_type_string(uint_t type, int detail) 265 { 266 static char buffer[64]; 267 switch (type) { 268 case ns_t_a: return (detail ? "Address" : "Addr"); 269 case ns_t_ns: return (detail ? "Authoritative Name Server" : "NS"); 270 case ns_t_cname: return (detail ? "Canonical Name" : "CNAME"); 271 case ns_t_soa: return (detail ? "Start Of a zone Authority" : "SOA"); 272 case ns_t_mb: return (detail ? "Mailbox domain name" : "MB"); 273 case ns_t_mg: return (detail ? "Mailbox Group member" : "MG"); 274 case ns_t_mr: return (detail ? "Mail Rename domain name" : "MR"); 275 case ns_t_null: return ("NULL"); 276 case ns_t_wks: return (detail ? "Well Known Service" : "WKS"); 277 case ns_t_ptr: return (detail ? "Domain Name Pointer" : "PTR"); 278 case ns_t_hinfo: return (detail ? "Host Information": "HINFO"); 279 case ns_t_minfo: 280 return (detail ? "Mailbox or maillist Info" : "MINFO"); 281 case ns_t_mx: return (detail ? "Mail Exchange" : "MX"); 282 case ns_t_txt: return (detail ? "Text strings" : "TXT"); 283 case ns_t_aaaa: return (detail ? "IPv6 Address" : "AAAA"); 284 case ns_t_axfr: return (detail ? "Transfer of entire zone" : "AXFR"); 285 case ns_t_mailb: 286 return (detail ? "Mailbox related records" : "MAILB"); 287 case ns_t_maila: return (detail ? "Mail agent RRs" : "MAILA"); 288 case ns_t_any: return (detail ? "All records" : "*"); 289 default: 290 (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", type); 291 return (buffer); 292 } 293 } 294 295 static char * 296 dns_class_string(uint_t cls, int detail) 297 { 298 static char buffer[64]; 299 switch (cls) { 300 case ns_c_in: return (detail ? "Internet" : "Internet"); 301 case ns_c_chaos: return (detail ? "CHAOS" : "CH"); 302 case ns_c_hs: return (detail ? "Hesiod" : "HS"); 303 case ns_c_any: return (detail ? "* (Any class)" : "*"); 304 default: 305 (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", cls); 306 return (buffer); 307 } 308 } 309 310 static size_t 311 skip_question(const uchar_t *header, const uchar_t *data, 312 const uchar_t *data_end) 313 { 314 const uchar_t *data_bak = data; 315 char dummy_buffer[NS_MAXDNAME]; 316 317 data = get_domain_name(header, data, data_end, dummy_buffer, 318 dummy_buffer + sizeof (dummy_buffer)); 319 /* Skip the 32 bits of class and type that follow the domain name */ 320 data += sizeof (uint32_t); 321 return (data - data_bak); 322 } 323 324 static size_t 325 print_question(char *line, const uchar_t *header, const uchar_t *data, 326 const uchar_t *data_end, int detail) 327 { 328 const uchar_t *data_bak = data; 329 uint16_t type; 330 uint16_t cls; 331 332 if (detail) { 333 line += snprintf(line, get_line_remain(), 334 DNS_INDENT "Domain Name: "); 335 } 336 data += print_domain_name(line, header, data, data_end); 337 338 /* 339 * Make sure we don't run off the end of the packet by reading the 340 * type and class. 341 * 342 * The pointer subtraction on the left side of the following 343 * expression has a signed result of type ptrdiff_t, and the right 344 * side has an unsigned result of type size_t. We therefore need 345 * to cast the right side of the expression to be of the same 346 * signed type to keep the result of the pointer arithmetic to be 347 * automatically cast to an unsigned value. We do a similar cast 348 * in other similar expressions throughout this file. 349 */ 350 if ((data_end - data) < (ptrdiff_t)(2 * sizeof (uint16_t))) 351 return (data_end - data_bak); 352 353 GETINT16(type, data); 354 GETINT16(cls, data); 355 356 if (detail) { 357 (void) snprintf(get_line(0, 0), get_line_remain(), 358 DNS_INDENT "Class: %u (%s)", 359 cls, dns_class_string(cls, detail)); 360 (void) snprintf(get_line(0, 0), get_line_remain(), 361 DNS_INDENT "Type: %u (%s)", type, 362 dns_type_string(type, detail)); 363 } else { 364 (void) sprintf(line + strlen(line), " %s %s \?", 365 dns_class_string(cls, detail), 366 dns_type_string(type, detail)); 367 } 368 return (data - data_bak); 369 } 370 371 static size_t 372 print_answer(char *line, const uchar_t *header, const uchar_t *data, 373 const uchar_t *data_end, int detail) 374 { 375 const uchar_t *data_bak = data; 376 const uchar_t *data_next; 377 uint16_t type; 378 uint16_t cls; 379 int32_t ttl; 380 uint16_t rdlen; 381 uint32_t serial, refresh, retry, expire, minimum; 382 uint8_t protocol; 383 int linepos; 384 uint16_t preference; 385 386 if (detail) { 387 line += snprintf(line, get_line_remain(), 388 DNS_INDENT "Domain Name: "); 389 } 390 data += print_domain_name(line, header, data, data_end); 391 392 /* 393 * Make sure we don't run off the end of the packet by reading the 394 * type, class, ttl, and length. 395 */ 396 if ((data_end - data) < 397 (ptrdiff_t)(3 * sizeof (uint16_t) + sizeof (uint32_t))) { 398 return (data_end - data_bak); 399 } 400 401 GETINT16(type, data); 402 GETINT16(cls, data); 403 404 if (detail) { 405 (void) snprintf(get_line(0, 0), get_line_remain(), 406 DNS_INDENT "Class: %d (%s)", cls, 407 dns_class_string(cls, detail)); 408 (void) snprintf(get_line(0, 0), get_line_remain(), 409 DNS_INDENT "Type: %d (%s)", type, 410 dns_type_string(type, detail)); 411 } else { 412 line += strlen(line); 413 line += sprintf(line, " %s %s ", 414 dns_class_string(cls, detail), 415 dns_type_string(type, detail)); 416 } 417 418 GETINT32(ttl, data); 419 if (detail) { 420 (void) snprintf(get_line(0, 0), get_line_remain(), 421 DNS_INDENT "TTL (Time To Live): %d", ttl); 422 } 423 424 GETINT16(rdlen, data); 425 if (detail) { 426 line = get_line(0, 0); 427 line += snprintf(line, get_line_remain(), DNS_INDENT "%s: ", 428 dns_type_string(type, detail)); 429 } 430 431 if (rdlen > data_end - data) 432 return (data_end - data_bak); 433 434 switch (type) { 435 case ns_t_a: 436 print_ip(AF_INET, line, data, rdlen); 437 break; 438 case ns_t_aaaa: 439 print_ip(AF_INET6, line, data, rdlen); 440 break; 441 case ns_t_hinfo: 442 line += sprintf(line, "CPU: "); 443 data_next = data + print_char_string(line, data, rdlen); 444 if (data_next >= data_end) 445 break; 446 line += strlen(line); 447 line += sprintf(line, "OS: "); 448 (void) print_char_string(line, data_next, 449 rdlen - (data_next - data)); 450 break; 451 case ns_t_ns: 452 case ns_t_cname: 453 case ns_t_mb: 454 case ns_t_mg: 455 case ns_t_mr: 456 case ns_t_ptr: 457 (void) print_domain_name(line, header, data, data_end); 458 break; 459 case ns_t_mx: 460 data_next = data; 461 if (rdlen < sizeof (uint16_t)) 462 break; 463 GETINT16(preference, data_next); 464 if (detail) { 465 (void) print_domain_name(line, header, data_next, 466 data_end); 467 (void) snprintf(get_line(0, 0), get_line_remain(), 468 DNS_INDENT "Preference: %u", preference); 469 } else { 470 (void) print_domain_name(line, header, data_next, 471 data_end); 472 } 473 break; 474 case ns_t_soa: 475 if (!detail) 476 break; 477 line = get_line(0, 0); 478 line += snprintf(line, get_line_remain(), 479 DNS_INDENT "MNAME (Server name): "); 480 data_next = data + print_domain_name(line, header, data, 481 data_end); 482 if (data_next >= data_end) 483 break; 484 line = get_line(0, 0); 485 line += snprintf(line, get_line_remain(), 486 DNS_INDENT "RNAME (Resposible mailbox): "); 487 data_next = data_next + 488 print_domain_name(line, header, data_next, data_end); 489 if ((data_end - data_next) < (ptrdiff_t)(5 * sizeof (uint32_t))) 490 break; 491 GETINT32(serial, data_next); 492 GETINT32(refresh, data_next); 493 GETINT32(retry, data_next); 494 GETINT32(expire, data_next); 495 GETINT32(minimum, data_next); 496 (void) snprintf(get_line(0, 0), get_line_remain(), 497 DNS_INDENT "Serial: %u", serial); 498 (void) snprintf(get_line(0, 0), get_line_remain(), 499 DNS_INDENT "Refresh: %u Retry: %u " 500 "Expire: %u Minimum: %u", 501 refresh, retry, expire, minimum); 502 break; 503 case ns_t_wks: 504 print_ip(AF_INET, line, data, rdlen); 505 if (!detail) 506 break; 507 data_next = data + sizeof (in_addr_t); 508 if (data_next >= data_end) 509 break; 510 GETINT8(protocol, data_next); 511 line = get_line(0, 0); 512 line += snprintf(line, get_line_remain(), 513 DNS_INDENT "Protocol: %u ", protocol); 514 switch (protocol) { 515 case IPPROTO_UDP: 516 (void) snprintf(line, get_line_remain(), "(UDP)"); 517 break; 518 case IPPROTO_TCP: 519 (void) snprintf(line, get_line_remain(), "(TCP)"); 520 break; 521 } 522 (void) snprintf(get_line(0, 0), get_line_remain(), 523 DNS_INDENT "Service bitmap:"); 524 (void) snprintf(line, get_line_remain(), 525 DNS_INDENT "0 8 16 24"); 526 linepos = 4; 527 while (data_next < data + rdlen) { 528 if (linepos == 4) { 529 line = get_line(0, 0); 530 line += snprintf(line, get_line_remain(), 531 DNS_INDENT); 532 linepos = 0; 533 } 534 line += snprintf(line, get_line_remain(), "%s", 535 binary_string(*data_next)); 536 linepos++; 537 data_next++; 538 } 539 break; 540 case ns_t_minfo: 541 if (!detail) 542 break; 543 line = get_line(0, 0); 544 line += snprintf(line, get_line_remain(), 545 DNS_INDENT "RMAILBX (Resposible mailbox): "); 546 data_next = data + print_domain_name(line, header, data, 547 data_end); 548 line = get_line(0, 0); 549 line += snprintf(line, get_line_remain(), 550 DNS_INDENT "EMAILBX (mailbox to receive err message): "); 551 data_next = data_next + print_domain_name(line, header, 552 data_next, data_end); 553 break; 554 } 555 data += rdlen; 556 return (data - data_bak); 557 } 558 559 static char * 560 binary_string(char data) 561 { 562 static char bstring[8 + 1]; 563 char *ptr; 564 int i; 565 ptr = bstring; 566 for (i = 0; i < 8; i++) { 567 *ptr++ = (data & 0x80) ? '1' : '0'; 568 data = data << 1; 569 } 570 *ptr = (char)0; 571 return (bstring); 572 } 573 574 static void 575 print_ip(int af, char *line, const uchar_t *data, uint16_t len) 576 { 577 in6_addr_t addr6; 578 in_addr_t addr4; 579 void *addr; 580 581 switch (af) { 582 case AF_INET: 583 if (len != sizeof (in_addr_t)) 584 return; 585 addr = memcpy(&addr4, data, sizeof (addr4)); 586 break; 587 case AF_INET6: 588 if (len != sizeof (in6_addr_t)) 589 return; 590 addr = memcpy(&addr6, data, sizeof (addr6)); 591 break; 592 } 593 594 (void) inet_ntop(af, addr, line, INET6_ADDRSTRLEN); 595 } 596 597 /* 598 * charbuf is assumed to be of size MAX_CHAR_STRING_SIZE. 599 */ 600 static const uchar_t * 601 get_char_string(const uchar_t *data, char *charbuf, uint16_t datalen) 602 { 603 uint8_t len; 604 char *name = charbuf; 605 int i = 0; 606 607 /* 608 * From RFC1035, a character-string is a single length octet followed 609 * by that number of characters. 610 */ 611 if (datalen > 1) { 612 len = *data; 613 data++; 614 if (len > 0 && len < MAX_CHAR_STRING_SIZE) { 615 for (i = 0; i < len; i++, data++) 616 name[i] = *data; 617 } 618 } 619 name[i] = '\0'; 620 return (data); 621 } 622 623 static size_t 624 print_char_string(char *line, const uchar_t *data, uint16_t len) 625 { 626 char charbuf[MAX_CHAR_STRING_SIZE]; 627 const uchar_t *data_bak = data; 628 629 data = get_char_string(data, charbuf, len); 630 (void) sprintf(line, "%s", charbuf); 631 return (data - data_bak); 632 } 633 634 /* 635 * header: the entire message header, this is where we start to 636 * count the offset of the compression scheme 637 * data: the start of the domain name 638 * namebuf: user supplied buffer 639 * return: the next byte after what we have parsed 640 */ 641 static const uchar_t * 642 get_domain_name(const uchar_t *header, const uchar_t *data, 643 const uchar_t *data_end, char *namebuf, char *namend) 644 { 645 uint8_t len; 646 char *name = namebuf; 647 648 /* 649 * From RFC1035, a domain name is a sequence of labels, where each 650 * label consists of a length octet followed by that number of 651 * octets. The domain name terminates with the zero length octet 652 * for the null label of the root. 653 */ 654 655 while (name < (namend - 1)) { 656 if ((data_end - data) < (ptrdiff_t)(sizeof (uint8_t))) { 657 /* The length octet is off the end of the packet. */ 658 break; 659 } 660 GETINT8(len, data); 661 if (len == 0) { 662 /* 663 * Domain names end with a length byte of zero, 664 * which represents the null label of the root. 665 */ 666 break; 667 } 668 /* 669 * test if we are using the compression scheme 670 */ 671 if ((len & 0xc0) == 0xc0) { 672 uint16_t offset; 673 const uchar_t *label_ptr; 674 675 /* 676 * From RFC1035, message compression allows a 677 * domain name or a list of labels at the end of a 678 * domain name to be replaced with a pointer to a 679 * prior occurance of the same name. In this 680 * scheme, the pointer is a two octet sequence 681 * where the most significant two bits are set, and 682 * the remaining 14 bits are the offset from the 683 * start of the message of the next label. 684 */ 685 data--; 686 if ((data_end - data) < 687 (ptrdiff_t)(sizeof (uint16_t))) { 688 /* 689 * The offset octets aren't entirely 690 * contained within this pakcet. 691 */ 692 data = data_end; 693 break; 694 } 695 GETINT16(offset, data); 696 label_ptr = header + (offset & 0x3fff); 697 /* 698 * We must verify that the offset is valid by 699 * checking that it is less than the current data 700 * pointer and that it isn't off the end of the 701 * packet. 702 */ 703 if (label_ptr > data || label_ptr >= data_end) 704 break; 705 (void) get_domain_name(header, label_ptr, data_end, 706 name, namend); 707 return (data); 708 } else { 709 if (len > (data_end - data)) { 710 /* 711 * The label isn't entirely contained 712 * within the packet. Don't read it. The 713 * caller checks that the data pointer is 714 * not beyond the end after we've 715 * incremented it. 716 */ 717 data = data_end; 718 break; 719 } 720 while (len > 0 && name < (namend - 2)) { 721 *name = *data; 722 name++; 723 data++; 724 len--; 725 } 726 *name = '.'; 727 name++; 728 } 729 } 730 *name = '\0'; 731 return (data); 732 } 733 734 static size_t 735 print_domain_name(char *line, const uchar_t *header, const uchar_t *data, 736 const uchar_t *data_end) 737 { 738 char name[NS_MAXDNAME]; 739 const uchar_t *new_data; 740 741 new_data = get_domain_name(header, data, data_end, name, 742 name + sizeof (name)); 743 744 (void) sprintf(line, "%s", name); 745 return (new_data - data); 746 } 747