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