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
53851Ssm26363 * Common Development and Distribution License (the "License").
63851Ssm26363 * 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 /*
223851Ssm26363 * 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
interpret_dns(int flags,int proto,const uchar_t * data,int len,int port)70*4904Srs200217 interpret_dns(int flags, int proto, const uchar_t *data, int len, int port)
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;
773851Ssm26363 const uchar_t *rrp; /* Resource Record Pointer. */
780Sstevel@tonic-gate const uchar_t *data_end;
79*4904Srs200217 const char *protostr;
80*4904Srs200217 char *protopfxstr;
81*4904Srs200217 char *protohdrstr;
820Sstevel@tonic-gate
830Sstevel@tonic-gate if (proto == IPPROTO_TCP) {
840Sstevel@tonic-gate /* not supported now */
850Sstevel@tonic-gate return;
860Sstevel@tonic-gate }
870Sstevel@tonic-gate
88*4904Srs200217 if (port == IPPORT_DOMAIN) {
89*4904Srs200217 protostr = "DNS";
90*4904Srs200217 protopfxstr = "DNS: ";
91*4904Srs200217 protohdrstr = "DNS Header";
92*4904Srs200217 } else {
93*4904Srs200217 protostr = "MDNS";
94*4904Srs200217 protopfxstr = "MDNS: ";
95*4904Srs200217 protohdrstr = "MDNS Header";
96*4904Srs200217 }
97*4904Srs200217
980Sstevel@tonic-gate /* We need at least the header in order to parse a packet. */
990Sstevel@tonic-gate if (sizeof (dns_header) > len) {
1000Sstevel@tonic-gate return;
1010Sstevel@tonic-gate }
1020Sstevel@tonic-gate data_end = data + len;
1030Sstevel@tonic-gate /*
1040Sstevel@tonic-gate * Copy the header into a local structure for aligned access to
1050Sstevel@tonic-gate * each field.
1060Sstevel@tonic-gate */
1070Sstevel@tonic-gate (void) memcpy(&header, data, sizeof (header));
1080Sstevel@tonic-gate id = ntohs(header.id);
1090Sstevel@tonic-gate qdcount = ntohs(header.qdcount);
1100Sstevel@tonic-gate ancount = ntohs(header.ancount);
1110Sstevel@tonic-gate nscount = ntohs(header.nscount);
1120Sstevel@tonic-gate arcount = ntohs(header.arcount);
1130Sstevel@tonic-gate
1140Sstevel@tonic-gate if (flags & F_SUM) {
1150Sstevel@tonic-gate line = get_sum_line();
116*4904Srs200217 line += sprintf(line, "%s %c ",
117*4904Srs200217 protostr, header.qr ? 'R' : 'C');
1180Sstevel@tonic-gate
1190Sstevel@tonic-gate if (header.qr) {
1200Sstevel@tonic-gate /* answer */
1210Sstevel@tonic-gate if (header.rcode == 0) {
1220Sstevel@tonic-gate /* reply is OK */
1233851Ssm26363 rrp = data + sizeof (dns_header);
1240Sstevel@tonic-gate while (qdcount--) {
1253851Ssm26363 if (rrp >= data_end) {
1260Sstevel@tonic-gate return;
1270Sstevel@tonic-gate }
1283851Ssm26363 rrp += skip_question(data,
1293851Ssm26363 rrp, data_end);
1300Sstevel@tonic-gate }
1313851Ssm26363 /* the answers follow the questions */
1320Sstevel@tonic-gate if (ancount > 0) {
1330Sstevel@tonic-gate (void) print_answer(line,
1343851Ssm26363 data, rrp, data_end, FALSE);
1350Sstevel@tonic-gate }
1360Sstevel@tonic-gate } else {
1370Sstevel@tonic-gate (void) sprintf(line, " Error: %d(%s)",
1380Sstevel@tonic-gate header.rcode,
1390Sstevel@tonic-gate dns_rcode_string(header.rcode));
1400Sstevel@tonic-gate }
1410Sstevel@tonic-gate } else {
1420Sstevel@tonic-gate /* question */
1433851Ssm26363 rrp = data + sizeof (dns_header);
1443851Ssm26363 if (rrp >= data_end) {
1450Sstevel@tonic-gate return;
1460Sstevel@tonic-gate }
1473851Ssm26363 (void) print_question(line, data, rrp, data_end,
1480Sstevel@tonic-gate FALSE);
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate }
1510Sstevel@tonic-gate if (flags & F_DTAIL) {
152*4904Srs200217 show_header(protopfxstr, protohdrstr, sizeof (dns_header));
1530Sstevel@tonic-gate show_space();
1540Sstevel@tonic-gate if (header.qr) {
1550Sstevel@tonic-gate /* answer */
1560Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
1570Sstevel@tonic-gate "Response ID = %d", id);
1580Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
1590Sstevel@tonic-gate "%s%s%s",
1600Sstevel@tonic-gate header.aa ? "AA (Authoritative Answer) " : "",
1610Sstevel@tonic-gate header.tc ? "TC (TrunCation) " : "",
1620Sstevel@tonic-gate header.ra ? "RA (Recursion Available) ": "");
1630Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
1640Sstevel@tonic-gate "Response Code: %d (%s)",
1650Sstevel@tonic-gate header.rcode, dns_rcode_string(header.rcode));
1660Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
1670Sstevel@tonic-gate "Reply to %d question(s)", qdcount);
1680Sstevel@tonic-gate } else {
1690Sstevel@tonic-gate /* question */
1700Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
1710Sstevel@tonic-gate "Query ID = %d", id);
1720Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
1730Sstevel@tonic-gate "Opcode: %s", dns_opcode_string(header.opcode));
1740Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
1750Sstevel@tonic-gate "%s%s",
1760Sstevel@tonic-gate header.tc ? "TC (TrunCation) " : "",
1770Sstevel@tonic-gate header.rd ? "RD (Recursion Desired) " : "");
1780Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
1790Sstevel@tonic-gate "%d question(s)", qdcount);
1803851Ssm26363 }
1813851Ssm26363 rrp = data + sizeof (dns_header);
1823851Ssm26363 count = 0;
1833851Ssm26363 while (qdcount--) {
1843851Ssm26363 if (rrp >= data_end) {
1853851Ssm26363 return;
1863851Ssm26363 }
1873851Ssm26363 count++;
1883851Ssm26363 rrp += print_question(get_line(0, 0),
1893851Ssm26363 data, rrp, data_end, TRUE);
1903851Ssm26363 show_space();
1913851Ssm26363 }
1923851Ssm26363 /* Only answers should hold answers, but just in case */
1933851Ssm26363 if (header.qr || ancount > 0) {
1943851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(),
1953851Ssm26363 "%d answer(s)", ancount);
1960Sstevel@tonic-gate count = 0;
1973851Ssm26363 while (ancount--) {
1983851Ssm26363 if (rrp >= data_end) {
1993851Ssm26363 return;
2003851Ssm26363 }
2010Sstevel@tonic-gate count++;
2023851Ssm26363 rrp += print_answer(get_line(0, 0),
2033851Ssm26363 data, rrp, data_end, TRUE);
2043851Ssm26363 show_space();
2053851Ssm26363 }
2063851Ssm26363 }
2073851Ssm26363 /* Likewise only answers should hold NS records */
2083851Ssm26363 if (header.qr || nscount > 0) {
2093851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(),
2103851Ssm26363 "%d name server resource(s)", nscount);
2113851Ssm26363 count = 0;
2123851Ssm26363 while (nscount--) {
2133851Ssm26363 if (rrp >= data_end) {
2143851Ssm26363 return;
2153851Ssm26363 }
2163851Ssm26363 count++;
2173851Ssm26363 rrp += print_answer(get_line(0, 0), data,
2183851Ssm26363 rrp, data_end, TRUE);
2193851Ssm26363 show_space();
2203851Ssm26363 }
2213851Ssm26363 }
2223851Ssm26363 /* Additional section may hold an EDNS0 record. */
2233851Ssm26363 if (header.qr || arcount > 0) {
2243851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(),
2253851Ssm26363 "%d additional record(s)", arcount);
2263851Ssm26363 count = 0;
2273851Ssm26363 while (arcount-- && rrp < data_end) {
2283851Ssm26363 count++;
2293851Ssm26363 rrp += print_answer(get_line(0, 0), data,
2303851Ssm26363 rrp, data_end, TRUE);
2310Sstevel@tonic-gate show_space();
2320Sstevel@tonic-gate }
2330Sstevel@tonic-gate }
2340Sstevel@tonic-gate }
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate
2370Sstevel@tonic-gate
2380Sstevel@tonic-gate static char *
dns_opcode_string(uint_t opcode)2390Sstevel@tonic-gate dns_opcode_string(uint_t opcode)
2400Sstevel@tonic-gate {
2410Sstevel@tonic-gate static char buffer[64];
2420Sstevel@tonic-gate switch (opcode) {
2430Sstevel@tonic-gate case ns_o_query: return ("Query");
2440Sstevel@tonic-gate case ns_o_iquery: return ("Inverse Query");
2450Sstevel@tonic-gate case ns_o_status: return ("Status");
2460Sstevel@tonic-gate default:
2470Sstevel@tonic-gate (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)",
2480Sstevel@tonic-gate opcode);
2490Sstevel@tonic-gate return (buffer);
2500Sstevel@tonic-gate }
2510Sstevel@tonic-gate }
2520Sstevel@tonic-gate
2530Sstevel@tonic-gate static char *
dns_rcode_string(uint_t rcode)2540Sstevel@tonic-gate dns_rcode_string(uint_t rcode)
2550Sstevel@tonic-gate {
2560Sstevel@tonic-gate static char buffer[64];
2570Sstevel@tonic-gate switch (rcode) {
2580Sstevel@tonic-gate case ns_r_noerror: return ("OK");
2590Sstevel@tonic-gate case ns_r_formerr: return ("Format Error");
2600Sstevel@tonic-gate case ns_r_servfail: return ("Server Fail");
2610Sstevel@tonic-gate case ns_r_nxdomain: return ("Name Error");
2620Sstevel@tonic-gate case ns_r_notimpl: return ("Unimplemented");
2630Sstevel@tonic-gate case ns_r_refused: return ("Refused");
2643851Ssm26363 case ns_r_badvers: return ("Bad Version"); /* EDNS rcode */
2650Sstevel@tonic-gate default:
2660Sstevel@tonic-gate (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", rcode);
2670Sstevel@tonic-gate return (buffer);
2680Sstevel@tonic-gate }
2690Sstevel@tonic-gate }
2700Sstevel@tonic-gate
2710Sstevel@tonic-gate static char *
dns_type_string(uint_t type,int detail)2720Sstevel@tonic-gate dns_type_string(uint_t type, int detail)
2730Sstevel@tonic-gate {
2740Sstevel@tonic-gate static char buffer[64];
2750Sstevel@tonic-gate switch (type) {
2760Sstevel@tonic-gate case ns_t_a: return (detail ? "Address" : "Addr");
2770Sstevel@tonic-gate case ns_t_ns: return (detail ? "Authoritative Name Server" : "NS");
2780Sstevel@tonic-gate case ns_t_cname: return (detail ? "Canonical Name" : "CNAME");
2790Sstevel@tonic-gate case ns_t_soa: return (detail ? "Start Of a zone Authority" : "SOA");
2800Sstevel@tonic-gate case ns_t_mb: return (detail ? "Mailbox domain name" : "MB");
2810Sstevel@tonic-gate case ns_t_mg: return (detail ? "Mailbox Group member" : "MG");
2820Sstevel@tonic-gate case ns_t_mr: return (detail ? "Mail Rename domain name" : "MR");
2830Sstevel@tonic-gate case ns_t_null: return ("NULL");
2840Sstevel@tonic-gate case ns_t_wks: return (detail ? "Well Known Service" : "WKS");
2850Sstevel@tonic-gate case ns_t_ptr: return (detail ? "Domain Name Pointer" : "PTR");
2860Sstevel@tonic-gate case ns_t_hinfo: return (detail ? "Host Information": "HINFO");
2870Sstevel@tonic-gate case ns_t_minfo:
2880Sstevel@tonic-gate return (detail ? "Mailbox or maillist Info" : "MINFO");
2890Sstevel@tonic-gate case ns_t_mx: return (detail ? "Mail Exchange" : "MX");
2900Sstevel@tonic-gate case ns_t_txt: return (detail ? "Text strings" : "TXT");
2910Sstevel@tonic-gate case ns_t_aaaa: return (detail ? "IPv6 Address" : "AAAA");
2923851Ssm26363 case ns_t_opt: return (detail ? "EDNS0 option" : "OPT");
2930Sstevel@tonic-gate case ns_t_axfr: return (detail ? "Transfer of entire zone" : "AXFR");
2940Sstevel@tonic-gate case ns_t_mailb:
2950Sstevel@tonic-gate return (detail ? "Mailbox related records" : "MAILB");
2960Sstevel@tonic-gate case ns_t_maila: return (detail ? "Mail agent RRs" : "MAILA");
2970Sstevel@tonic-gate case ns_t_any: return (detail ? "All records" : "*");
2980Sstevel@tonic-gate default:
2990Sstevel@tonic-gate (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", type);
3000Sstevel@tonic-gate return (buffer);
3010Sstevel@tonic-gate }
3020Sstevel@tonic-gate }
3030Sstevel@tonic-gate
3040Sstevel@tonic-gate static char *
dns_class_string(uint_t cls,int detail)3050Sstevel@tonic-gate dns_class_string(uint_t cls, int detail)
3060Sstevel@tonic-gate {
3070Sstevel@tonic-gate static char buffer[64];
3080Sstevel@tonic-gate switch (cls) {
3090Sstevel@tonic-gate case ns_c_in: return (detail ? "Internet" : "Internet");
3100Sstevel@tonic-gate case ns_c_chaos: return (detail ? "CHAOS" : "CH");
3110Sstevel@tonic-gate case ns_c_hs: return (detail ? "Hesiod" : "HS");
3120Sstevel@tonic-gate case ns_c_any: return (detail ? "* (Any class)" : "*");
3130Sstevel@tonic-gate default:
3140Sstevel@tonic-gate (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", cls);
3150Sstevel@tonic-gate return (buffer);
3160Sstevel@tonic-gate }
3170Sstevel@tonic-gate }
3180Sstevel@tonic-gate
3190Sstevel@tonic-gate static size_t
skip_question(const uchar_t * header,const uchar_t * data,const uchar_t * data_end)3200Sstevel@tonic-gate skip_question(const uchar_t *header, const uchar_t *data,
3210Sstevel@tonic-gate const uchar_t *data_end)
3220Sstevel@tonic-gate {
3230Sstevel@tonic-gate const uchar_t *data_bak = data;
3240Sstevel@tonic-gate char dummy_buffer[NS_MAXDNAME];
3250Sstevel@tonic-gate
3260Sstevel@tonic-gate data = get_domain_name(header, data, data_end, dummy_buffer,
3270Sstevel@tonic-gate dummy_buffer + sizeof (dummy_buffer));
3280Sstevel@tonic-gate /* Skip the 32 bits of class and type that follow the domain name */
3290Sstevel@tonic-gate data += sizeof (uint32_t);
3300Sstevel@tonic-gate return (data - data_bak);
3310Sstevel@tonic-gate }
3320Sstevel@tonic-gate
3330Sstevel@tonic-gate static size_t
print_question(char * line,const uchar_t * header,const uchar_t * data,const uchar_t * data_end,int detail)3340Sstevel@tonic-gate print_question(char *line, const uchar_t *header, const uchar_t *data,
3350Sstevel@tonic-gate const uchar_t *data_end, int detail)
3360Sstevel@tonic-gate {
3370Sstevel@tonic-gate const uchar_t *data_bak = data;
3380Sstevel@tonic-gate uint16_t type;
3390Sstevel@tonic-gate uint16_t cls;
3400Sstevel@tonic-gate
3410Sstevel@tonic-gate if (detail) {
3420Sstevel@tonic-gate line += snprintf(line, get_line_remain(),
3430Sstevel@tonic-gate DNS_INDENT "Domain Name: ");
3440Sstevel@tonic-gate }
3450Sstevel@tonic-gate data += print_domain_name(line, header, data, data_end);
3460Sstevel@tonic-gate
3470Sstevel@tonic-gate /*
3480Sstevel@tonic-gate * Make sure we don't run off the end of the packet by reading the
3490Sstevel@tonic-gate * type and class.
3500Sstevel@tonic-gate *
3510Sstevel@tonic-gate * The pointer subtraction on the left side of the following
3520Sstevel@tonic-gate * expression has a signed result of type ptrdiff_t, and the right
3530Sstevel@tonic-gate * side has an unsigned result of type size_t. We therefore need
3540Sstevel@tonic-gate * to cast the right side of the expression to be of the same
3550Sstevel@tonic-gate * signed type to keep the result of the pointer arithmetic to be
3560Sstevel@tonic-gate * automatically cast to an unsigned value. We do a similar cast
3570Sstevel@tonic-gate * in other similar expressions throughout this file.
3580Sstevel@tonic-gate */
3590Sstevel@tonic-gate if ((data_end - data) < (ptrdiff_t)(2 * sizeof (uint16_t)))
3600Sstevel@tonic-gate return (data_end - data_bak);
3610Sstevel@tonic-gate
3620Sstevel@tonic-gate GETINT16(type, data);
3630Sstevel@tonic-gate GETINT16(cls, data);
3640Sstevel@tonic-gate
365*4904Srs200217 /*
366*4904Srs200217 * Multicast DNS re-uses the top bit of the class field
367*4904Srs200217 * in the question and answer sections. Unicast DNS only
368*4904Srs200217 * uses 1 (Internet), 3 and 4. Hence it is safe. The top
369*4904Srs200217 * order bit is always cleared here to display the rrclass in case
370*4904Srs200217 * of Multicast DNS packets.
371*4904Srs200217 */
372*4904Srs200217 cls = cls & 0x7fff;
373*4904Srs200217
3740Sstevel@tonic-gate if (detail) {
3750Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
3760Sstevel@tonic-gate DNS_INDENT "Class: %u (%s)",
3770Sstevel@tonic-gate cls, dns_class_string(cls, detail));
3780Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
3790Sstevel@tonic-gate DNS_INDENT "Type: %u (%s)", type,
3800Sstevel@tonic-gate dns_type_string(type, detail));
3810Sstevel@tonic-gate } else {
3820Sstevel@tonic-gate (void) sprintf(line + strlen(line), " %s %s \?",
3830Sstevel@tonic-gate dns_class_string(cls, detail),
3840Sstevel@tonic-gate dns_type_string(type, detail));
3850Sstevel@tonic-gate }
3860Sstevel@tonic-gate return (data - data_bak);
3870Sstevel@tonic-gate }
3880Sstevel@tonic-gate
3893851Ssm26363 /*
3903851Ssm26363 * print_answer() is used to display the contents of a single resource
3913851Ssm26363 * record (RR) from either the answer, name server or additional
3923851Ssm26363 * section of the DNS packet.
3933851Ssm26363 *
3943851Ssm26363 * Input:
3953851Ssm26363 * *line: snoops output buffer.
3963851Ssm26363 * *header: start of the DNS packet, required for names and rcode.
3973851Ssm26363 * *data: location within header from where the RR starts.
3983851Ssm26363 * *data_end: where DNS data ends.
3993851Ssm26363 * detail: simple or verbose output.
4003851Ssm26363 *
4013851Ssm26363 * Returns:
4023851Ssm26363 * Pointer to next RR or data_end.
4033851Ssm26363 *
4043851Ssm26363 * Most RRs have the same top level format as defined in RFC 1035:
4053851Ssm26363 *
4063851Ssm26363 * 1 1 1 1 1 1
4073851Ssm26363 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
4083851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4093851Ssm26363 * | |
4103851Ssm26363 * / NAME /
4113851Ssm26363 * | |
4123851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4133851Ssm26363 * | TYPE |
4143851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4153851Ssm26363 * | CLASS |
4163851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4173851Ssm26363 * | TTL |
4183851Ssm26363 * | |
4193851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4203851Ssm26363 * | RDLENGTH |
4213851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
4223851Ssm26363 * / RDATA /
4233851Ssm26363 * / /
4243851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4253851Ssm26363 *
4263851Ssm26363 * However RFC 2671 introduced an exception to this rule
4273851Ssm26363 * with the "Extension Mechanisms for DNS" (EDNS0).
4283851Ssm26363 * When the type is 41 the remaining resource record format
4293851Ssm26363 * is:
4303851Ssm26363 *
4313851Ssm26363 * 1 1 1 1 1 1
4323851Ssm26363 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
4333851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4343851Ssm26363 * | TYPE = 41 |
4353851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4363851Ssm26363 * | Sender's UDP payload size |
4373851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4383851Ssm26363 * | Extended-rcode | Version |
4393851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4403851Ssm26363 * | Zero |
4413851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4423851Ssm26363 * | RDLENGTH |
4433851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
4443851Ssm26363 * / RDATA /
4453851Ssm26363 * / /
4463851Ssm26363 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4473851Ssm26363 *
4483851Ssm26363 */
4490Sstevel@tonic-gate static size_t
print_answer(char * line,const uchar_t * header,const uchar_t * data,const uchar_t * data_end,int detail)4500Sstevel@tonic-gate print_answer(char *line, const uchar_t *header, const uchar_t *data,
4510Sstevel@tonic-gate const uchar_t *data_end, int detail)
4520Sstevel@tonic-gate {
4530Sstevel@tonic-gate const uchar_t *data_bak = data;
4540Sstevel@tonic-gate const uchar_t *data_next;
4550Sstevel@tonic-gate uint16_t type;
4560Sstevel@tonic-gate uint16_t cls;
4570Sstevel@tonic-gate int32_t ttl;
4580Sstevel@tonic-gate uint16_t rdlen;
4590Sstevel@tonic-gate uint32_t serial, refresh, retry, expire, minimum;
4600Sstevel@tonic-gate uint8_t protocol;
4610Sstevel@tonic-gate int linepos;
4620Sstevel@tonic-gate uint16_t preference;
4633851Ssm26363 /* declarations for EDNS follow */
4643851Ssm26363 uint16_t size; /* Sender's UDP payload size */
4653851Ssm26363 uint8_t xrcode; /* Extended-rcode */
4663851Ssm26363 uint8_t ver; /* Version */
4673851Ssm26363 uint16_t rcode; /* Extracted from the DNS header */
4683851Ssm26363 union { /* DNS header overlay used for extraction */
4693851Ssm26363 HEADER *head;
4703851Ssm26363 const uchar_t *raw;
4713851Ssm26363 } headptr;
4720Sstevel@tonic-gate
4730Sstevel@tonic-gate if (detail) {
4740Sstevel@tonic-gate line += snprintf(line, get_line_remain(),
4750Sstevel@tonic-gate DNS_INDENT "Domain Name: ");
4760Sstevel@tonic-gate }
4770Sstevel@tonic-gate data += print_domain_name(line, header, data, data_end);
4780Sstevel@tonic-gate
4790Sstevel@tonic-gate /*
4803851Ssm26363 * Next, get the record type, being careful to make sure we
4813851Ssm26363 * don't run off the end of the packet.
4820Sstevel@tonic-gate */
4833851Ssm26363 if ((data_end - data) < (ptrdiff_t)(sizeof (type))) {
4840Sstevel@tonic-gate return (data_end - data_bak);
4850Sstevel@tonic-gate }
4860Sstevel@tonic-gate
4870Sstevel@tonic-gate GETINT16(type, data);
4883851Ssm26363
4893851Ssm26363 if (type == ns_t_opt) {
4903851Ssm26363 /*
4913851Ssm26363 * Make sure we won't run off the end reading size,
4923851Ssm26363 * xrcode, version, zero and rdlen.
4933851Ssm26363 */
4943851Ssm26363 if ((data_end - data) <
4953851Ssm26363 ((ptrdiff_t)(sizeof (size)
496*4904Srs200217 + sizeof (xrcode)
497*4904Srs200217 + sizeof (ver)
498*4904Srs200217 + sizeof (cls) /* zero */
499*4904Srs200217 + sizeof (rdlen)))) {
5003851Ssm26363 return (data_end - data_bak);
5013851Ssm26363 }
5023851Ssm26363
5033851Ssm26363 GETINT16(size, data);
5043851Ssm26363 GETINT8(xrcode, data);
5053851Ssm26363 /*
5063851Ssm26363 * The extended rcode represents the top half of the
5073851Ssm26363 * rcode which must be added to the rcode in the header.
5083851Ssm26363 */
5093851Ssm26363 rcode = 0xff & (xrcode << 4);
5103851Ssm26363 headptr.raw = header; /* Overlay the header... */
5113851Ssm26363 rcode += headptr.head->rcode; /* And pluck out the rcode. */
5123851Ssm26363
5133851Ssm26363 GETINT8(ver, data);
5143851Ssm26363 GETINT16(cls, data); /* zero */
5153851Ssm26363 GETINT16(rdlen, data);
5163851Ssm26363
5173851Ssm26363 if (detail) {
5183851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(),
5193851Ssm26363 DNS_INDENT "Type: %u (%s)", type,
5203851Ssm26363 dns_type_string(type, detail));
5213851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(),
5223851Ssm26363 DNS_INDENT "UDP payload size: %u (0x%.4x)",
5233851Ssm26363 size, size);
5243851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(),
5253851Ssm26363 DNS_INDENT "Extended rcode: %u "
5263851Ssm26363 "(translates to %u (%s))",
5273851Ssm26363 xrcode, rcode, dns_rcode_string(rcode));
5283851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(),
5293851Ssm26363 DNS_INDENT "EDNS0 Version: %u", ver);
5303851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(),
5313851Ssm26363 DNS_INDENT "zero: %u", cls);
5323851Ssm26363 (void) snprintf(get_line(0, 0), get_line_remain(),
5333851Ssm26363 DNS_INDENT "Data length: %u", rdlen);
5343851Ssm26363 } else {
5353851Ssm26363 line += strlen(line);
5363851Ssm26363 line += sprintf(line, " %s UDP %u rc %d ver %u len %u",
5373851Ssm26363 dns_type_string(type, detail), size, rcode, ver,
5383851Ssm26363 rdlen);
5393851Ssm26363 }
5403851Ssm26363
5413851Ssm26363 /*
5423851Ssm26363 * Make sure that rdlen is within data boundary.
5433851Ssm26363 */
5443851Ssm26363 if (rdlen > data_end - data)
5453851Ssm26363 return (data_end - data_bak);
5463851Ssm26363
5473851Ssm26363 /* Future OPT decode code goes here. */
5483851Ssm26363
5493851Ssm26363 data += rdlen;
5503851Ssm26363 return (data - data_bak);
5513851Ssm26363 }
5523851Ssm26363
5533851Ssm26363 /*
5543851Ssm26363 * Make sure we don't run off the end of the packet by reading the
5553851Ssm26363 * class, ttl, and length.
5563851Ssm26363 */
5573851Ssm26363 if ((data_end - data) <
5583851Ssm26363 ((ptrdiff_t)(sizeof (cls)
559*4904Srs200217 + sizeof (ttl)
560*4904Srs200217 + sizeof (rdlen)))) {
5613851Ssm26363 return (data_end - data_bak);
5623851Ssm26363 }
5633851Ssm26363
5640Sstevel@tonic-gate GETINT16(cls, data);
5650Sstevel@tonic-gate
566*4904Srs200217 /*
567*4904Srs200217 * Multicast DNS re-uses the top bit of the class field
568*4904Srs200217 * in the question and answer sections. Unicast DNS only
569*4904Srs200217 * uses 1 (Internet), 3 and 4. Hence it is safe. The top
570*4904Srs200217 * order bit is always cleared here to display the rrclass in case
571*4904Srs200217 * of Multicast DNS packets.
572*4904Srs200217 */
573*4904Srs200217 cls = cls & 0x7fff;
574*4904Srs200217
5750Sstevel@tonic-gate if (detail) {
5760Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
5770Sstevel@tonic-gate DNS_INDENT "Class: %d (%s)", cls,
5780Sstevel@tonic-gate dns_class_string(cls, detail));
5790Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
5800Sstevel@tonic-gate DNS_INDENT "Type: %d (%s)", type,
5810Sstevel@tonic-gate dns_type_string(type, detail));
5820Sstevel@tonic-gate } else {
5830Sstevel@tonic-gate line += strlen(line);
5840Sstevel@tonic-gate line += sprintf(line, " %s %s ",
5850Sstevel@tonic-gate dns_class_string(cls, detail),
5860Sstevel@tonic-gate dns_type_string(type, detail));
5870Sstevel@tonic-gate }
5880Sstevel@tonic-gate
5890Sstevel@tonic-gate GETINT32(ttl, data);
5900Sstevel@tonic-gate if (detail) {
5910Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
5920Sstevel@tonic-gate DNS_INDENT "TTL (Time To Live): %d", ttl);
5930Sstevel@tonic-gate }
5940Sstevel@tonic-gate
5950Sstevel@tonic-gate GETINT16(rdlen, data);
5960Sstevel@tonic-gate if (detail) {
5970Sstevel@tonic-gate line = get_line(0, 0);
5980Sstevel@tonic-gate line += snprintf(line, get_line_remain(), DNS_INDENT "%s: ",
5990Sstevel@tonic-gate dns_type_string(type, detail));
6000Sstevel@tonic-gate }
6010Sstevel@tonic-gate
6020Sstevel@tonic-gate if (rdlen > data_end - data)
6030Sstevel@tonic-gate return (data_end - data_bak);
6040Sstevel@tonic-gate
6050Sstevel@tonic-gate switch (type) {
6060Sstevel@tonic-gate case ns_t_a:
6070Sstevel@tonic-gate print_ip(AF_INET, line, data, rdlen);
6080Sstevel@tonic-gate break;
6090Sstevel@tonic-gate case ns_t_aaaa:
6100Sstevel@tonic-gate print_ip(AF_INET6, line, data, rdlen);
6110Sstevel@tonic-gate break;
6120Sstevel@tonic-gate case ns_t_hinfo:
6130Sstevel@tonic-gate line += sprintf(line, "CPU: ");
6140Sstevel@tonic-gate data_next = data + print_char_string(line, data, rdlen);
6150Sstevel@tonic-gate if (data_next >= data_end)
6160Sstevel@tonic-gate break;
6170Sstevel@tonic-gate line += strlen(line);
6180Sstevel@tonic-gate line += sprintf(line, "OS: ");
6190Sstevel@tonic-gate (void) print_char_string(line, data_next,
6200Sstevel@tonic-gate rdlen - (data_next - data));
6210Sstevel@tonic-gate break;
6220Sstevel@tonic-gate case ns_t_ns:
6230Sstevel@tonic-gate case ns_t_cname:
6240Sstevel@tonic-gate case ns_t_mb:
6250Sstevel@tonic-gate case ns_t_mg:
6260Sstevel@tonic-gate case ns_t_mr:
6270Sstevel@tonic-gate case ns_t_ptr:
6280Sstevel@tonic-gate (void) print_domain_name(line, header, data, data_end);
6290Sstevel@tonic-gate break;
6300Sstevel@tonic-gate case ns_t_mx:
6310Sstevel@tonic-gate data_next = data;
6320Sstevel@tonic-gate if (rdlen < sizeof (uint16_t))
6330Sstevel@tonic-gate break;
6340Sstevel@tonic-gate GETINT16(preference, data_next);
6350Sstevel@tonic-gate if (detail) {
6360Sstevel@tonic-gate (void) print_domain_name(line, header, data_next,
6370Sstevel@tonic-gate data_end);
6380Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
6390Sstevel@tonic-gate DNS_INDENT "Preference: %u", preference);
6400Sstevel@tonic-gate } else {
6410Sstevel@tonic-gate (void) print_domain_name(line, header, data_next,
6420Sstevel@tonic-gate data_end);
6430Sstevel@tonic-gate }
6440Sstevel@tonic-gate break;
6450Sstevel@tonic-gate case ns_t_soa:
6460Sstevel@tonic-gate if (!detail)
6470Sstevel@tonic-gate break;
6480Sstevel@tonic-gate line = get_line(0, 0);
6490Sstevel@tonic-gate line += snprintf(line, get_line_remain(),
6500Sstevel@tonic-gate DNS_INDENT "MNAME (Server name): ");
6510Sstevel@tonic-gate data_next = data + print_domain_name(line, header, data,
6520Sstevel@tonic-gate data_end);
6530Sstevel@tonic-gate if (data_next >= data_end)
6540Sstevel@tonic-gate break;
6550Sstevel@tonic-gate line = get_line(0, 0);
6560Sstevel@tonic-gate line += snprintf(line, get_line_remain(),
6570Sstevel@tonic-gate DNS_INDENT "RNAME (Resposible mailbox): ");
6580Sstevel@tonic-gate data_next = data_next +
6590Sstevel@tonic-gate print_domain_name(line, header, data_next, data_end);
6600Sstevel@tonic-gate if ((data_end - data_next) < (ptrdiff_t)(5 * sizeof (uint32_t)))
6610Sstevel@tonic-gate break;
6620Sstevel@tonic-gate GETINT32(serial, data_next);
6630Sstevel@tonic-gate GETINT32(refresh, data_next);
6640Sstevel@tonic-gate GETINT32(retry, data_next);
6650Sstevel@tonic-gate GETINT32(expire, data_next);
6660Sstevel@tonic-gate GETINT32(minimum, data_next);
6670Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
6680Sstevel@tonic-gate DNS_INDENT "Serial: %u", serial);
6690Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
6700Sstevel@tonic-gate DNS_INDENT "Refresh: %u Retry: %u "
6710Sstevel@tonic-gate "Expire: %u Minimum: %u",
6720Sstevel@tonic-gate refresh, retry, expire, minimum);
6730Sstevel@tonic-gate break;
6740Sstevel@tonic-gate case ns_t_wks:
6750Sstevel@tonic-gate print_ip(AF_INET, line, data, rdlen);
6760Sstevel@tonic-gate if (!detail)
6770Sstevel@tonic-gate break;
6780Sstevel@tonic-gate data_next = data + sizeof (in_addr_t);
6790Sstevel@tonic-gate if (data_next >= data_end)
6800Sstevel@tonic-gate break;
6810Sstevel@tonic-gate GETINT8(protocol, data_next);
6820Sstevel@tonic-gate line = get_line(0, 0);
6830Sstevel@tonic-gate line += snprintf(line, get_line_remain(),
6840Sstevel@tonic-gate DNS_INDENT "Protocol: %u ", protocol);
6850Sstevel@tonic-gate switch (protocol) {
6860Sstevel@tonic-gate case IPPROTO_UDP:
6870Sstevel@tonic-gate (void) snprintf(line, get_line_remain(), "(UDP)");
6880Sstevel@tonic-gate break;
6890Sstevel@tonic-gate case IPPROTO_TCP:
6900Sstevel@tonic-gate (void) snprintf(line, get_line_remain(), "(TCP)");
6910Sstevel@tonic-gate break;
6920Sstevel@tonic-gate }
6930Sstevel@tonic-gate (void) snprintf(get_line(0, 0), get_line_remain(),
6940Sstevel@tonic-gate DNS_INDENT "Service bitmap:");
6950Sstevel@tonic-gate (void) snprintf(line, get_line_remain(),
6960Sstevel@tonic-gate DNS_INDENT "0 8 16 24");
6970Sstevel@tonic-gate linepos = 4;
6980Sstevel@tonic-gate while (data_next < data + rdlen) {
6990Sstevel@tonic-gate if (linepos == 4) {
7000Sstevel@tonic-gate line = get_line(0, 0);
7010Sstevel@tonic-gate line += snprintf(line, get_line_remain(),
7020Sstevel@tonic-gate DNS_INDENT);
7030Sstevel@tonic-gate linepos = 0;
7040Sstevel@tonic-gate }
7050Sstevel@tonic-gate line += snprintf(line, get_line_remain(), "%s",
7060Sstevel@tonic-gate binary_string(*data_next));
7070Sstevel@tonic-gate linepos++;
7080Sstevel@tonic-gate data_next++;
7090Sstevel@tonic-gate }
7100Sstevel@tonic-gate break;
7110Sstevel@tonic-gate case ns_t_minfo:
7120Sstevel@tonic-gate if (!detail)
7130Sstevel@tonic-gate break;
7140Sstevel@tonic-gate line = get_line(0, 0);
7150Sstevel@tonic-gate line += snprintf(line, get_line_remain(),
7160Sstevel@tonic-gate DNS_INDENT "RMAILBX (Resposible mailbox): ");
7170Sstevel@tonic-gate data_next = data + print_domain_name(line, header, data,
7180Sstevel@tonic-gate data_end);
7190Sstevel@tonic-gate line = get_line(0, 0);
7200Sstevel@tonic-gate line += snprintf(line, get_line_remain(),
7210Sstevel@tonic-gate DNS_INDENT "EMAILBX (mailbox to receive err message): ");
7220Sstevel@tonic-gate data_next = data_next + print_domain_name(line, header,
7230Sstevel@tonic-gate data_next, data_end);
7240Sstevel@tonic-gate break;
7250Sstevel@tonic-gate }
7260Sstevel@tonic-gate data += rdlen;
7270Sstevel@tonic-gate return (data - data_bak);
7280Sstevel@tonic-gate }
7290Sstevel@tonic-gate
7300Sstevel@tonic-gate static char *
binary_string(char data)7310Sstevel@tonic-gate binary_string(char data)
7320Sstevel@tonic-gate {
7330Sstevel@tonic-gate static char bstring[8 + 1];
7340Sstevel@tonic-gate char *ptr;
7350Sstevel@tonic-gate int i;
7360Sstevel@tonic-gate ptr = bstring;
7370Sstevel@tonic-gate for (i = 0; i < 8; i++) {
7380Sstevel@tonic-gate *ptr++ = (data & 0x80) ? '1' : '0';
7390Sstevel@tonic-gate data = data << 1;
7400Sstevel@tonic-gate }
7410Sstevel@tonic-gate *ptr = (char)0;
7420Sstevel@tonic-gate return (bstring);
7430Sstevel@tonic-gate }
7440Sstevel@tonic-gate
7450Sstevel@tonic-gate static void
print_ip(int af,char * line,const uchar_t * data,uint16_t len)7460Sstevel@tonic-gate print_ip(int af, char *line, const uchar_t *data, uint16_t len)
7470Sstevel@tonic-gate {
7480Sstevel@tonic-gate in6_addr_t addr6;
7490Sstevel@tonic-gate in_addr_t addr4;
7500Sstevel@tonic-gate void *addr;
7510Sstevel@tonic-gate
7520Sstevel@tonic-gate switch (af) {
7530Sstevel@tonic-gate case AF_INET:
7540Sstevel@tonic-gate if (len != sizeof (in_addr_t))
7550Sstevel@tonic-gate return;
7560Sstevel@tonic-gate addr = memcpy(&addr4, data, sizeof (addr4));
7570Sstevel@tonic-gate break;
7580Sstevel@tonic-gate case AF_INET6:
7590Sstevel@tonic-gate if (len != sizeof (in6_addr_t))
7600Sstevel@tonic-gate return;
7610Sstevel@tonic-gate addr = memcpy(&addr6, data, sizeof (addr6));
7620Sstevel@tonic-gate break;
7630Sstevel@tonic-gate }
7640Sstevel@tonic-gate
7650Sstevel@tonic-gate (void) inet_ntop(af, addr, line, INET6_ADDRSTRLEN);
7660Sstevel@tonic-gate }
7670Sstevel@tonic-gate
7680Sstevel@tonic-gate /*
7690Sstevel@tonic-gate * charbuf is assumed to be of size MAX_CHAR_STRING_SIZE.
7700Sstevel@tonic-gate */
7710Sstevel@tonic-gate static const uchar_t *
get_char_string(const uchar_t * data,char * charbuf,uint16_t datalen)7720Sstevel@tonic-gate get_char_string(const uchar_t *data, char *charbuf, uint16_t datalen)
7730Sstevel@tonic-gate {
774410Skcpoon int len;
7750Sstevel@tonic-gate char *name = charbuf;
7760Sstevel@tonic-gate int i = 0;
7770Sstevel@tonic-gate
7780Sstevel@tonic-gate /*
7790Sstevel@tonic-gate * From RFC1035, a character-string is a single length octet followed
7800Sstevel@tonic-gate * by that number of characters.
7810Sstevel@tonic-gate */
7820Sstevel@tonic-gate if (datalen > 1) {
7830Sstevel@tonic-gate len = *data;
7840Sstevel@tonic-gate data++;
7850Sstevel@tonic-gate if (len > 0 && len < MAX_CHAR_STRING_SIZE) {
7860Sstevel@tonic-gate for (i = 0; i < len; i++, data++)
7870Sstevel@tonic-gate name[i] = *data;
7880Sstevel@tonic-gate }
7890Sstevel@tonic-gate }
7900Sstevel@tonic-gate name[i] = '\0';
7910Sstevel@tonic-gate return (data);
7920Sstevel@tonic-gate }
7930Sstevel@tonic-gate
7940Sstevel@tonic-gate static size_t
print_char_string(char * line,const uchar_t * data,uint16_t len)7950Sstevel@tonic-gate print_char_string(char *line, const uchar_t *data, uint16_t len)
7960Sstevel@tonic-gate {
7970Sstevel@tonic-gate char charbuf[MAX_CHAR_STRING_SIZE];
7980Sstevel@tonic-gate const uchar_t *data_bak = data;
7990Sstevel@tonic-gate
8000Sstevel@tonic-gate data = get_char_string(data, charbuf, len);
8010Sstevel@tonic-gate (void) sprintf(line, "%s", charbuf);
8020Sstevel@tonic-gate return (data - data_bak);
8030Sstevel@tonic-gate }
8040Sstevel@tonic-gate
8050Sstevel@tonic-gate /*
8060Sstevel@tonic-gate * header: the entire message header, this is where we start to
8070Sstevel@tonic-gate * count the offset of the compression scheme
8080Sstevel@tonic-gate * data: the start of the domain name
8090Sstevel@tonic-gate * namebuf: user supplied buffer
8100Sstevel@tonic-gate * return: the next byte after what we have parsed
8110Sstevel@tonic-gate */
8120Sstevel@tonic-gate static const uchar_t *
get_domain_name(const uchar_t * header,const uchar_t * data,const uchar_t * data_end,char * namebuf,char * namend)8130Sstevel@tonic-gate get_domain_name(const uchar_t *header, const uchar_t *data,
8140Sstevel@tonic-gate const uchar_t *data_end, char *namebuf, char *namend)
8150Sstevel@tonic-gate {
8160Sstevel@tonic-gate uint8_t len;
8170Sstevel@tonic-gate char *name = namebuf;
8180Sstevel@tonic-gate
8190Sstevel@tonic-gate /*
8200Sstevel@tonic-gate * From RFC1035, a domain name is a sequence of labels, where each
8210Sstevel@tonic-gate * label consists of a length octet followed by that number of
8220Sstevel@tonic-gate * octets. The domain name terminates with the zero length octet
8230Sstevel@tonic-gate * for the null label of the root.
8240Sstevel@tonic-gate */
8250Sstevel@tonic-gate
8260Sstevel@tonic-gate while (name < (namend - 1)) {
8270Sstevel@tonic-gate if ((data_end - data) < (ptrdiff_t)(sizeof (uint8_t))) {
8280Sstevel@tonic-gate /* The length octet is off the end of the packet. */
8290Sstevel@tonic-gate break;
8300Sstevel@tonic-gate }
8310Sstevel@tonic-gate GETINT8(len, data);
8320Sstevel@tonic-gate if (len == 0) {
8330Sstevel@tonic-gate /*
8340Sstevel@tonic-gate * Domain names end with a length byte of zero,
8350Sstevel@tonic-gate * which represents the null label of the root.
8360Sstevel@tonic-gate */
8370Sstevel@tonic-gate break;
8380Sstevel@tonic-gate }
8390Sstevel@tonic-gate /*
8400Sstevel@tonic-gate * test if we are using the compression scheme
8410Sstevel@tonic-gate */
8420Sstevel@tonic-gate if ((len & 0xc0) == 0xc0) {
8430Sstevel@tonic-gate uint16_t offset;
8440Sstevel@tonic-gate const uchar_t *label_ptr;
8450Sstevel@tonic-gate
8460Sstevel@tonic-gate /*
8470Sstevel@tonic-gate * From RFC1035, message compression allows a
8480Sstevel@tonic-gate * domain name or a list of labels at the end of a
8490Sstevel@tonic-gate * domain name to be replaced with a pointer to a
8500Sstevel@tonic-gate * prior occurance of the same name. In this
8510Sstevel@tonic-gate * scheme, the pointer is a two octet sequence
8520Sstevel@tonic-gate * where the most significant two bits are set, and
8530Sstevel@tonic-gate * the remaining 14 bits are the offset from the
8540Sstevel@tonic-gate * start of the message of the next label.
8550Sstevel@tonic-gate */
8560Sstevel@tonic-gate data--;
8570Sstevel@tonic-gate if ((data_end - data) <
8580Sstevel@tonic-gate (ptrdiff_t)(sizeof (uint16_t))) {
8590Sstevel@tonic-gate /*
8600Sstevel@tonic-gate * The offset octets aren't entirely
8610Sstevel@tonic-gate * contained within this pakcet.
8620Sstevel@tonic-gate */
8630Sstevel@tonic-gate data = data_end;
8640Sstevel@tonic-gate break;
8650Sstevel@tonic-gate }
8660Sstevel@tonic-gate GETINT16(offset, data);
8670Sstevel@tonic-gate label_ptr = header + (offset & 0x3fff);
8680Sstevel@tonic-gate /*
8690Sstevel@tonic-gate * We must verify that the offset is valid by
8700Sstevel@tonic-gate * checking that it is less than the current data
8710Sstevel@tonic-gate * pointer and that it isn't off the end of the
8720Sstevel@tonic-gate * packet.
8730Sstevel@tonic-gate */
8740Sstevel@tonic-gate if (label_ptr > data || label_ptr >= data_end)
8750Sstevel@tonic-gate break;
8760Sstevel@tonic-gate (void) get_domain_name(header, label_ptr, data_end,
8770Sstevel@tonic-gate name, namend);
8780Sstevel@tonic-gate return (data);
8790Sstevel@tonic-gate } else {
8800Sstevel@tonic-gate if (len > (data_end - data)) {
8810Sstevel@tonic-gate /*
8820Sstevel@tonic-gate * The label isn't entirely contained
8830Sstevel@tonic-gate * within the packet. Don't read it. The
8840Sstevel@tonic-gate * caller checks that the data pointer is
8850Sstevel@tonic-gate * not beyond the end after we've
8860Sstevel@tonic-gate * incremented it.
8870Sstevel@tonic-gate */
8880Sstevel@tonic-gate data = data_end;
8890Sstevel@tonic-gate break;
8900Sstevel@tonic-gate }
8910Sstevel@tonic-gate while (len > 0 && name < (namend - 2)) {
8920Sstevel@tonic-gate *name = *data;
8930Sstevel@tonic-gate name++;
8940Sstevel@tonic-gate data++;
8950Sstevel@tonic-gate len--;
8960Sstevel@tonic-gate }
8970Sstevel@tonic-gate *name = '.';
8980Sstevel@tonic-gate name++;
8990Sstevel@tonic-gate }
9000Sstevel@tonic-gate }
9010Sstevel@tonic-gate *name = '\0';
9020Sstevel@tonic-gate return (data);
9030Sstevel@tonic-gate }
9040Sstevel@tonic-gate
9050Sstevel@tonic-gate static size_t
print_domain_name(char * line,const uchar_t * header,const uchar_t * data,const uchar_t * data_end)9060Sstevel@tonic-gate print_domain_name(char *line, const uchar_t *header, const uchar_t *data,
9070Sstevel@tonic-gate const uchar_t *data_end)
9080Sstevel@tonic-gate {
9090Sstevel@tonic-gate char name[NS_MAXDNAME];
9100Sstevel@tonic-gate const uchar_t *new_data;
9110Sstevel@tonic-gate
9120Sstevel@tonic-gate new_data = get_domain_name(header, data, data_end, name,
9130Sstevel@tonic-gate name + sizeof (name));
9140Sstevel@tonic-gate
9150Sstevel@tonic-gate (void) sprintf(line, "%s", name);
9160Sstevel@tonic-gate return (new_data - data);
9170Sstevel@tonic-gate }
918