1/*
2 * testcode/pktview.c - debug program to disassemble a DNS packet.
3 *
4 * Copyright (c) 2007, NLnet Labs. All rights reserved.
5 *
6 * This software is open source.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36/**
37 * \file
38 *
39 * This program shows a dns packet wire format.
40 */
41
42#include "config.h"
43#include "util/log.h"
44#include "util/data/dname.h"
45#include "util/data/msgparse.h"
46#include "testcode/unitmain.h"
47#include "testcode/readhex.h"
48#include "sldns/sbuffer.h"
49#include "sldns/parseutil.h"
50
51/** usage information for pktview */
52static void usage(char* argv[])
53{
54	printf("usage: %s\n", argv[0]);
55	printf("present hex packet on stdin.\n");
56	exit(1);
57}
58
59/** read hex input */
60static void read_input(sldns_buffer* pkt, FILE* in)
61{
62	char buf[102400];
63	char* np = buf;
64	while(fgets(np, (int)sizeof(buf) - (np-buf), in)) {
65		if(buf[0] == ';') /* comment */
66			continue;
67		np = &np[strlen(np)];
68	}
69	hex_to_buf(pkt, buf);
70}
71
72/** analyze domain name in packet, possibly compressed */
73static void analyze_dname(sldns_buffer* pkt)
74{
75	size_t oldpos = sldns_buffer_position(pkt);
76	size_t len;
77	printf("[pos %d] dname: ", (int)oldpos);
78	dname_print(stdout, pkt, sldns_buffer_current(pkt));
79	len = pkt_dname_len(pkt);
80	printf(" len=%d", (int)len);
81	if(sldns_buffer_position(pkt)-oldpos != len)
82		printf(" comprlen=%d\n",
83			(int)(sldns_buffer_position(pkt)-oldpos));
84	else	printf("\n");
85}
86
87/** analyze rdata in packet */
88static void analyze_rdata(sldns_buffer*pkt, const sldns_rr_descriptor* desc,
89	uint16_t rdlen)
90{
91	int rdf = 0;
92	int count = (int)desc->_dname_count;
93	size_t len, oldpos;
94	while(rdlen > 0 && count) {
95		switch(desc->_wireformat[rdf]) {
96		case LDNS_RDF_TYPE_DNAME:
97			oldpos = sldns_buffer_position(pkt);
98			analyze_dname(pkt);
99			rdlen -= sldns_buffer_position(pkt)-oldpos;
100			count --;
101			len = 0;
102			break;
103		case LDNS_RDF_TYPE_STR:
104			len = sldns_buffer_current(pkt)[0] + 1;
105			break;
106		default:
107			len = get_rdf_size(desc->_wireformat[rdf]);
108		}
109		if(len) {
110			printf(" wf[%d]", (int)len);
111			sldns_buffer_skip(pkt, (ssize_t)len);
112			rdlen -= len;
113		}
114		rdf++;
115	}
116	if(rdlen) {
117		size_t i;
118		printf(" remain[%d]\n", (int)rdlen);
119		for(i=0; i<rdlen; i++)
120			printf(" %2.2X", (unsigned)sldns_buffer_current(pkt)[i]);
121		printf("\n");
122	}
123	else	printf("\n");
124	sldns_buffer_skip(pkt, (ssize_t)rdlen);
125}
126
127/** analyze rr in packet */
128static void analyze_rr(sldns_buffer* pkt, int q)
129{
130	uint16_t type, dclass, len;
131	uint32_t ttl;
132	analyze_dname(pkt);
133	type = sldns_buffer_read_u16(pkt);
134	dclass = sldns_buffer_read_u16(pkt);
135	printf("type %s(%d)", sldns_rr_descript(type)?
136		sldns_rr_descript(type)->_name: "??" , (int)type);
137	printf(" class %s(%d) ", sldns_lookup_by_id(sldns_rr_classes,
138		(int)dclass)?sldns_lookup_by_id(sldns_rr_classes,
139		(int)dclass)->name:"??", (int)dclass);
140	if(q) {
141		printf("\n");
142	} else {
143		ttl = sldns_buffer_read_u32(pkt);
144		printf(" ttl %d (0x%x)", (int)ttl, (unsigned)ttl);
145		len = sldns_buffer_read_u16(pkt);
146		printf(" rdata len %d:\n", (int)len);
147		if(sldns_rr_descript(type))
148			analyze_rdata(pkt, sldns_rr_descript(type), len);
149		else sldns_buffer_skip(pkt, (ssize_t)len);
150	}
151}
152
153/** analyse pkt */
154static void analyze(sldns_buffer* pkt)
155{
156	uint16_t i, f, qd, an, ns, ar;
157	int rrnum = 0;
158	printf("packet length %d\n", (int)sldns_buffer_limit(pkt));
159	if(sldns_buffer_limit(pkt) < 12) return;
160
161	i = sldns_buffer_read_u16(pkt);
162	printf("id (hostorder): %d (0x%x)\n", (int)i, (unsigned)i);
163	f = sldns_buffer_read_u16(pkt);
164	printf("flags: 0x%x\n", (unsigned)f);
165	qd = sldns_buffer_read_u16(pkt);
166	printf("qdcount: %d\n", (int)qd);
167	an = sldns_buffer_read_u16(pkt);
168	printf("ancount: %d\n", (int)an);
169	ns = sldns_buffer_read_u16(pkt);
170	printf("nscount: %d\n", (int)ns);
171	ar = sldns_buffer_read_u16(pkt);
172	printf("arcount: %d\n", (int)ar);
173
174	printf(";-- query section\n");
175	while(sldns_buffer_remaining(pkt) > 0) {
176		if(rrnum == (int)qd)
177			printf(";-- answer section\n");
178		if(rrnum == (int)qd+(int)an)
179			printf(";-- authority section\n");
180		if(rrnum == (int)qd+(int)an+(int)ns)
181			printf(";-- additional section\n");
182		printf("rr %d ", rrnum);
183		analyze_rr(pkt, rrnum < (int)qd);
184		rrnum++;
185	}
186}
187
188/** main program for pktview */
189int main(int argc, char* argv[])
190{
191	sldns_buffer* pkt = sldns_buffer_new(65553);
192	if(argc != 1) {
193		usage(argv);
194	}
195	if(!pkt) fatal_exit("out of memory");
196
197	read_input(pkt, stdin);
198	analyze(pkt);
199
200	sldns_buffer_free(pkt);
201	return 0;
202}
203