]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnspcap.cc
Merge pull request #14032 from rgacogne/ddist-192-changelog-secpoll
[thirdparty/pdns.git] / pdns / dnspcap.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #define __FAVOR_BSD
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include "dnspcap.hh"
27 #include <boost/format.hpp>
28 #include <fcntl.h>
29
30 #include "namespaces.hh"
31 PcapPacketReader::PcapPacketReader(const string& fname) : d_fname(fname)
32 {
33 d_fp = pdns::UniqueFilePtr(fopen(fname.c_str(), "r"));
34 if (!d_fp) {
35 unixDie("Unable to open file " + fname);
36 }
37
38 int flags = fcntl(fileno(d_fp.get()), F_GETFL, 0);
39 fcntl(fileno(d_fp.get()), F_SETFL, flags & (~O_NONBLOCK)); // bsd needs this in stdin (??)
40
41 checkedFread(&d_pfh);
42
43 if (d_pfh.magic != 2712847316UL) {
44 throw runtime_error((boost::format("PCAP file %s has bad magic %x, should be %x") % fname % d_pfh.magic % 2712847316UL).str());
45 }
46
47 if( d_pfh.linktype==1) {
48 d_skipMediaHeader=sizeof(struct ether_header);
49 }
50 else if( d_pfh.linktype==12) { // LOOP
51 d_skipMediaHeader=4;
52 }
53 else if(d_pfh.linktype==101) {
54 d_skipMediaHeader=0;
55 }
56 else if(d_pfh.linktype==113) {
57 d_skipMediaHeader=16;
58 }
59 else throw runtime_error((boost::format("Unsupported link type %d") % d_pfh.linktype).str());
60
61 d_runts = d_oversized = d_correctpackets = d_nonetheripudp = 0;
62
63 size_t alignmentCorrection = d_skipMediaHeader % alignof(struct ip);
64
65 d_buffer = d_readbuffer + alignmentCorrection;
66 d_bufsize = sizeof(d_readbuffer) - alignmentCorrection;
67
68 if (d_skipMediaHeader > d_bufsize) throw runtime_error("media header is too big");
69 }
70
71 void PcapPacketReader::checkedFreadSize(void* ptr, size_t size)
72 {
73 int ret = fread(ptr, 1, size, d_fp.get());
74 if (ret < 0) {
75 unixDie( (boost::format("Error reading %d bytes from %s") % size % d_fname).str());
76 }
77
78 if(!ret)
79 throw EofException();
80
81 if((size_t)ret != size)
82 throw EofException((boost::format("Incomplete read from '%s', got only %d bytes") % d_fname % ret).str());
83 }
84
85 bool PcapPacketReader::getUDPPacket()
86 try
87 {
88 for(;;) {
89 checkedFread(&d_pheader);
90 if(!d_pheader.caplen) {
91 d_runts++;
92 continue;
93 }
94
95 if(d_pheader.caplen > d_bufsize) {
96 d_oversized++;
97 throw runtime_error((boost::format("Can't handle a %d byte packet, have space for %zu") % d_pheader.caplen % d_bufsize).str());
98 }
99
100 checkedFreadSize(d_buffer, d_pheader.caplen);
101
102 if(d_pheader.caplen < d_pheader.len) {
103 d_runts++;
104 continue;
105 }
106
107 if (d_pheader.caplen < d_skipMediaHeader) {
108 d_runts++;
109 continue;
110 }
111
112 d_ip=reinterpret_cast<struct ip*>(d_buffer + d_skipMediaHeader);
113 d_ip6=reinterpret_cast<struct ip6_hdr*>(d_buffer + d_skipMediaHeader);
114 uint16_t contentCode=0;
115
116 if(d_pfh.linktype==1) {
117 if (d_pheader.caplen < sizeof(*d_ether)) {
118 d_runts++;
119 continue;
120 }
121 d_ether=reinterpret_cast<struct ether_header*>(d_buffer);
122 contentCode=ntohs(d_ether->ether_type);
123 }
124 else if(d_pfh.linktype == 12) { // LOOP
125 if (d_pheader.caplen < (d_skipMediaHeader + sizeof(*d_ip))) {
126 d_runts++;
127 continue;
128 }
129 if(d_ip->ip_v == 4)
130 contentCode = 0x0800;
131 else
132 contentCode = 0x86dd;
133 }
134 else if(d_pfh.linktype==101) {
135 if (d_pheader.caplen < (d_skipMediaHeader + sizeof(*d_ip))) {
136 d_runts++;
137 continue;
138 }
139 if(d_ip->ip_v==4)
140 contentCode = 0x0800;
141 else
142 contentCode = 0x86dd;
143 }
144 else if(d_pfh.linktype==113) {
145 if (d_pheader.caplen < sizeof(*d_lcc)) {
146 d_runts++;
147 continue;
148 }
149 d_lcc=reinterpret_cast<struct pdns_lcc_header*>(d_buffer);
150 contentCode=ntohs(d_lcc->lcc_protocol);
151 }
152
153 if(contentCode==0x0800 && (d_pheader.caplen >= (d_skipMediaHeader + sizeof(*d_ip))) && d_ip->ip_p==17) { // udp
154 if (d_pheader.caplen < (d_skipMediaHeader + (4 * d_ip->ip_hl) + sizeof(*d_udp))) {
155 d_runts++;
156 continue;
157 }
158 d_udp=reinterpret_cast<const struct udphdr*>(d_buffer + d_skipMediaHeader + 4 * d_ip->ip_hl);
159 d_payload = (unsigned char*)d_udp + sizeof(struct udphdr);
160 d_len = ntohs(d_udp->uh_ulen) - sizeof(struct udphdr);
161 if (d_pheader.caplen < (d_skipMediaHeader + (4 * d_ip->ip_hl) + sizeof(*d_udp) + d_len)) {
162 d_runts++;
163 continue;
164 }
165 if((const char*)d_payload + d_len > d_buffer + d_pheader.caplen) {
166 d_runts++;
167 continue;
168 }
169 d_correctpackets++;
170 return true;
171 }
172 else if(contentCode==0x86dd && (d_pheader.caplen >= (d_skipMediaHeader + sizeof(*d_ip6))) && d_ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt==17) { // udpv6, we ignore anything with extension hdr
173 if (d_pheader.caplen < (d_skipMediaHeader + sizeof(struct ip6_hdr) + sizeof(struct udphdr))) {
174 d_runts++;
175 continue;
176 }
177 d_udp=reinterpret_cast<const struct udphdr*>(d_buffer + d_skipMediaHeader + sizeof(struct ip6_hdr));
178 d_payload = (unsigned char*)d_udp + sizeof(struct udphdr);
179 d_len = ntohs(d_udp->uh_ulen) - sizeof(struct udphdr);
180 if (d_pheader.caplen < (d_skipMediaHeader + sizeof(struct ip6_hdr) + sizeof(struct udphdr) + d_len)) {
181 d_runts++;
182 continue;
183 }
184 if((const char*)d_payload + d_len > d_buffer + d_pheader.caplen) {
185 d_runts++;
186 continue;
187 }
188
189 d_correctpackets++;
190 return true;
191 }
192 else {
193 d_nonetheripudp++;
194 }
195 }
196 }
197 catch(const EofException&) {
198 return false;
199 }
200
201 ComboAddress PcapPacketReader::getSource() const
202 {
203 ComboAddress ret;
204 if(d_ip->ip_v == 4) {
205 ret.sin4.sin_family = AF_INET;
206 ret.sin4.sin_addr = d_ip->ip_src;
207 ret.sin4.sin_port = d_udp->uh_sport; // should deal with TCP too!
208 } else {
209 ret.sin6.sin6_family = AF_INET6;
210 ret.sin6.sin6_addr = d_ip6->ip6_src;
211 ret.sin6.sin6_port = d_udp->uh_sport; // should deal with TCP too!
212 }
213 return ret;
214 }
215
216 ComboAddress PcapPacketReader::getDest() const
217 {
218 ComboAddress ret;
219 if(d_ip->ip_v == 4) {
220 ret.sin4.sin_family = AF_INET;
221 ret.sin4.sin_addr = d_ip->ip_dst;
222 ret.sin4.sin_port = d_udp->uh_dport; // should deal with TCP too!
223 } else {
224 ret.sin6.sin6_family = AF_INET6;
225 ret.sin6.sin6_addr = d_ip6->ip6_dst;
226 ret.sin6.sin6_port = d_udp->uh_dport; // should deal with TCP too!
227 }
228 return ret;
229 }
230
231 PcapPacketWriter::PcapPacketWriter(const string& fname, const PcapPacketReader& ppr) : PcapPacketWriter(fname)
232 {
233 setPPR(ppr);
234 }
235
236 PcapPacketWriter::PcapPacketWriter(const string& fname) : d_fname(fname)
237 {
238 d_fp = pdns::openFileForWriting(fname, 0600, true, false);
239 if (!d_fp) {
240 unixDie("Unable to open file");
241 }
242
243 int flags = fcntl(fileno(d_fp.get()), F_GETFL, 0);
244 fcntl(fileno(d_fp.get()), F_SETFL,flags & (~O_NONBLOCK)); // bsd needs this in stdin (??)
245 }
246
247 void PcapPacketWriter::write()
248 {
249 if (!d_ppr) {
250 return;
251 }
252
253 if(d_first) {
254 fwrite(&d_ppr->d_pfh, 1, sizeof(d_ppr->d_pfh), d_fp.get());
255 d_first=false;
256 }
257 fwrite(&d_ppr->d_pheader, 1, sizeof(d_ppr->d_pheader), d_fp.get());
258 fwrite(d_ppr->d_buffer, 1, d_ppr->d_pheader.caplen, d_fp.get());
259 }