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