]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnswasher.cc
Update rules-actions.rst
[thirdparty/pdns.git] / pdns / dnswasher.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
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
30 read a packet, check if it has the QR bit set.
31
32 If the question has the response bit set, obfuscate the destination IP address
33 otherwise, obfuscate the response IP address
34 */
35
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40 #include "statbag.hh"
41 #include "dnspcap.hh"
42 #include "iputils.hh"
43 #include "ipcipher.hh"
44 #include "namespaces.hh"
45 #include <boost/program_options.hpp>
46 #include "base64.hh"
47
48 StatBag S;
49
50 namespace po = boost::program_options;
51 po::variables_map g_vm;
52
53
54 class IPObfuscator
55 {
56 public:
57 virtual ~IPObfuscator()
58 {
59 }
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)
68 {
69 }
70
71 ~IPSeqObfuscator()
72 {}
73
74 static std::unique_ptr<IPObfuscator> make()
75 {
76 return std::unique_ptr<IPObfuscator>(new IPSeqObfuscator());
77 }
78
79 uint32_t obf4(uint32_t orig) override
80 {
81 if(d_romap.count(orig))
82 return d_ipmap[orig];
83 else {
84 return d_ipmap[orig]=d_counter++;
85 }
86 }
87
88 struct in6_addr obf6(const struct in6_addr& orig) override
89 {
90 uint32_t val;
91 if(d_ro6map.count(orig))
92 val=d_ip6map[orig];
93 else {
94 val=d_ip6map[orig]=d_counter++;
95 }
96 struct in6_addr ret;
97
98 val=htonl(val);
99 memset(&ret, 0, sizeof(ret));
100 memcpy(((char*)&ret)+12, &val, 4);
101 return ret;
102 }
103
104 private:
105 map<uint32_t, uint32_t> d_ipmap;
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 };
114 // For IPv6 addresses
115 map<struct in6_addr, uint32_t, cmp> d_ip6map;
116 const decltype(d_ip6map)& d_ro6map;
117
118 // The counter that we'll convert to an IP address
119 uint32_t d_counter;
120 };
121
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 }
138
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
164 void usage() {
165 cerr<<"Syntax: dnswasher INFILE1 [INFILE2..] OUTFILE"<<endl;
166 }
167
168 int main(int argc, char** argv)
169 try
170 {
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");
178
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");
184
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);
198 }
199
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;
207 usage();
208 exit(EXIT_FAILURE);
209 }
210
211 bool doDecrypt = g_vm.count("decrypt");
212
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>());
228
229 ipo = IPCipherObfuscator::make(key, doDecrypt);
230 }
231 else {
232 cerr<<"Can't specify both 'key' and 'passphrase'"<<endl;
233 exit(EXIT_FAILURE);
234 }
235
236 for(const auto& inf : g_vm["infiles"].as<vector<string>>()) {
237 PcapPacketReader pr(inf);
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;
243
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;
247
248 if(dh->qr)
249 *dst=ipo->obf4(*dst);
250 else
251 *src=ipo->obf4(*src);
252
253 pr.d_ip->ip_sum=0;
254 } else if (pr.d_ip->ip_v == 6) {
255 auto src=&pr.d_ip6->ip6_src;
256 auto dst=&pr.d_ip6->ip6_dst;
257
258 if(dh->qr)
259 *dst=ipo->obf6(*dst);
260 else
261 *src=ipo->obf6(*src);
262 // IPv6 checksum does not cover source/destination addresses
263 }
264 pw.write();
265 }
266 }
267 cerr<<"Saw "<<pr.d_correctpackets<<" correct packets, "<<pr.d_runts<<" runts, "<< pr.d_oversized<<" oversize, "<<
268 pr.d_nonetheripudp<<" unknown encaps"<<endl;
269 }
270 }
271 catch(std::exception& e)
272 {
273 cerr<<"Fatal: "<<e.what()<<endl;
274 }