10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*3851Ssm26363 * Common Development and Distribution License (the "License"). 6*3851Ssm26363 * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*3851Ssm26363 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <stdio.h> 290Sstevel@tonic-gate #include <string.h> 300Sstevel@tonic-gate #include <limits.h> 310Sstevel@tonic-gate #include <sys/types.h> 320Sstevel@tonic-gate #include <sys/errno.h> 330Sstevel@tonic-gate #include <sys/tiuser.h> 340Sstevel@tonic-gate #include <arpa/nameser.h> 350Sstevel@tonic-gate #include <arpa/inet.h> 360Sstevel@tonic-gate #include <netinet/in.h> 370Sstevel@tonic-gate #include "snoop.h" 380Sstevel@tonic-gate 390Sstevel@tonic-gate /* The string used to indent detail lines */ 400Sstevel@tonic-gate #define DNS_INDENT " " 410Sstevel@tonic-gate /* 420Sstevel@tonic-gate * From RFC1035, the maximum size of a character-string is limited by the 430Sstevel@tonic-gate * one octet length field. We add one character to that to make sure the 440Sstevel@tonic-gate * result is terminated. 450Sstevel@tonic-gate */ 460Sstevel@tonic-gate #define MAX_CHAR_STRING_SIZE UCHAR_MAX + 1 470Sstevel@tonic-gate 480Sstevel@tonic-gate /* private functions */ 490Sstevel@tonic-gate static char *dns_opcode_string(uint_t opcode); 500Sstevel@tonic-gate static char *dns_rcode_string(uint_t rcode); 510Sstevel@tonic-gate static char *dns_type_string(uint_t type, int detail); 520Sstevel@tonic-gate static char *dns_class_string(uint_t cls, int detail); 530Sstevel@tonic-gate static size_t skip_question(const uchar_t *header, const uchar_t *data, 540Sstevel@tonic-gate const uchar_t *data_end); 550Sstevel@tonic-gate static size_t print_question(char *line, const uchar_t *header, 560Sstevel@tonic-gate const uchar_t *data, const uchar_t *data_end, int detail); 570Sstevel@tonic-gate static size_t print_answer(char *line, const uchar_t *header, 580Sstevel@tonic-gate const uchar_t *data, const uchar_t *data_end, int detail); 590Sstevel@tonic-gate static char *binary_string(char data); 600Sstevel@tonic-gate static void print_ip(int af, char *line, const uchar_t *data, uint16_t len); 610Sstevel@tonic-gate static const uchar_t *get_char_string(const uchar_t *data, char *charbuf, 620Sstevel@tonic-gate uint16_t datalen); 630Sstevel@tonic-gate static size_t print_char_string(char *line, const uchar_t *data, uint16_t len); 640Sstevel@tonic-gate static const uchar_t *get_domain_name(const uchar_t *header, 650Sstevel@tonic-gate const uchar_t *data, const uchar_t *data_end, char *namebuf, char *namend); 660Sstevel@tonic-gate static size_t print_domain_name(char *line, const uchar_t *header, 670Sstevel@tonic-gate const uchar_t *data, const uchar_t *data_end); 680Sstevel@tonic-gate 690Sstevel@tonic-gate void 700Sstevel@tonic-gate interpret_dns(int flags, int proto, const uchar_t *data, int len) 710Sstevel@tonic-gate { 720Sstevel@tonic-gate typedef HEADER dns_header; 730Sstevel@tonic-gate dns_header header; 740Sstevel@tonic-gate char *line; 750Sstevel@tonic-gate ushort_t id, qdcount, ancount, nscount, arcount; 760Sstevel@tonic-gate ushort_t count; 77*3851Ssm26363 const uchar_t *rrp; /* Resource Record Pointer. */ 780Sstevel@tonic-gate const uchar_t *data_end; 790Sstevel@tonic-gate 800Sstevel@tonic-gate if (proto == IPPROTO_TCP) { 810Sstevel@tonic-gate /* not supported now */ 820Sstevel@tonic-gate return; 830Sstevel@tonic-gate } 840Sstevel@tonic-gate 850Sstevel@tonic-gate /* We need at least the header in order to parse a packet. */ 860Sstevel@tonic-gate if (sizeof (dns_header) > len) { 870Sstevel@tonic-gate return; 880Sstevel@tonic-gate } 890Sstevel@tonic-gate data_end = data + len; 900Sstevel@tonic-gate /* 910Sstevel@tonic-gate * Copy the header into a local structure for aligned access to 920Sstevel@tonic-gate * each field. 930Sstevel@tonic-gate */ 940Sstevel@tonic-gate (void) memcpy(&header, data, sizeof (header)); 950Sstevel@tonic-gate id = ntohs(header.id); 960Sstevel@tonic-gate qdcount = ntohs(header.qdcount); 970Sstevel@tonic-gate ancount = ntohs(header.ancount); 980Sstevel@tonic-gate nscount = ntohs(header.nscount); 990Sstevel@tonic-gate arcount = ntohs(header.arcount); 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate if (flags & F_SUM) { 1020Sstevel@tonic-gate line = get_sum_line(); 1030Sstevel@tonic-gate line += sprintf(line, "DNS %c ", header.qr ? 'R' : 'C'); 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate if (header.qr) { 1060Sstevel@tonic-gate /* answer */ 1070Sstevel@tonic-gate if (header.rcode == 0) { 1080Sstevel@tonic-gate /* reply is OK */ 109*3851Ssm26363 rrp = data + sizeof (dns_header); 1100Sstevel@tonic-gate while (qdcount--) { 111*3851Ssm26363 if (rrp >= data_end) { 1120Sstevel@tonic-gate return; 1130Sstevel@tonic-gate } 114*3851Ssm26363 rrp += skip_question(data, 115*3851Ssm26363 rrp, data_end); 1160Sstevel@tonic-gate } 117*3851Ssm26363 /* the answers follow the questions */ 1180Sstevel@tonic-gate if (ancount > 0) { 1190Sstevel@tonic-gate (void) print_answer(line, 120*3851Ssm26363 data, rrp, data_end, FALSE); 1210Sstevel@tonic-gate } 1220Sstevel@tonic-gate } else { 1230Sstevel@tonic-gate (void) sprintf(line, " Error: %d(%s)", 1240Sstevel@tonic-gate header.rcode, 1250Sstevel@tonic-gate dns_rcode_string(header.rcode)); 1260Sstevel@tonic-gate } 1270Sstevel@tonic-gate } else { 1280Sstevel@tonic-gate /* question */ 129*3851Ssm26363 rrp = data + sizeof (dns_header); 130*3851Ssm26363 if (rrp >= data_end) { 1310Sstevel@tonic-gate return; 1320Sstevel@tonic-gate } 133*3851Ssm26363 (void) print_question(line, data, rrp, data_end, 1340Sstevel@tonic-gate FALSE); 1350Sstevel@tonic-gate } 1360Sstevel@tonic-gate } 1370Sstevel@tonic-gate if (flags & F_DTAIL) { 1380Sstevel@tonic-gate show_header("DNS: ", "DNS Header", sizeof (dns_header)); 1390Sstevel@tonic-gate show_space(); 1400Sstevel@tonic-gate if (header.qr) { 1410Sstevel@tonic-gate /* answer */ 1420Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 1430Sstevel@tonic-gate "Response ID = %d", id); 1440Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 1450Sstevel@tonic-gate "%s%s%s", 1460Sstevel@tonic-gate header.aa ? "AA (Authoritative Answer) " : "", 1470Sstevel@tonic-gate header.tc ? "TC (TrunCation) " : "", 1480Sstevel@tonic-gate header.ra ? "RA (Recursion Available) ": ""); 1490Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 1500Sstevel@tonic-gate "Response Code: %d (%s)", 1510Sstevel@tonic-gate header.rcode, dns_rcode_string(header.rcode)); 1520Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 1530Sstevel@tonic-gate "Reply to %d question(s)", qdcount); 1540Sstevel@tonic-gate } else { 1550Sstevel@tonic-gate /* question */ 1560Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 1570Sstevel@tonic-gate "Query ID = %d", id); 1580Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 1590Sstevel@tonic-gate "Opcode: %s", dns_opcode_string(header.opcode)); 1600Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 1610Sstevel@tonic-gate "%s%s", 1620Sstevel@tonic-gate header.tc ? "TC (TrunCation) " : "", 1630Sstevel@tonic-gate header.rd ? "RD (Recursion Desired) " : ""); 1640Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 1650Sstevel@tonic-gate "%d question(s)", qdcount); 166*3851Ssm26363 } 167*3851Ssm26363 rrp = data + sizeof (dns_header); 168*3851Ssm26363 count = 0; 169*3851Ssm26363 while (qdcount--) { 170*3851Ssm26363 if (rrp >= data_end) { 171*3851Ssm26363 return; 172*3851Ssm26363 } 173*3851Ssm26363 count++; 174*3851Ssm26363 rrp += print_question(get_line(0, 0), 175*3851Ssm26363 data, rrp, data_end, TRUE); 176*3851Ssm26363 show_space(); 177*3851Ssm26363 } 178*3851Ssm26363 /* Only answers should hold answers, but just in case */ 179*3851Ssm26363 if (header.qr || ancount > 0) { 180*3851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(), 181*3851Ssm26363 "%d answer(s)", ancount); 1820Sstevel@tonic-gate count = 0; 183*3851Ssm26363 while (ancount--) { 184*3851Ssm26363 if (rrp >= data_end) { 185*3851Ssm26363 return; 186*3851Ssm26363 } 1870Sstevel@tonic-gate count++; 188*3851Ssm26363 rrp += print_answer(get_line(0, 0), 189*3851Ssm26363 data, rrp, data_end, TRUE); 190*3851Ssm26363 show_space(); 191*3851Ssm26363 } 192*3851Ssm26363 } 193*3851Ssm26363 /* Likewise only answers should hold NS records */ 194*3851Ssm26363 if (header.qr || nscount > 0) { 195*3851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(), 196*3851Ssm26363 "%d name server resource(s)", nscount); 197*3851Ssm26363 count = 0; 198*3851Ssm26363 while (nscount--) { 199*3851Ssm26363 if (rrp >= data_end) { 200*3851Ssm26363 return; 201*3851Ssm26363 } 202*3851Ssm26363 count++; 203*3851Ssm26363 rrp += print_answer(get_line(0, 0), data, 204*3851Ssm26363 rrp, data_end, TRUE); 205*3851Ssm26363 show_space(); 206*3851Ssm26363 } 207*3851Ssm26363 } 208*3851Ssm26363 /* Additional section may hold an EDNS0 record. */ 209*3851Ssm26363 if (header.qr || arcount > 0) { 210*3851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(), 211*3851Ssm26363 "%d additional record(s)", arcount); 212*3851Ssm26363 count = 0; 213*3851Ssm26363 while (arcount-- && rrp < data_end) { 214*3851Ssm26363 count++; 215*3851Ssm26363 rrp += print_answer(get_line(0, 0), data, 216*3851Ssm26363 rrp, data_end, TRUE); 2170Sstevel@tonic-gate show_space(); 2180Sstevel@tonic-gate } 2190Sstevel@tonic-gate } 2200Sstevel@tonic-gate } 2210Sstevel@tonic-gate } 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate static char * 2250Sstevel@tonic-gate dns_opcode_string(uint_t opcode) 2260Sstevel@tonic-gate { 2270Sstevel@tonic-gate static char buffer[64]; 2280Sstevel@tonic-gate switch (opcode) { 2290Sstevel@tonic-gate case ns_o_query: return ("Query"); 2300Sstevel@tonic-gate case ns_o_iquery: return ("Inverse Query"); 2310Sstevel@tonic-gate case ns_o_status: return ("Status"); 2320Sstevel@tonic-gate default: 2330Sstevel@tonic-gate (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", 2340Sstevel@tonic-gate opcode); 2350Sstevel@tonic-gate return (buffer); 2360Sstevel@tonic-gate } 2370Sstevel@tonic-gate } 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate static char * 2400Sstevel@tonic-gate dns_rcode_string(uint_t rcode) 2410Sstevel@tonic-gate { 2420Sstevel@tonic-gate static char buffer[64]; 2430Sstevel@tonic-gate switch (rcode) { 2440Sstevel@tonic-gate case ns_r_noerror: return ("OK"); 2450Sstevel@tonic-gate case ns_r_formerr: return ("Format Error"); 2460Sstevel@tonic-gate case ns_r_servfail: return ("Server Fail"); 2470Sstevel@tonic-gate case ns_r_nxdomain: return ("Name Error"); 2480Sstevel@tonic-gate case ns_r_notimpl: return ("Unimplemented"); 2490Sstevel@tonic-gate case ns_r_refused: return ("Refused"); 250*3851Ssm26363 case ns_r_badvers: return ("Bad Version"); /* EDNS rcode */ 2510Sstevel@tonic-gate default: 2520Sstevel@tonic-gate (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", rcode); 2530Sstevel@tonic-gate return (buffer); 2540Sstevel@tonic-gate } 2550Sstevel@tonic-gate } 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate static char * 2580Sstevel@tonic-gate dns_type_string(uint_t type, int detail) 2590Sstevel@tonic-gate { 2600Sstevel@tonic-gate static char buffer[64]; 2610Sstevel@tonic-gate switch (type) { 2620Sstevel@tonic-gate case ns_t_a: return (detail ? "Address" : "Addr"); 2630Sstevel@tonic-gate case ns_t_ns: return (detail ? "Authoritative Name Server" : "NS"); 2640Sstevel@tonic-gate case ns_t_cname: return (detail ? "Canonical Name" : "CNAME"); 2650Sstevel@tonic-gate case ns_t_soa: return (detail ? "Start Of a zone Authority" : "SOA"); 2660Sstevel@tonic-gate case ns_t_mb: return (detail ? "Mailbox domain name" : "MB"); 2670Sstevel@tonic-gate case ns_t_mg: return (detail ? "Mailbox Group member" : "MG"); 2680Sstevel@tonic-gate case ns_t_mr: return (detail ? "Mail Rename domain name" : "MR"); 2690Sstevel@tonic-gate case ns_t_null: return ("NULL"); 2700Sstevel@tonic-gate case ns_t_wks: return (detail ? "Well Known Service" : "WKS"); 2710Sstevel@tonic-gate case ns_t_ptr: return (detail ? "Domain Name Pointer" : "PTR"); 2720Sstevel@tonic-gate case ns_t_hinfo: return (detail ? "Host Information": "HINFO"); 2730Sstevel@tonic-gate case ns_t_minfo: 2740Sstevel@tonic-gate return (detail ? "Mailbox or maillist Info" : "MINFO"); 2750Sstevel@tonic-gate case ns_t_mx: return (detail ? "Mail Exchange" : "MX"); 2760Sstevel@tonic-gate case ns_t_txt: return (detail ? "Text strings" : "TXT"); 2770Sstevel@tonic-gate case ns_t_aaaa: return (detail ? "IPv6 Address" : "AAAA"); 278*3851Ssm26363 case ns_t_opt: return (detail ? "EDNS0 option" : "OPT"); 2790Sstevel@tonic-gate case ns_t_axfr: return (detail ? "Transfer of entire zone" : "AXFR"); 2800Sstevel@tonic-gate case ns_t_mailb: 2810Sstevel@tonic-gate return (detail ? "Mailbox related records" : "MAILB"); 2820Sstevel@tonic-gate case ns_t_maila: return (detail ? "Mail agent RRs" : "MAILA"); 2830Sstevel@tonic-gate case ns_t_any: return (detail ? "All records" : "*"); 2840Sstevel@tonic-gate default: 2850Sstevel@tonic-gate (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", type); 2860Sstevel@tonic-gate return (buffer); 2870Sstevel@tonic-gate } 2880Sstevel@tonic-gate } 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate static char * 2910Sstevel@tonic-gate dns_class_string(uint_t cls, int detail) 2920Sstevel@tonic-gate { 2930Sstevel@tonic-gate static char buffer[64]; 2940Sstevel@tonic-gate switch (cls) { 2950Sstevel@tonic-gate case ns_c_in: return (detail ? "Internet" : "Internet"); 2960Sstevel@tonic-gate case ns_c_chaos: return (detail ? "CHAOS" : "CH"); 2970Sstevel@tonic-gate case ns_c_hs: return (detail ? "Hesiod" : "HS"); 2980Sstevel@tonic-gate case ns_c_any: return (detail ? "* (Any class)" : "*"); 2990Sstevel@tonic-gate default: 3000Sstevel@tonic-gate (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", cls); 3010Sstevel@tonic-gate return (buffer); 3020Sstevel@tonic-gate } 3030Sstevel@tonic-gate } 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate static size_t 3060Sstevel@tonic-gate skip_question(const uchar_t *header, const uchar_t *data, 3070Sstevel@tonic-gate const uchar_t *data_end) 3080Sstevel@tonic-gate { 3090Sstevel@tonic-gate const uchar_t *data_bak = data; 3100Sstevel@tonic-gate char dummy_buffer[NS_MAXDNAME]; 3110Sstevel@tonic-gate 3120Sstevel@tonic-gate data = get_domain_name(header, data, data_end, dummy_buffer, 3130Sstevel@tonic-gate dummy_buffer + sizeof (dummy_buffer)); 3140Sstevel@tonic-gate /* Skip the 32 bits of class and type that follow the domain name */ 3150Sstevel@tonic-gate data += sizeof (uint32_t); 3160Sstevel@tonic-gate return (data - data_bak); 3170Sstevel@tonic-gate } 3180Sstevel@tonic-gate 3190Sstevel@tonic-gate static size_t 3200Sstevel@tonic-gate print_question(char *line, const uchar_t *header, const uchar_t *data, 3210Sstevel@tonic-gate const uchar_t *data_end, int detail) 3220Sstevel@tonic-gate { 3230Sstevel@tonic-gate const uchar_t *data_bak = data; 3240Sstevel@tonic-gate uint16_t type; 3250Sstevel@tonic-gate uint16_t cls; 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate if (detail) { 3280Sstevel@tonic-gate line += snprintf(line, get_line_remain(), 3290Sstevel@tonic-gate DNS_INDENT "Domain Name: "); 3300Sstevel@tonic-gate } 3310Sstevel@tonic-gate data += print_domain_name(line, header, data, data_end); 3320Sstevel@tonic-gate 3330Sstevel@tonic-gate /* 3340Sstevel@tonic-gate * Make sure we don't run off the end of the packet by reading the 3350Sstevel@tonic-gate * type and class. 3360Sstevel@tonic-gate * 3370Sstevel@tonic-gate * The pointer subtraction on the left side of the following 3380Sstevel@tonic-gate * expression has a signed result of type ptrdiff_t, and the right 3390Sstevel@tonic-gate * side has an unsigned result of type size_t. We therefore need 3400Sstevel@tonic-gate * to cast the right side of the expression to be of the same 3410Sstevel@tonic-gate * signed type to keep the result of the pointer arithmetic to be 3420Sstevel@tonic-gate * automatically cast to an unsigned value. We do a similar cast 3430Sstevel@tonic-gate * in other similar expressions throughout this file. 3440Sstevel@tonic-gate */ 3450Sstevel@tonic-gate if ((data_end - data) < (ptrdiff_t)(2 * sizeof (uint16_t))) 3460Sstevel@tonic-gate return (data_end - data_bak); 3470Sstevel@tonic-gate 3480Sstevel@tonic-gate GETINT16(type, data); 3490Sstevel@tonic-gate GETINT16(cls, data); 3500Sstevel@tonic-gate 3510Sstevel@tonic-gate if (detail) { 3520Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 3530Sstevel@tonic-gate DNS_INDENT "Class: %u (%s)", 3540Sstevel@tonic-gate cls, dns_class_string(cls, detail)); 3550Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 3560Sstevel@tonic-gate DNS_INDENT "Type: %u (%s)", type, 3570Sstevel@tonic-gate dns_type_string(type, detail)); 3580Sstevel@tonic-gate } else { 3590Sstevel@tonic-gate (void) sprintf(line + strlen(line), " %s %s \?", 3600Sstevel@tonic-gate dns_class_string(cls, detail), 3610Sstevel@tonic-gate dns_type_string(type, detail)); 3620Sstevel@tonic-gate } 3630Sstevel@tonic-gate return (data - data_bak); 3640Sstevel@tonic-gate } 3650Sstevel@tonic-gate 366*3851Ssm26363 /* 367*3851Ssm26363 * print_answer() is used to display the contents of a single resource 368*3851Ssm26363 * record (RR) from either the answer, name server or additional 369*3851Ssm26363 * section of the DNS packet. 370*3851Ssm26363 * 371*3851Ssm26363 * Input: 372*3851Ssm26363 * *line: snoops output buffer. 373*3851Ssm26363 * *header: start of the DNS packet, required for names and rcode. 374*3851Ssm26363 * *data: location within header from where the RR starts. 375*3851Ssm26363 * *data_end: where DNS data ends. 376*3851Ssm26363 * detail: simple or verbose output. 377*3851Ssm26363 * 378*3851Ssm26363 * Returns: 379*3851Ssm26363 * Pointer to next RR or data_end. 380*3851Ssm26363 * 381*3851Ssm26363 * Most RRs have the same top level format as defined in RFC 1035: 382*3851Ssm26363 * 383*3851Ssm26363 * 1 1 1 1 1 1 384*3851Ssm26363 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 385*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 386*3851Ssm26363 * | | 387*3851Ssm26363 * / NAME / 388*3851Ssm26363 * | | 389*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 390*3851Ssm26363 * | TYPE | 391*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 392*3851Ssm26363 * | CLASS | 393*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 394*3851Ssm26363 * | TTL | 395*3851Ssm26363 * | | 396*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 397*3851Ssm26363 * | RDLENGTH | 398*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| 399*3851Ssm26363 * / RDATA / 400*3851Ssm26363 * / / 401*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 402*3851Ssm26363 * 403*3851Ssm26363 * However RFC 2671 introduced an exception to this rule 404*3851Ssm26363 * with the "Extension Mechanisms for DNS" (EDNS0). 405*3851Ssm26363 * When the type is 41 the remaining resource record format 406*3851Ssm26363 * is: 407*3851Ssm26363 * 408*3851Ssm26363 * 1 1 1 1 1 1 409*3851Ssm26363 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 410*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 411*3851Ssm26363 * | TYPE = 41 | 412*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 413*3851Ssm26363 * | Sender's UDP payload size | 414*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 415*3851Ssm26363 * | Extended-rcode | Version | 416*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 417*3851Ssm26363 * | Zero | 418*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 419*3851Ssm26363 * | RDLENGTH | 420*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| 421*3851Ssm26363 * / RDATA / 422*3851Ssm26363 * / / 423*3851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 424*3851Ssm26363 * 425*3851Ssm26363 */ 4260Sstevel@tonic-gate static size_t 4270Sstevel@tonic-gate print_answer(char *line, const uchar_t *header, const uchar_t *data, 4280Sstevel@tonic-gate const uchar_t *data_end, int detail) 4290Sstevel@tonic-gate { 4300Sstevel@tonic-gate const uchar_t *data_bak = data; 4310Sstevel@tonic-gate const uchar_t *data_next; 4320Sstevel@tonic-gate uint16_t type; 4330Sstevel@tonic-gate uint16_t cls; 4340Sstevel@tonic-gate int32_t ttl; 4350Sstevel@tonic-gate uint16_t rdlen; 4360Sstevel@tonic-gate uint32_t serial, refresh, retry, expire, minimum; 4370Sstevel@tonic-gate uint8_t protocol; 4380Sstevel@tonic-gate int linepos; 4390Sstevel@tonic-gate uint16_t preference; 440*3851Ssm26363 /* declarations for EDNS follow */ 441*3851Ssm26363 uint16_t size; /* Sender's UDP payload size */ 442*3851Ssm26363 uint8_t xrcode; /* Extended-rcode */ 443*3851Ssm26363 uint8_t ver; /* Version */ 444*3851Ssm26363 uint16_t rcode; /* Extracted from the DNS header */ 445*3851Ssm26363 union { /* DNS header overlay used for extraction */ 446*3851Ssm26363 HEADER *head; 447*3851Ssm26363 const uchar_t *raw; 448*3851Ssm26363 } headptr; 4490Sstevel@tonic-gate 4500Sstevel@tonic-gate if (detail) { 4510Sstevel@tonic-gate line += snprintf(line, get_line_remain(), 4520Sstevel@tonic-gate DNS_INDENT "Domain Name: "); 4530Sstevel@tonic-gate } 4540Sstevel@tonic-gate data += print_domain_name(line, header, data, data_end); 4550Sstevel@tonic-gate 4560Sstevel@tonic-gate /* 457*3851Ssm26363 * Next, get the record type, being careful to make sure we 458*3851Ssm26363 * don't run off the end of the packet. 4590Sstevel@tonic-gate */ 460*3851Ssm26363 if ((data_end - data) < (ptrdiff_t)(sizeof (type))) { 4610Sstevel@tonic-gate return (data_end - data_bak); 4620Sstevel@tonic-gate } 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate GETINT16(type, data); 465*3851Ssm26363 466*3851Ssm26363 if (type == ns_t_opt) { 467*3851Ssm26363 /* 468*3851Ssm26363 * Make sure we won't run off the end reading size, 469*3851Ssm26363 * xrcode, version, zero and rdlen. 470*3851Ssm26363 */ 471*3851Ssm26363 if ((data_end - data) < 472*3851Ssm26363 ((ptrdiff_t)(sizeof (size) 473*3851Ssm26363 + sizeof (xrcode) 474*3851Ssm26363 + sizeof (ver) 475*3851Ssm26363 + sizeof (cls) /* zero */ 476*3851Ssm26363 + sizeof (rdlen)))) { 477*3851Ssm26363 return (data_end - data_bak); 478*3851Ssm26363 } 479*3851Ssm26363 480*3851Ssm26363 GETINT16(size, data); 481*3851Ssm26363 GETINT8(xrcode, data); 482*3851Ssm26363 /* 483*3851Ssm26363 * The extended rcode represents the top half of the 484*3851Ssm26363 * rcode which must be added to the rcode in the header. 485*3851Ssm26363 */ 486*3851Ssm26363 rcode = 0xff & (xrcode << 4); 487*3851Ssm26363 headptr.raw = header; /* Overlay the header... */ 488*3851Ssm26363 rcode += headptr.head->rcode; /* And pluck out the rcode. */ 489*3851Ssm26363 490*3851Ssm26363 GETINT8(ver, data); 491*3851Ssm26363 GETINT16(cls, data); /* zero */ 492*3851Ssm26363 GETINT16(rdlen, data); 493*3851Ssm26363 494*3851Ssm26363 if (detail) { 495*3851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(), 496*3851Ssm26363 DNS_INDENT "Type: %u (%s)", type, 497*3851Ssm26363 dns_type_string(type, detail)); 498*3851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(), 499*3851Ssm26363 DNS_INDENT "UDP payload size: %u (0x%.4x)", 500*3851Ssm26363 size, size); 501*3851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(), 502*3851Ssm26363 DNS_INDENT "Extended rcode: %u " 503*3851Ssm26363 "(translates to %u (%s))", 504*3851Ssm26363 xrcode, rcode, dns_rcode_string(rcode)); 505*3851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(), 506*3851Ssm26363 DNS_INDENT "EDNS0 Version: %u", ver); 507*3851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(), 508*3851Ssm26363 DNS_INDENT "zero: %u", cls); 509*3851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(), 510*3851Ssm26363 DNS_INDENT "Data length: %u", rdlen); 511*3851Ssm26363 } else { 512*3851Ssm26363 line += strlen(line); 513*3851Ssm26363 line += sprintf(line, " %s UDP %u rc %d ver %u len %u", 514*3851Ssm26363 dns_type_string(type, detail), size, rcode, ver, 515*3851Ssm26363 rdlen); 516*3851Ssm26363 } 517*3851Ssm26363 518*3851Ssm26363 /* 519*3851Ssm26363 * Make sure that rdlen is within data boundary. 520*3851Ssm26363 */ 521*3851Ssm26363 if (rdlen > data_end - data) 522*3851Ssm26363 return (data_end - data_bak); 523*3851Ssm26363 524*3851Ssm26363 /* Future OPT decode code goes here. */ 525*3851Ssm26363 526*3851Ssm26363 data += rdlen; 527*3851Ssm26363 return (data - data_bak); 528*3851Ssm26363 } 529*3851Ssm26363 530*3851Ssm26363 /* 531*3851Ssm26363 * Make sure we don't run off the end of the packet by reading the 532*3851Ssm26363 * class, ttl, and length. 533*3851Ssm26363 */ 534*3851Ssm26363 if ((data_end - data) < 535*3851Ssm26363 ((ptrdiff_t)(sizeof (cls) 536*3851Ssm26363 + sizeof (ttl) 537*3851Ssm26363 + sizeof (rdlen)))) { 538*3851Ssm26363 return (data_end - data_bak); 539*3851Ssm26363 } 540*3851Ssm26363 5410Sstevel@tonic-gate GETINT16(cls, data); 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate if (detail) { 5440Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 5450Sstevel@tonic-gate DNS_INDENT "Class: %d (%s)", cls, 5460Sstevel@tonic-gate dns_class_string(cls, detail)); 5470Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 5480Sstevel@tonic-gate DNS_INDENT "Type: %d (%s)", type, 5490Sstevel@tonic-gate dns_type_string(type, detail)); 5500Sstevel@tonic-gate } else { 5510Sstevel@tonic-gate line += strlen(line); 5520Sstevel@tonic-gate line += sprintf(line, " %s %s ", 5530Sstevel@tonic-gate dns_class_string(cls, detail), 5540Sstevel@tonic-gate dns_type_string(type, detail)); 5550Sstevel@tonic-gate } 5560Sstevel@tonic-gate 5570Sstevel@tonic-gate GETINT32(ttl, data); 5580Sstevel@tonic-gate if (detail) { 5590Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 5600Sstevel@tonic-gate DNS_INDENT "TTL (Time To Live): %d", ttl); 5610Sstevel@tonic-gate } 5620Sstevel@tonic-gate 5630Sstevel@tonic-gate GETINT16(rdlen, data); 5640Sstevel@tonic-gate if (detail) { 5650Sstevel@tonic-gate line = get_line(0, 0); 5660Sstevel@tonic-gate line += snprintf(line, get_line_remain(), DNS_INDENT "%s: ", 5670Sstevel@tonic-gate dns_type_string(type, detail)); 5680Sstevel@tonic-gate } 5690Sstevel@tonic-gate 5700Sstevel@tonic-gate if (rdlen > data_end - data) 5710Sstevel@tonic-gate return (data_end - data_bak); 5720Sstevel@tonic-gate 5730Sstevel@tonic-gate switch (type) { 5740Sstevel@tonic-gate case ns_t_a: 5750Sstevel@tonic-gate print_ip(AF_INET, line, data, rdlen); 5760Sstevel@tonic-gate break; 5770Sstevel@tonic-gate case ns_t_aaaa: 5780Sstevel@tonic-gate print_ip(AF_INET6, line, data, rdlen); 5790Sstevel@tonic-gate break; 5800Sstevel@tonic-gate case ns_t_hinfo: 5810Sstevel@tonic-gate line += sprintf(line, "CPU: "); 5820Sstevel@tonic-gate data_next = data + print_char_string(line, data, rdlen); 5830Sstevel@tonic-gate if (data_next >= data_end) 5840Sstevel@tonic-gate break; 5850Sstevel@tonic-gate line += strlen(line); 5860Sstevel@tonic-gate line += sprintf(line, "OS: "); 5870Sstevel@tonic-gate (void) print_char_string(line, data_next, 5880Sstevel@tonic-gate rdlen - (data_next - data)); 5890Sstevel@tonic-gate break; 5900Sstevel@tonic-gate case ns_t_ns: 5910Sstevel@tonic-gate case ns_t_cname: 5920Sstevel@tonic-gate case ns_t_mb: 5930Sstevel@tonic-gate case ns_t_mg: 5940Sstevel@tonic-gate case ns_t_mr: 5950Sstevel@tonic-gate case ns_t_ptr: 5960Sstevel@tonic-gate (void) print_domain_name(line, header, data, data_end); 5970Sstevel@tonic-gate break; 5980Sstevel@tonic-gate case ns_t_mx: 5990Sstevel@tonic-gate data_next = data; 6000Sstevel@tonic-gate if (rdlen < sizeof (uint16_t)) 6010Sstevel@tonic-gate break; 6020Sstevel@tonic-gate GETINT16(preference, data_next); 6030Sstevel@tonic-gate if (detail) { 6040Sstevel@tonic-gate (void) print_domain_name(line, header, data_next, 6050Sstevel@tonic-gate data_end); 6060Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 6070Sstevel@tonic-gate DNS_INDENT "Preference: %u", preference); 6080Sstevel@tonic-gate } else { 6090Sstevel@tonic-gate (void) print_domain_name(line, header, data_next, 6100Sstevel@tonic-gate data_end); 6110Sstevel@tonic-gate } 6120Sstevel@tonic-gate break; 6130Sstevel@tonic-gate case ns_t_soa: 6140Sstevel@tonic-gate if (!detail) 6150Sstevel@tonic-gate break; 6160Sstevel@tonic-gate line = get_line(0, 0); 6170Sstevel@tonic-gate line += snprintf(line, get_line_remain(), 6180Sstevel@tonic-gate DNS_INDENT "MNAME (Server name): "); 6190Sstevel@tonic-gate data_next = data + print_domain_name(line, header, data, 6200Sstevel@tonic-gate data_end); 6210Sstevel@tonic-gate if (data_next >= data_end) 6220Sstevel@tonic-gate break; 6230Sstevel@tonic-gate line = get_line(0, 0); 6240Sstevel@tonic-gate line += snprintf(line, get_line_remain(), 6250Sstevel@tonic-gate DNS_INDENT "RNAME (Resposible mailbox): "); 6260Sstevel@tonic-gate data_next = data_next + 6270Sstevel@tonic-gate print_domain_name(line, header, data_next, data_end); 6280Sstevel@tonic-gate if ((data_end - data_next) < (ptrdiff_t)(5 * sizeof (uint32_t))) 6290Sstevel@tonic-gate break; 6300Sstevel@tonic-gate GETINT32(serial, data_next); 6310Sstevel@tonic-gate GETINT32(refresh, data_next); 6320Sstevel@tonic-gate GETINT32(retry, data_next); 6330Sstevel@tonic-gate GETINT32(expire, data_next); 6340Sstevel@tonic-gate GETINT32(minimum, data_next); 6350Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 6360Sstevel@tonic-gate DNS_INDENT "Serial: %u", serial); 6370Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 6380Sstevel@tonic-gate DNS_INDENT "Refresh: %u Retry: %u " 6390Sstevel@tonic-gate "Expire: %u Minimum: %u", 6400Sstevel@tonic-gate refresh, retry, expire, minimum); 6410Sstevel@tonic-gate break; 6420Sstevel@tonic-gate case ns_t_wks: 6430Sstevel@tonic-gate print_ip(AF_INET, line, data, rdlen); 6440Sstevel@tonic-gate if (!detail) 6450Sstevel@tonic-gate break; 6460Sstevel@tonic-gate data_next = data + sizeof (in_addr_t); 6470Sstevel@tonic-gate if (data_next >= data_end) 6480Sstevel@tonic-gate break; 6490Sstevel@tonic-gate GETINT8(protocol, data_next); 6500Sstevel@tonic-gate line = get_line(0, 0); 6510Sstevel@tonic-gate line += snprintf(line, get_line_remain(), 6520Sstevel@tonic-gate DNS_INDENT "Protocol: %u ", protocol); 6530Sstevel@tonic-gate switch (protocol) { 6540Sstevel@tonic-gate case IPPROTO_UDP: 6550Sstevel@tonic-gate (void) snprintf(line, get_line_remain(), "(UDP)"); 6560Sstevel@tonic-gate break; 6570Sstevel@tonic-gate case IPPROTO_TCP: 6580Sstevel@tonic-gate (void) snprintf(line, get_line_remain(), "(TCP)"); 6590Sstevel@tonic-gate break; 6600Sstevel@tonic-gate } 6610Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(), 6620Sstevel@tonic-gate DNS_INDENT "Service bitmap:"); 6630Sstevel@tonic-gate (void) snprintf(line, get_line_remain(), 6640Sstevel@tonic-gate DNS_INDENT "0 8 16 24"); 6650Sstevel@tonic-gate linepos = 4; 6660Sstevel@tonic-gate while (data_next < data + rdlen) { 6670Sstevel@tonic-gate if (linepos == 4) { 6680Sstevel@tonic-gate line = get_line(0, 0); 6690Sstevel@tonic-gate line += snprintf(line, get_line_remain(), 6700Sstevel@tonic-gate DNS_INDENT); 6710Sstevel@tonic-gate linepos = 0; 6720Sstevel@tonic-gate } 6730Sstevel@tonic-gate line += snprintf(line, get_line_remain(), "%s", 6740Sstevel@tonic-gate binary_string(*data_next)); 6750Sstevel@tonic-gate linepos++; 6760Sstevel@tonic-gate data_next++; 6770Sstevel@tonic-gate } 6780Sstevel@tonic-gate break; 6790Sstevel@tonic-gate case ns_t_minfo: 6800Sstevel@tonic-gate if (!detail) 6810Sstevel@tonic-gate break; 6820Sstevel@tonic-gate line = get_line(0, 0); 6830Sstevel@tonic-gate line += snprintf(line, get_line_remain(), 6840Sstevel@tonic-gate DNS_INDENT "RMAILBX (Resposible mailbox): "); 6850Sstevel@tonic-gate data_next = data + print_domain_name(line, header, data, 6860Sstevel@tonic-gate data_end); 6870Sstevel@tonic-gate line = get_line(0, 0); 6880Sstevel@tonic-gate line += snprintf(line, get_line_remain(), 6890Sstevel@tonic-gate DNS_INDENT "EMAILBX (mailbox to receive err message): "); 6900Sstevel@tonic-gate data_next = data_next + print_domain_name(line, header, 6910Sstevel@tonic-gate data_next, data_end); 6920Sstevel@tonic-gate break; 6930Sstevel@tonic-gate } 6940Sstevel@tonic-gate data += rdlen; 6950Sstevel@tonic-gate return (data - data_bak); 6960Sstevel@tonic-gate } 6970Sstevel@tonic-gate 6980Sstevel@tonic-gate static char * 6990Sstevel@tonic-gate binary_string(char data) 7000Sstevel@tonic-gate { 7010Sstevel@tonic-gate static char bstring[8 + 1]; 7020Sstevel@tonic-gate char *ptr; 7030Sstevel@tonic-gate int i; 7040Sstevel@tonic-gate ptr = bstring; 7050Sstevel@tonic-gate for (i = 0; i < 8; i++) { 7060Sstevel@tonic-gate *ptr++ = (data & 0x80) ? '1' : '0'; 7070Sstevel@tonic-gate data = data << 1; 7080Sstevel@tonic-gate } 7090Sstevel@tonic-gate *ptr = (char)0; 7100Sstevel@tonic-gate return (bstring); 7110Sstevel@tonic-gate } 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate static void 7140Sstevel@tonic-gate print_ip(int af, char *line, const uchar_t *data, uint16_t len) 7150Sstevel@tonic-gate { 7160Sstevel@tonic-gate in6_addr_t addr6; 7170Sstevel@tonic-gate in_addr_t addr4; 7180Sstevel@tonic-gate void *addr; 7190Sstevel@tonic-gate 7200Sstevel@tonic-gate switch (af) { 7210Sstevel@tonic-gate case AF_INET: 7220Sstevel@tonic-gate if (len != sizeof (in_addr_t)) 7230Sstevel@tonic-gate return; 7240Sstevel@tonic-gate addr = memcpy(&addr4, data, sizeof (addr4)); 7250Sstevel@tonic-gate break; 7260Sstevel@tonic-gate case AF_INET6: 7270Sstevel@tonic-gate if (len != sizeof (in6_addr_t)) 7280Sstevel@tonic-gate return; 7290Sstevel@tonic-gate addr = memcpy(&addr6, data, sizeof (addr6)); 7300Sstevel@tonic-gate break; 7310Sstevel@tonic-gate } 7320Sstevel@tonic-gate 7330Sstevel@tonic-gate (void) inet_ntop(af, addr, line, INET6_ADDRSTRLEN); 7340Sstevel@tonic-gate } 7350Sstevel@tonic-gate 7360Sstevel@tonic-gate /* 7370Sstevel@tonic-gate * charbuf is assumed to be of size MAX_CHAR_STRING_SIZE. 7380Sstevel@tonic-gate */ 7390Sstevel@tonic-gate static const uchar_t * 7400Sstevel@tonic-gate get_char_string(const uchar_t *data, char *charbuf, uint16_t datalen) 7410Sstevel@tonic-gate { 742410Skcpoon int len; 7430Sstevel@tonic-gate char *name = charbuf; 7440Sstevel@tonic-gate int i = 0; 7450Sstevel@tonic-gate 7460Sstevel@tonic-gate /* 7470Sstevel@tonic-gate * From RFC1035, a character-string is a single length octet followed 7480Sstevel@tonic-gate * by that number of characters. 7490Sstevel@tonic-gate */ 7500Sstevel@tonic-gate if (datalen > 1) { 7510Sstevel@tonic-gate len = *data; 7520Sstevel@tonic-gate data++; 7530Sstevel@tonic-gate if (len > 0 && len < MAX_CHAR_STRING_SIZE) { 7540Sstevel@tonic-gate for (i = 0; i < len; i++, data++) 7550Sstevel@tonic-gate name[i] = *data; 7560Sstevel@tonic-gate } 7570Sstevel@tonic-gate } 7580Sstevel@tonic-gate name[i] = '\0'; 7590Sstevel@tonic-gate return (data); 7600Sstevel@tonic-gate } 7610Sstevel@tonic-gate 7620Sstevel@tonic-gate static size_t 7630Sstevel@tonic-gate print_char_string(char *line, const uchar_t *data, uint16_t len) 7640Sstevel@tonic-gate { 7650Sstevel@tonic-gate char charbuf[MAX_CHAR_STRING_SIZE]; 7660Sstevel@tonic-gate const uchar_t *data_bak = data; 7670Sstevel@tonic-gate 7680Sstevel@tonic-gate data = get_char_string(data, charbuf, len); 7690Sstevel@tonic-gate (void) sprintf(line, "%s", charbuf); 7700Sstevel@tonic-gate return (data - data_bak); 7710Sstevel@tonic-gate } 7720Sstevel@tonic-gate 7730Sstevel@tonic-gate /* 7740Sstevel@tonic-gate * header: the entire message header, this is where we start to 7750Sstevel@tonic-gate * count the offset of the compression scheme 7760Sstevel@tonic-gate * data: the start of the domain name 7770Sstevel@tonic-gate * namebuf: user supplied buffer 7780Sstevel@tonic-gate * return: the next byte after what we have parsed 7790Sstevel@tonic-gate */ 7800Sstevel@tonic-gate static const uchar_t * 7810Sstevel@tonic-gate get_domain_name(const uchar_t *header, const uchar_t *data, 7820Sstevel@tonic-gate const uchar_t *data_end, char *namebuf, char *namend) 7830Sstevel@tonic-gate { 7840Sstevel@tonic-gate uint8_t len; 7850Sstevel@tonic-gate char *name = namebuf; 7860Sstevel@tonic-gate 7870Sstevel@tonic-gate /* 7880Sstevel@tonic-gate * From RFC1035, a domain name is a sequence of labels, where each 7890Sstevel@tonic-gate * label consists of a length octet followed by that number of 7900Sstevel@tonic-gate * octets. The domain name terminates with the zero length octet 7910Sstevel@tonic-gate * for the null label of the root. 7920Sstevel@tonic-gate */ 7930Sstevel@tonic-gate 7940Sstevel@tonic-gate while (name < (namend - 1)) { 7950Sstevel@tonic-gate if ((data_end - data) < (ptrdiff_t)(sizeof (uint8_t))) { 7960Sstevel@tonic-gate /* The length octet is off the end of the packet. */ 7970Sstevel@tonic-gate break; 7980Sstevel@tonic-gate } 7990Sstevel@tonic-gate GETINT8(len, data); 8000Sstevel@tonic-gate if (len == 0) { 8010Sstevel@tonic-gate /* 8020Sstevel@tonic-gate * Domain names end with a length byte of zero, 8030Sstevel@tonic-gate * which represents the null label of the root. 8040Sstevel@tonic-gate */ 8050Sstevel@tonic-gate break; 8060Sstevel@tonic-gate } 8070Sstevel@tonic-gate /* 8080Sstevel@tonic-gate * test if we are using the compression scheme 8090Sstevel@tonic-gate */ 8100Sstevel@tonic-gate if ((len & 0xc0) == 0xc0) { 8110Sstevel@tonic-gate uint16_t offset; 8120Sstevel@tonic-gate const uchar_t *label_ptr; 8130Sstevel@tonic-gate 8140Sstevel@tonic-gate /* 8150Sstevel@tonic-gate * From RFC1035, message compression allows a 8160Sstevel@tonic-gate * domain name or a list of labels at the end of a 8170Sstevel@tonic-gate * domain name to be replaced with a pointer to a 8180Sstevel@tonic-gate * prior occurance of the same name. In this 8190Sstevel@tonic-gate * scheme, the pointer is a two octet sequence 8200Sstevel@tonic-gate * where the most significant two bits are set, and 8210Sstevel@tonic-gate * the remaining 14 bits are the offset from the 8220Sstevel@tonic-gate * start of the message of the next label. 8230Sstevel@tonic-gate */ 8240Sstevel@tonic-gate data--; 8250Sstevel@tonic-gate if ((data_end - data) < 8260Sstevel@tonic-gate (ptrdiff_t)(sizeof (uint16_t))) { 8270Sstevel@tonic-gate /* 8280Sstevel@tonic-gate * The offset octets aren't entirely 8290Sstevel@tonic-gate * contained within this pakcet. 8300Sstevel@tonic-gate */ 8310Sstevel@tonic-gate data = data_end; 8320Sstevel@tonic-gate break; 8330Sstevel@tonic-gate } 8340Sstevel@tonic-gate GETINT16(offset, data); 8350Sstevel@tonic-gate label_ptr = header + (offset & 0x3fff); 8360Sstevel@tonic-gate /* 8370Sstevel@tonic-gate * We must verify that the offset is valid by 8380Sstevel@tonic-gate * checking that it is less than the current data 8390Sstevel@tonic-gate * pointer and that it isn't off the end of the 8400Sstevel@tonic-gate * packet. 8410Sstevel@tonic-gate */ 8420Sstevel@tonic-gate if (label_ptr > data || label_ptr >= data_end) 8430Sstevel@tonic-gate break; 8440Sstevel@tonic-gate (void) get_domain_name(header, label_ptr, data_end, 8450Sstevel@tonic-gate name, namend); 8460Sstevel@tonic-gate return (data); 8470Sstevel@tonic-gate } else { 8480Sstevel@tonic-gate if (len > (data_end - data)) { 8490Sstevel@tonic-gate /* 8500Sstevel@tonic-gate * The label isn't entirely contained 8510Sstevel@tonic-gate * within the packet. Don't read it. The 8520Sstevel@tonic-gate * caller checks that the data pointer is 8530Sstevel@tonic-gate * not beyond the end after we've 8540Sstevel@tonic-gate * incremented it. 8550Sstevel@tonic-gate */ 8560Sstevel@tonic-gate data = data_end; 8570Sstevel@tonic-gate break; 8580Sstevel@tonic-gate } 8590Sstevel@tonic-gate while (len > 0 && name < (namend - 2)) { 8600Sstevel@tonic-gate *name = *data; 8610Sstevel@tonic-gate name++; 8620Sstevel@tonic-gate data++; 8630Sstevel@tonic-gate len--; 8640Sstevel@tonic-gate } 8650Sstevel@tonic-gate *name = '.'; 8660Sstevel@tonic-gate name++; 8670Sstevel@tonic-gate } 8680Sstevel@tonic-gate } 8690Sstevel@tonic-gate *name = '\0'; 8700Sstevel@tonic-gate return (data); 8710Sstevel@tonic-gate } 8720Sstevel@tonic-gate 8730Sstevel@tonic-gate static size_t 8740Sstevel@tonic-gate print_domain_name(char *line, const uchar_t *header, const uchar_t *data, 8750Sstevel@tonic-gate const uchar_t *data_end) 8760Sstevel@tonic-gate { 8770Sstevel@tonic-gate char name[NS_MAXDNAME]; 8780Sstevel@tonic-gate const uchar_t *new_data; 8790Sstevel@tonic-gate 8800Sstevel@tonic-gate new_data = get_domain_name(header, data, data_end, name, 8810Sstevel@tonic-gate name + sizeof (name)); 8820Sstevel@tonic-gate 8830Sstevel@tonic-gate (void) sprintf(line, "%s", name); 8840Sstevel@tonic-gate return (new_data - data); 8850Sstevel@tonic-gate } 886