]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnspcap.cc
Merge pull request #7820 from pieterlexis/systemd-no-setuid
[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=fopen(fname.c_str(),"r");
34 if(!d_fp)
35 unixDie("Unable to open file " + fname);
36
37 int flags=fcntl(fileno(d_fp),F_GETFL,0);
38 fcntl(fileno(d_fp), F_SETFL,flags&(~O_NONBLOCK)); // bsd needs this in stdin (??)
39
40 checkedFread(&d_pfh);
41
42 if(d_pfh.magic != 2712847316UL)
43 throw runtime_error((format("PCAP file %s has bad magic %x, should be %x") % fname % d_pfh.magic % 2712847316UL).str());
44
45 if( d_pfh.linktype==1) {
46 d_skipMediaHeader=sizeof(struct ether_header);
47 }
48 else if(d_pfh.linktype==101) {
49 d_skipMediaHeader=0;
50 }
51 else if(d_pfh.linktype==113) {
52 d_skipMediaHeader=16;
53 }
54 else throw runtime_error((format("Unsupported link type %d") % d_pfh.linktype).str());
55
56 d_runts = d_oversized = d_correctpackets = d_nonetheripudp = 0;
57 }
58
59 PcapPacketReader::~PcapPacketReader()
60 {
61 fclose(d_fp);
62 }
63
64
65 void PcapPacketReader::checkedFreadSize(void* ptr, size_t size)
66 {
67 int ret=fread(ptr, 1, size, d_fp);
68 if(ret < 0)
69 unixDie( (format("Error reading %d bytes from %s") % size % d_fname).str());
70
71 if(!ret)
72 throw EofException();
73
74 if((size_t)ret != size)
75 throw EofException((format("Incomplete read from '%s', got only %d bytes") % d_fname % ret).str());
76 }
77
78 bool PcapPacketReader::getUDPPacket()
79 try
80 {
81 for(;;) {
82 checkedFread(&d_pheader);
83 if(!d_pheader.caplen) {
84 d_runts++;
85 continue;
86 }
87
88 if(d_pheader.caplen > sizeof(d_buffer)) {
89 d_oversized++;
90 throw runtime_error((format("Can't handle a %d byte packet, have space for %d") % d_pheader.caplen % sizeof(d_buffer)).str());
91 }
92
93 checkedFreadSize(d_buffer, d_pheader.caplen);
94
95 if(d_pheader.caplen < d_pheader.len) {
96 d_runts++;
97 continue;
98 }
99
100 if (d_pheader.caplen < d_skipMediaHeader) {
101 d_runts++;
102 continue;
103 }
104
105 d_ip=reinterpret_cast<struct ip*>(d_buffer + d_skipMediaHeader);
106 d_ip6=reinterpret_cast<struct ip6_hdr*>(d_buffer + d_skipMediaHeader);
107 uint16_t contentCode=0;
108
109 if(d_pfh.linktype==1) {
110 if (d_pheader.caplen < sizeof(*d_ether)) {
111 d_runts++;
112 continue;
113 }
114 d_ether=reinterpret_cast<struct ether_header*>(d_buffer);
115 contentCode=ntohs(d_ether->ether_type);
116 }
117 else if(d_pfh.linktype==101) {
118 if (d_pheader.caplen < (d_skipMediaHeader + sizeof(*d_ip))) {
119 d_runts++;
120 continue;
121 }
122 if(d_ip->ip_v==4)
123 contentCode = 0x0800;
124 else
125 contentCode = 0x86dd;
126 }
127 else if(d_pfh.linktype==113) {
128 if (d_pheader.caplen < sizeof(*d_lcc)) {
129 d_runts++;
130 continue;
131 }
132 d_lcc=reinterpret_cast<struct pdns_lcc_header*>(d_buffer);
133 contentCode=ntohs(d_lcc->lcc_protocol);
134 }
135
136 if(contentCode==0x0800 && (d_pheader.caplen >= (d_skipMediaHeader + sizeof(*d_ip))) && d_ip->ip_p==17) { // udp
137 if (d_pheader.caplen < (d_skipMediaHeader + (4 * d_ip->ip_hl) + sizeof(*d_udp))) {
138 d_runts++;
139 continue;
140 }
141 d_udp=reinterpret_cast<const struct udphdr*>(d_buffer + d_skipMediaHeader + 4 * d_ip->ip_hl);
142 d_payload = (unsigned char*)d_udp + sizeof(struct udphdr);
143 d_len = ntohs(d_udp->uh_ulen) - sizeof(struct udphdr);
144 if (d_pheader.caplen < (d_skipMediaHeader + (4 * d_ip->ip_hl) + sizeof(*d_udp) + d_len)) {
145 d_runts++;
146 continue;
147 }
148 if((const char*)d_payload + d_len > d_buffer + d_pheader.caplen) {
149 d_runts++;
150 continue;
151 }
152 d_correctpackets++;
153 return true;
154 }
155 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
156 if (d_pheader.caplen < (d_skipMediaHeader + sizeof(struct ip6_hdr) + sizeof(struct udphdr))) {
157 d_runts++;
158 continue;
159 }
160 d_udp=reinterpret_cast<const struct udphdr*>(d_buffer + d_skipMediaHeader + sizeof(struct ip6_hdr));
161 d_payload = (unsigned char*)d_udp + sizeof(struct udphdr);
162 d_len = ntohs(d_udp->uh_ulen) - sizeof(struct udphdr);
163 if (d_pheader.caplen < (d_skipMediaHeader + sizeof(struct ip6_hdr) + sizeof(struct udphdr) + d_len)) {
164 d_runts++;
165 continue;
166 }
167 if((const char*)d_payload + d_len > d_buffer + d_pheader.caplen) {
168 d_runts++;
169 continue;
170 }
171
172 d_correctpackets++;
173 return true;
174 }
175 else {
176 d_nonetheripudp++;
177 }
178 }
179 }
180 catch(const EofException&) {
181 return false;
182 }
183
184 ComboAddress PcapPacketReader::getSource() const
185 {
186 ComboAddress ret;
187 if(d_ip->ip_v == 4) {
188 ret.sin4.sin_family = AF_INET;
189 ret.sin4.sin_addr = d_ip->ip_src;
190 ret.sin4.sin_port = d_udp->uh_sport; // should deal with TCP too!
191 } else {
192 ret.sin6.sin6_family = AF_INET6;
193 ret.sin6.sin6_addr = d_ip6->ip6_src;
194 ret.sin6.sin6_port = d_udp->uh_sport; // should deal with TCP too!
195 }
196 return ret;
197 }
198
199 ComboAddress PcapPacketReader::getDest() const
200 {
201 ComboAddress ret;
202 if(d_ip->ip_v == 4) {
203 ret.sin4.sin_family = AF_INET;
204 ret.sin4.sin_addr = d_ip->ip_dst;
205 ret.sin4.sin_port = d_udp->uh_dport; // should deal with TCP too!
206 } else {
207 ret.sin6.sin6_family = AF_INET6;
208 ret.sin6.sin6_addr = d_ip6->ip6_dst;
209 ret.sin6.sin6_port = d_udp->uh_dport; // should deal with TCP too!
210 }
211 return ret;
212 }
213
214 PcapPacketWriter::PcapPacketWriter(const string& fname, const PcapPacketReader& ppr) : PcapPacketWriter(fname)
215 {
216 setPPR(ppr);
217 }
218
219 PcapPacketWriter::PcapPacketWriter(const string& fname) : d_fname(fname)
220 {
221 d_fp=fopen(fname.c_str(),"w");
222 if(!d_fp)
223 unixDie("Unable to open file");
224
225 int flags=fcntl(fileno(d_fp),F_GETFL,0);
226 fcntl(fileno(d_fp), F_SETFL,flags&(~O_NONBLOCK)); // bsd needs this in stdin (??)
227 }
228
229 void PcapPacketWriter::write()
230 {
231 if (!d_ppr) {
232 return;
233 }
234
235 if(d_first) {
236 fwrite(&d_ppr->d_pfh, 1, sizeof(d_ppr->d_pfh), d_fp);
237 d_first=false;
238 }
239 fwrite(&d_ppr->d_pheader, 1, sizeof(d_ppr->d_pheader), d_fp);
240 fwrite(d_ppr->d_buffer, 1, d_ppr->d_pheader.caplen, d_fp);
241 }
242
243 PcapPacketWriter::~PcapPacketWriter()
244 {
245 fclose(d_fp);
246 }