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"
PcapPacketReader(const string & fname)31 PcapPacketReader::PcapPacketReader(const string& fname) : d_fname(fname)
32 {
33   d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(fname.c_str(), "r"), fclose);
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 
checkedFreadSize(void * ptr,size_t size)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 
getUDPPacket()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 
getSource() const201 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 
getDest() const216 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 
PcapPacketWriter(const string & fname,const PcapPacketReader & ppr)231 PcapPacketWriter::PcapPacketWriter(const string& fname, const PcapPacketReader& ppr) : PcapPacketWriter(fname)
232 {
233   setPPR(ppr);
234 }
235 
PcapPacketWriter(const string & fname)236 PcapPacketWriter::PcapPacketWriter(const string& fname) : d_fname(fname)
237 {
238   d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(fname.c_str(),"w"), fclose);
239 
240   if (!d_fp) {
241     unixDie("Unable to open file");
242   }
243 
244   int flags = fcntl(fileno(d_fp.get()), F_GETFL, 0);
245   fcntl(fileno(d_fp.get()), F_SETFL,flags & (~O_NONBLOCK)); // bsd needs this in stdin (??)
246 }
247 
write()248 void PcapPacketWriter::write()
249 {
250   if (!d_ppr) {
251     return;
252   }
253 
254   if(d_first) {
255     fwrite(&d_ppr->d_pfh, 1, sizeof(d_ppr->d_pfh), d_fp.get());
256     d_first=false;
257   }
258   fwrite(&d_ppr->d_pheader, 1, sizeof(d_ppr->d_pheader), d_fp.get());
259   fwrite(d_ppr->d_buffer, 1, d_ppr->d_pheader.caplen, d_fp.get());
260 }
261