]> git.ipfire.org Git - thirdparty/pdns.git/blobdiff - pdns/dnswasher.cc
Merge pull request #8223 from PowerDNS/omoerbeek-patch-1
[thirdparty/pdns.git] / pdns / dnswasher.cc
index ef18b6f56474cebb888a1db5d533e683fc2e07dc..c3da28c2d595845a749e49d34922b9b72779ce00 100644 (file)
@@ -1,3 +1,25 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
 /** two modes:
 
 anonymizing and stripping tcpdumps of irrelevant traffic, so operators can send non-privacy violating dumps
@@ -18,19 +40,43 @@ otherwise, obfuscate the response IP address
 #include "statbag.hh"
 #include "dnspcap.hh"
 #include "iputils.hh"
-
+#include "ipcipher.hh"
 #include "namespaces.hh"
+#include <boost/program_options.hpp>
+#include "base64.hh"
 
 StatBag S;
 
+namespace po = boost::program_options;
+po::variables_map g_vm;
+
+
 class IPObfuscator
 {
 public:
-  IPObfuscator() : d_romap(d_ipmap), d_ro6map(d_ip6map), d_counter(0)
+  virtual ~IPObfuscator()
+  {
+  }
+  virtual uint32_t obf4(uint32_t orig)=0;
+  virtual struct in6_addr obf6(const struct in6_addr& orig)=0;
+};
+
+class IPSeqObfuscator : public IPObfuscator
+{
+public:
+  IPSeqObfuscator() : d_romap(d_ipmap), d_ro6map(d_ip6map), d_counter(0)
+  {
+  }
+
+  ~IPSeqObfuscator()
+  {}
+
+  static std::unique_ptr<IPObfuscator> make()
   {
+    return std::unique_ptr<IPObfuscator>(new IPSeqObfuscator());
   }
 
-  uint32_t obf4(uint32_t orig)
+  uint32_t obf4(uint32_t orig) override
   {
     if(d_romap.count(orig))
       return d_ipmap[orig];
@@ -39,7 +85,7 @@ public:
     }
   }
 
-  struct in6_addr obf6(const struct in6_addr& orig)
+  struct in6_addr obf6(const struct in6_addr& orig) override
   {
     uint32_t val;
     if(d_ro6map.count(orig))
@@ -73,6 +119,48 @@ private:
   uint32_t d_counter;
 };
 
+class IPCipherObfuscator : public IPObfuscator
+{
+public:
+  IPCipherObfuscator(const std::string& key, bool decrypt)  : d_key(key), d_decrypt(decrypt)
+  {
+    if(d_key.size()!=16) {
+      throw std::runtime_error("IPCipher requires a 128 bit key");
+    }
+  }
+
+  ~IPCipherObfuscator()
+  {}
+  static std::unique_ptr<IPObfuscator> make(std::string key, bool decrypt)
+  {
+    return std::unique_ptr<IPObfuscator>(new IPCipherObfuscator(key, decrypt));
+  }
+
+  uint32_t obf4(uint32_t orig) override
+  {
+    ComboAddress ca;
+    ca.sin4.sin_family = AF_INET;
+    ca.sin4.sin_addr.s_addr = orig;
+    ca = d_decrypt ? decryptCA(ca, d_key) : encryptCA(ca, d_key);
+    return ca.sin4.sin_addr.s_addr;
+
+  }
+
+  struct in6_addr obf6(const struct in6_addr& orig) override
+  {
+    ComboAddress ca;
+    ca.sin4.sin_family = AF_INET6;
+    ca.sin6.sin6_addr = orig;
+    ca = d_decrypt ? decryptCA(ca, d_key) : encryptCA(ca, d_key);
+    return ca.sin6.sin6_addr;
+  }
+
+private:
+  std::string d_key;
+  bool d_decrypt;
+};
+
+
 void usage() {
   cerr<<"Syntax: dnswasher INFILE1 [INFILE2..] OUTFILE"<<endl;
 }
@@ -80,53 +168,98 @@ void usage() {
 int main(int argc, char** argv)
 try
 {
-  for (int i = 1; i < argc; i++) {
-    if ((string) argv[i] == "--help") {
-      usage();
-      exit(EXIT_SUCCESS);
-    }
+  po::options_description desc("Allowed options");
+  desc.add_options()
+    ("help,h", "produce help message")
+    ("version", "show version number")
+    ("key,k", po::value<string>(), "base64 encoded 128 bit key for ipcipher")
+    ("passphrase,p", po::value<string>(), "passphrase for ipcipher (will be used to derive key)")
+    ("decrypt,d", "decrypt IP addresses with ipcipher");
 
-    if ((string) argv[i] == "--version") {
-      cerr<<"dnswasher "<<VERSION<<endl;
-      exit(EXIT_SUCCESS);
-    }
+  po::options_description alloptions;
+  po::options_description hidden("hidden options");
+  hidden.add_options()
+    ("infiles", po::value<vector<string>>(), "PCAP source file(s)")
+    ("outfile", po::value<string>(), "outfile");
+
+
+  alloptions.add(desc).add(hidden);
+  po::positional_options_description p;
+  p.add("infiles", 1);
+  p.add("outfile", 1);
+
+  po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm);
+  po::notify(g_vm);
+
+  if(g_vm.count("help")) {
+    usage();
+    cout<<desc<<endl;
+    exit(EXIT_SUCCESS);
   }
 
-  if(argc < 3) {
+  if(g_vm.count("version")) {
+    cout<<"dnswasher "<<VERSION<<endl;
+    exit(EXIT_SUCCESS);
+  }
+
+  if(!g_vm.count("outfile")) {
+    cout<<"Missing outfile"<<endl;
     usage();
-    exit(1);
+    exit(EXIT_FAILURE);
   }
 
-  PcapPacketWriter pw(argv[argc-1]);
-  IPObfuscator ipo;
-  // 0          1   2   3    - argc == 4
-  // dnswasher in1 in2 out
-  for(int n=1; n < argc -1; ++n) {
-    PcapPacketReader pr(argv[n]);
+  bool doDecrypt = g_vm.count("decrypt");
+
+  PcapPacketWriter pw(g_vm["outfile"].as<string>());
+  std::unique_ptr<IPObfuscator> ipo;
+
+  if(!g_vm.count("key") && !g_vm.count("passphrase"))
+    ipo = IPSeqObfuscator::make();
+  else if(g_vm.count("key") && !g_vm.count("passphrase")) {
+    string key;
+    if(B64Decode(g_vm["key"].as<string>(), key) < 0) {
+      cerr<<"Invalidly encoded base64 key provided"<<endl;
+      exit(EXIT_FAILURE);
+    }
+    ipo = IPCipherObfuscator::make(key, doDecrypt);
+  }
+  else if(!g_vm.count("key") && g_vm.count("passphrase")) {
+    string key = makeIPCipherKey(g_vm["passphrase"].as<string>());
+
+    ipo = IPCipherObfuscator::make(key, doDecrypt);
+  }
+  else {
+    cerr<<"Can't specify both 'key' and 'passphrase'"<<endl;
+    exit(EXIT_FAILURE);
+  }
+
+  for(const auto& inf : g_vm["infiles"].as<vector<string>>()) {
+    PcapPacketReader pr(inf);
     pw.setPPR(pr);
 
     while(pr.getUDPPacket()) {
       if(ntohs(pr.d_udp->uh_dport)==53 || (ntohs(pr.d_udp->uh_sport)==53 && pr.d_len > sizeof(dnsheader))) {
         dnsheader* dh=(dnsheader*)pr.d_payload;
-        
+
         if (pr.d_ip->ip_v == 4){
           uint32_t *src=(uint32_t*)&pr.d_ip->ip_src;
           uint32_t *dst=(uint32_t*)&pr.d_ip->ip_dst;
-          
+
           if(dh->qr)
-            *dst=htonl(ipo.obf4(*dst));
+            *dst=ipo->obf4(*dst);
           else
-            *src=htonl(ipo.obf4(*src));
-          
+            *src=ipo->obf4(*src);
+
           pr.d_ip->ip_sum=0;
         } else if (pr.d_ip->ip_v == 6) {
           auto src=&pr.d_ip6->ip6_src;
           auto dst=&pr.d_ip6->ip6_dst;
-          
+
           if(dh->qr)
-            *dst=ipo.obf6(*dst);
+            *dst=ipo->obf6(*dst);
           else
-            *src=ipo.obf6(*src);
+            *src=ipo->obf6(*src);
+          // IPv6 checksum does not cover source/destination addresses
         }
         pw.write();
       }