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