1 /* wdns-dump-pcap: read a pcap file, and optionally dump broken DNS messages */
2 
3 /* XXX -- assumes all packets are DNS, use a bpf if not */
4 /* XXX -- doesn't handle fragments */
5 /* XXX -- only handles ethernet/ipv4 messages */
6 
7 #include "private.h"
8 
9 #include <pcap.h>
10 #include <wdns.h>
11 
12 static uint64_t count;
13 static uint64_t count_dump;
14 
15 #define eth_type_ip	0x0800
16 #define eth_type_ipv6	0x86dd
17 
18 #define advance(p, len, sz) do { (p) += (sz); (len) -= (sz); } while (0)
19 #define getu16(dst, src) do { memcpy(&(dst), src, 2); dst = ntohs(dst); } while (0)
20 
21 static void
packet_dump(u_char * dumper,const struct pcap_pkthdr * hdr,const u_char * pkt)22 packet_dump(u_char *dumper,
23 	    const struct pcap_pkthdr *hdr,
24 	    const u_char *pkt)
25 {
26 	pcap_dump(dumper, hdr, pkt);
27 	VERBOSE("count=%" PRIu64 " dumping broken packet\n", count);
28 	count_dump += 1;
29 }
30 
31 static void
packet_handler(u_char * dumper,const struct pcap_pkthdr * hdr,const u_char * pkt)32 packet_handler(u_char *dumper,
33 	       const struct pcap_pkthdr *hdr,
34 	       const u_char *pkt)
35 {
36 	const u_char *dns_p, *p;
37 	uint16_t e_type;
38 	uint16_t ip_len;
39 	uint32_t dns_len;
40 	uint32_t len = hdr->caplen;
41 	uint8_t ihl;
42 	wdns_message_t m;
43 	wdns_res res;
44 
45 	p = pkt;
46 	count++;
47 
48 	VERBOSE("count=%" PRIu64 " parsing packet\n", count);
49 
50 	/* ethernet */
51 	if (len < 14) {
52 		VERBOSE("count=%" PRIu64 " too short for ethernet\n", count);
53 		return;
54 	}
55 	advance(p, len, 12);
56 	getu16(e_type, p);
57 	advance(p, len, 2);
58 	if (e_type != eth_type_ip) {
59 		VERBOSE("count=%" PRIu64 " not IP e_type=%#.x\n", count, e_type);
60 		return;
61 	}
62 
63 	/* skip ip */
64 	ihl = *p & 0x0f;
65 	if (len < ihl * 4U) {
66 		VERBOSE("count=%" PRIu64" IP too short\n", count);
67 		return;
68 	}
69 	getu16(ip_len, p + 2);
70 	if (ip_len < len)
71 		len = ip_len;
72 	advance(p, len, ihl * 4U);
73 
74 	/* skip udp */
75 	if (len < 8) {
76 		VERBOSE("count=%" PRIu64" UDP too short\n", count);
77 		return;
78 	}
79 	advance(p, len, 8);
80 
81 	/* dns header */
82 	if (len < 12) {
83 		VERBOSE("count=%" PRIu64" DNS header too short\n", count);
84 		return;
85 	}
86 
87 	dns_p = p;
88 	dns_len = len;
89 
90 	res = wdns_parse_message(&m, dns_p, dns_len);
91 	if (res == wdns_res_success) {
92 		wdns_print_message(stdout, &m);
93 		wdns_clear_message(&m);
94 	} else {
95 		VERBOSE("wdns_res=%u\n", res);
96 		packet_dump(dumper, hdr, pkt);
97 	}
98 
99 	VERBOSE("%s", "\n");
100 	return;
101 }
102 
103 int
main(int argc,char ** argv)104 main(int argc, char **argv) {
105 	pcap_t *pcap;
106 	pcap_dumper_t *dumper = NULL;
107 	char errbuf[PCAP_ERRBUF_SIZE];
108 	struct bpf_program bpfp;
109 
110 	if (argc != 4) {
111 		fprintf(stderr, "Usage: %s <INFILE> <OUTFILE> <BPF>\n", argv[0]);
112 		return (EXIT_FAILURE);
113 	}
114 
115 	pcap = pcap_open_offline(argv[1], errbuf);
116 	if (pcap == NULL) {
117 		fprintf(stderr, "pcap_open_offline() failed: %s\n", errbuf);
118 		return (EXIT_FAILURE);
119 	}
120 
121 	dumper = pcap_dump_open(pcap, argv[2]);
122 	if (dumper == NULL) {
123 		pcap_perror(pcap, "pcap_dump_open:");
124 		return (EXIT_FAILURE);
125 	}
126 
127 	if (pcap_compile(pcap, &bpfp, argv[3], 1, 0) != 0) {
128 		pcap_perror(pcap, "pcap_compile:");
129 		pcap_close(pcap);
130 		pcap_dump_close(dumper);
131 		return (EXIT_FAILURE);
132 	} else {
133 		if (pcap_setfilter(pcap, &bpfp) != 0) {
134 			pcap_perror(pcap, "pcap_setfilter:");
135 			pcap_close(pcap);
136 			pcap_dump_close(dumper);
137 			return (EXIT_FAILURE);
138 		}
139 		pcap_freecode(&bpfp);
140 	}
141 
142 	pcap_loop(pcap, -1, packet_handler, (u_char *) dumper);
143 	pcap_close(pcap);
144 	pcap_dump_close(dumper);
145 
146 	fprintf(stderr, "count=%" PRIu64 "\n", count);
147 	fprintf(stderr, "count_dump=%" PRIu64 "\n", count_dump);
148 
149 	return (EXIT_SUCCESS);
150 }
151