]>
Commit | Line | Data |
---|---|---|
12471842 PL |
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 | ||
ab06937b BH |
23 | /** two modes: |
24 | ||
25 | anonymizing and stripping tcpdumps of irrelevant traffic, so operators can send non-privacy violating dumps | |
26 | for analysis. | |
27 | ||
28 | algorithm: | |
29 | ||
9be88092 | 30 | read a packet, check if it has the QR bit set. |
ab06937b BH |
31 | |
32 | If the question has the response bit set, obfuscate the destination IP address | |
33 | otherwise, obfuscate the response IP address | |
ab06937b BH |
34 | */ |
35 | ||
11212b81 | 36 | |
870a0fe4 AT |
37 | #ifdef HAVE_CONFIG_H |
38 | #include "config.h" | |
39 | #endif | |
ab06937b | 40 | #include "statbag.hh" |
ab06937b | 41 | #include "dnspcap.hh" |
40ff5c7d | 42 | #include "iputils.hh" |
152b0801 | 43 | #include "ipcipher.hh" |
10f4eea8 | 44 | #include "namespaces.hh" |
152b0801 | 45 | #include <boost/program_options.hpp> |
46 | #include "base64.hh" | |
ab06937b BH |
47 | |
48 | StatBag S; | |
49 | ||
152b0801 | 50 | namespace po = boost::program_options; |
51 | po::variables_map g_vm; | |
52 | ||
53 | ||
ab06937b BH |
54 | class IPObfuscator |
55 | { | |
56 | public: | |
260936e8 OM |
57 | virtual ~IPObfuscator() |
58 | { | |
59 | } | |
152b0801 | 60 | virtual uint32_t obf4(uint32_t orig)=0; |
61 | virtual struct in6_addr obf6(const struct in6_addr& orig)=0; | |
62 | }; | |
63 | ||
64 | class IPSeqObfuscator : public IPObfuscator | |
65 | { | |
66 | public: | |
67 | IPSeqObfuscator() : d_romap(d_ipmap), d_ro6map(d_ip6map), d_counter(0) | |
ab06937b BH |
68 | { |
69 | } | |
70 | ||
152b0801 | 71 | ~IPSeqObfuscator() |
72 | {} | |
73 | ||
74 | static std::unique_ptr<IPObfuscator> make() | |
75 | { | |
76 | return std::unique_ptr<IPObfuscator>(new IPSeqObfuscator()); | |
77 | } | |
488bcb39 | 78 | |
152b0801 | 79 | uint32_t obf4(uint32_t orig) override |
ab06937b BH |
80 | { |
81 | if(d_romap.count(orig)) | |
82 | return d_ipmap[orig]; | |
83 | else { | |
84 | return d_ipmap[orig]=d_counter++; | |
85 | } | |
86 | } | |
87 | ||
152b0801 | 88 | struct in6_addr obf6(const struct in6_addr& orig) override |
cbb0be81 | 89 | { |
7856684d | 90 | uint32_t val; |
cbb0be81 | 91 | if(d_ro6map.count(orig)) |
7856684d | 92 | val=d_ip6map[orig]; |
cbb0be81 | 93 | else { |
7856684d | 94 | val=d_ip6map[orig]=d_counter++; |
cbb0be81 | 95 | } |
7856684d | 96 | struct in6_addr ret; |
647efa8b | 97 | |
7856684d | 98 | val=htonl(val); |
647efa8b | 99 | memset(&ret, 0, sizeof(ret)); |
7856684d | 100 | memcpy(((char*)&ret)+12, &val, 4); |
101 | return ret; | |
cbb0be81 PL |
102 | } |
103 | ||
ab06937b BH |
104 | private: |
105 | map<uint32_t, uint32_t> d_ipmap; | |
7856684d | 106 | const decltype(d_ipmap)& d_romap; |
107 | ||
108 | struct cmp { | |
109 | bool operator()(const struct in6_addr&a , const struct in6_addr&b) const | |
110 | { | |
111 | return memcmp(&a, &b, sizeof(a)) < 0; | |
112 | } | |
113 | }; | |
cbb0be81 | 114 | // For IPv6 addresses |
7856684d | 115 | map<struct in6_addr, uint32_t, cmp> d_ip6map; |
116 | const decltype(d_ip6map)& d_ro6map; | |
117 | ||
cbb0be81 | 118 | // The counter that we'll convert to an IP address |
ab06937b BH |
119 | uint32_t d_counter; |
120 | }; | |
121 | ||
152b0801 | 122 | class IPCipherObfuscator : public IPObfuscator |
123 | { | |
124 | public: | |
125 | IPCipherObfuscator(const std::string& key, bool decrypt) : d_key(key), d_decrypt(decrypt) | |
126 | { | |
127 | if(d_key.size()!=16) { | |
128 | throw std::runtime_error("IPCipher requires a 128 bit key"); | |
129 | } | |
130 | } | |
131 | ||
132 | ~IPCipherObfuscator() | |
133 | {} | |
134 | static std::unique_ptr<IPObfuscator> make(std::string key, bool decrypt) | |
135 | { | |
136 | return std::unique_ptr<IPObfuscator>(new IPCipherObfuscator(key, decrypt)); | |
137 | } | |
488bcb39 | 138 | |
152b0801 | 139 | uint32_t obf4(uint32_t orig) override |
140 | { | |
141 | ComboAddress ca; | |
142 | ca.sin4.sin_family = AF_INET; | |
143 | ca.sin4.sin_addr.s_addr = orig; | |
144 | ca = d_decrypt ? decryptCA(ca, d_key) : encryptCA(ca, d_key); | |
145 | return ca.sin4.sin_addr.s_addr; | |
146 | ||
147 | } | |
148 | ||
149 | struct in6_addr obf6(const struct in6_addr& orig) override | |
150 | { | |
151 | ComboAddress ca; | |
152 | ca.sin4.sin_family = AF_INET6; | |
153 | ca.sin6.sin6_addr = orig; | |
154 | ca = d_decrypt ? decryptCA(ca, d_key) : encryptCA(ca, d_key); | |
155 | return ca.sin6.sin6_addr; | |
156 | } | |
157 | ||
158 | private: | |
159 | std::string d_key; | |
160 | bool d_decrypt; | |
161 | }; | |
162 | ||
163 | ||
3d414a24 | 164 | void usage() { |
e795f590 | 165 | cerr<<"Syntax: dnswasher INFILE1 [INFILE2..] OUTFILE"<<endl; |
3d414a24 PL |
166 | } |
167 | ||
ab06937b BH |
168 | int main(int argc, char** argv) |
169 | try | |
170 | { | |
152b0801 | 171 | po::options_description desc("Allowed options"); |
172 | desc.add_options() | |
173 | ("help,h", "produce help message") | |
174 | ("version", "show version number") | |
175 | ("key,k", po::value<string>(), "base64 encoded 128 bit key for ipcipher") | |
176 | ("passphrase,p", po::value<string>(), "passphrase for ipcipher (will be used to derive key)") | |
177 | ("decrypt,d", "decrypt IP addresses with ipcipher"); | |
488bcb39 | 178 | |
152b0801 | 179 | po::options_description alloptions; |
180 | po::options_description hidden("hidden options"); | |
181 | hidden.add_options() | |
182 | ("infiles", po::value<vector<string>>(), "PCAP source file(s)") | |
183 | ("outfile", po::value<string>(), "outfile"); | |
3d414a24 | 184 | |
152b0801 | 185 | |
186 | alloptions.add(desc).add(hidden); | |
187 | po::positional_options_description p; | |
188 | p.add("infiles", 1); | |
189 | p.add("outfile", 1); | |
190 | ||
191 | po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm); | |
192 | po::notify(g_vm); | |
193 | ||
194 | if(g_vm.count("help")) { | |
195 | usage(); | |
196 | cout<<desc<<endl; | |
197 | exit(EXIT_SUCCESS); | |
3d414a24 PL |
198 | } |
199 | ||
152b0801 | 200 | if(g_vm.count("version")) { |
201 | cout<<"dnswasher "<<VERSION<<endl; | |
202 | exit(EXIT_SUCCESS); | |
203 | } | |
204 | ||
205 | if(!g_vm.count("outfile")) { | |
206 | cout<<"Missing outfile"<<endl; | |
3d414a24 | 207 | usage(); |
152b0801 | 208 | exit(EXIT_FAILURE); |
209 | } | |
210 | ||
211 | bool doDecrypt = g_vm.count("decrypt"); | |
488bcb39 | 212 | |
152b0801 | 213 | PcapPacketWriter pw(g_vm["outfile"].as<string>()); |
214 | std::unique_ptr<IPObfuscator> ipo; | |
215 | ||
216 | if(!g_vm.count("key") && !g_vm.count("passphrase")) | |
217 | ipo = IPSeqObfuscator::make(); | |
218 | else if(g_vm.count("key") && !g_vm.count("passphrase")) { | |
219 | string key; | |
220 | if(B64Decode(g_vm["key"].as<string>(), key) < 0) { | |
221 | cerr<<"Invalidly encoded base64 key provided"<<endl; | |
222 | exit(EXIT_FAILURE); | |
223 | } | |
224 | ipo = IPCipherObfuscator::make(key, doDecrypt); | |
225 | } | |
226 | else if(!g_vm.count("key") && g_vm.count("passphrase")) { | |
227 | string key = makeIPCipherKey(g_vm["passphrase"].as<string>()); | |
488bcb39 | 228 | |
152b0801 | 229 | ipo = IPCipherObfuscator::make(key, doDecrypt); |
230 | } | |
231 | else { | |
232 | cerr<<"Can't specify both 'key' and 'passphrase'"<<endl; | |
233 | exit(EXIT_FAILURE); | |
e2928fdb | 234 | } |
3d414a24 | 235 | |
152b0801 | 236 | for(const auto& inf : g_vm["infiles"].as<vector<string>>()) { |
237 | PcapPacketReader pr(inf); | |
e795f590 | 238 | pw.setPPR(pr); |
239 | ||
240 | while(pr.getUDPPacket()) { | |
241 | if(ntohs(pr.d_udp->uh_dport)==53 || (ntohs(pr.d_udp->uh_sport)==53 && pr.d_len > sizeof(dnsheader))) { | |
242 | dnsheader* dh=(dnsheader*)pr.d_payload; | |
488bcb39 | 243 | |
e795f590 | 244 | if (pr.d_ip->ip_v == 4){ |
245 | uint32_t *src=(uint32_t*)&pr.d_ip->ip_src; | |
246 | uint32_t *dst=(uint32_t*)&pr.d_ip->ip_dst; | |
488bcb39 | 247 | |
e795f590 | 248 | if(dh->qr) |
152b0801 | 249 | *dst=ipo->obf4(*dst); |
e795f590 | 250 | else |
152b0801 | 251 | *src=ipo->obf4(*src); |
488bcb39 | 252 | |
e795f590 | 253 | pr.d_ip->ip_sum=0; |
254 | } else if (pr.d_ip->ip_v == 6) { | |
7856684d | 255 | auto src=&pr.d_ip6->ip6_src; |
256 | auto dst=&pr.d_ip6->ip6_dst; | |
488bcb39 | 257 | |
e795f590 | 258 | if(dh->qr) |
152b0801 | 259 | *dst=ipo->obf6(*dst); |
e795f590 | 260 | else |
152b0801 | 261 | *src=ipo->obf6(*src); |
726b013f | 262 | // IPv6 checksum does not cover source/destination addresses |
e795f590 | 263 | } |
264 | pw.write(); | |
cbb0be81 | 265 | } |
ab06937b | 266 | } |
e795f590 | 267 | cerr<<"Saw "<<pr.d_correctpackets<<" correct packets, "<<pr.d_runts<<" runts, "<< pr.d_oversized<<" oversize, "<< |
268 | pr.d_nonetheripudp<<" unknown encaps"<<endl; | |
ab06937b | 269 | } |
ab06937b | 270 | } |
adc10f99 | 271 | catch(std::exception& e) |
ab06937b BH |
272 | { |
273 | cerr<<"Fatal: "<<e.what()<<endl; | |
274 | } |