]>
Commit | Line | Data |
---|---|---|
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 | } |