]> granicus.if.org Git - pdns/commitdiff
ixfrdist: first implementation of the update thread
authorPieter Lexis <pieter.lexis@powerdns.com>
Wed, 10 Jan 2018 16:15:06 +0000 (17:15 +0100)
committerPieter Lexis <pieter.lexis@powerdns.com>
Mon, 29 Jan 2018 08:20:13 +0000 (09:20 +0100)
pdns/ixfrdist.cc
pdns/ixfrutils.cc

index 8de866c315d5319bacf5f9d078e1d0df204cc891..74bf3882da2abc31de062be351b86fdce9a70d4e 100644 (file)
 #include "config.h"
 #endif
 #include <boost/program_options.hpp>
+#include <sys/stat.h>
 #include "ixfr.hh"
 #include "ixfrutils.hh"
+#include "resolver.hh"
+#include "dns_random.hh"
 
 /* BEGIN Needed because of deeper dependencies */
 #include "arguments.hh"
@@ -42,20 +45,110 @@ using namespace boost::multi_index;
 
 namespace po = boost::program_options;
 po::variables_map g_vm;
+string g_workdir;
+ComboAddress g_master;
+bool g_verbose = false;
 
 void usage(po::options_description &desc) {
   cerr << "Usage: ixfrdist [OPTION]... DOMAIN [DOMAIN]..."<<endl;
   cerr << desc << "\n";
 }
 
-void updateThread() {
-}
+void updateThread(const vector<DNSName> &domains) {
+  std::map<DNSName, uint32_t> serials;
+  std::map<DNSName, time_t> lastCheck;
+
+  // Initialize the serials we have
+  for (const auto &domain : domains) {
+    lastCheck[domain] = 0;
+    string dir = g_workdir + "/" + domain.toString();
+    try {
+      serials[domain] = getSerialsFromDir(dir);
+    } catch (runtime_error &e) {
+      // Most likely, the directory does not exist.
+      cerr<<"[INFO] "<<e.what()<<", attempting to create"<<endl;
+      // Attempt to create it, if _that_ fails, there is no hope
+      if (mkdir(dir.c_str(), 0777) == -1) {
+        cerr<<"[ERROR] Could not create '"<<dir<<"': "<<strerror(errno)<<endl;
+        exit(EXIT_FAILURE);
+      }
+    }
+  }
+
+  while (true) {
+    time_t now = time(nullptr);
+    for (const auto &domain : domains) {
+      string dir = g_workdir + "/" + domain.toString();
+      if (now - lastCheck[domain] < 30) { // YOLO 30 seconds
+          continue;
+      }
+      if (serials.find(domain) != serials.end() && serials[domain] != 0) {
+        if (g_verbose) {
+          cerr<<"[INFO] Attempting to retrieve SOA Serial update for '"<<domain<<"' from '"<<g_master.toStringWithPort()<<"'"<<endl;
+        }
+        shared_ptr<SOARecordContent> sr;
+        try {
+          auto newSerial = getSerialFromMaster(g_master, domain, sr); // TODO TSIG
+          if (g_verbose) {
+            cerr<<"[INFO]   Got SOA Serial: "<< newSerial<<", had Serial: "<<serials[domain]<<endl;
+          }
+          if (newSerial == serials[domain]) {
+            if (g_verbose) {
+              cerr<<"[INFO]   Not updating."<<endl;
+            }
+            continue;
+          }
+        } catch (runtime_error &e) {
+          cerr<<"[WARNING] Unable to get SOA serial update for '"<<domain<<"': "<<e.what()<<endl;
+          continue;
+        }
+      }
+      // Now get the full zone!
+      if (g_verbose) {
+        cerr<<"[INFO] Attempting to receive full zonedata for '"<<domain<<"'"<<endl;
+      }
+      ComboAddress local = g_master.sin4.sin_family == AF_INET ? ComboAddress("0.0.0.0") : ComboAddress("::");
+      TSIGTriplet tt;
+      try {
+        AXFRRetriever axfr(g_master, domain, tt, &local);
+        unsigned int nrecords=0;
+        Resolver::res_t nop;
+        vector<DNSRecord> chunk;
+        records_t records;
+        while(axfr.getChunk(nop, &chunk)) {
+          for(auto& dr : chunk) {
+            if(dr.d_type == QType::TSIG)
+              continue;
+            dr.d_name.makeUsRelative(domain);
+            records.insert(dr);
+            nrecords++;
+          }
+        }
+        if (g_verbose) {
+          cerr<<"[INFO]   Done! Received "<<nrecords<<" records. Attempting to write to disk!"<<endl;
+        }
+        writeZoneToDisk(records, domain, dir);
+        if (g_verbose) {
+          cerr<<"[INFO]   Done!"<<endl;
+        }
+      } catch (ResolverException &e) {
+        cerr<<"[WARNING] Could not retrieve AXFR: "<<e.reason<<endl;
+      } catch (runtime_error &e) {
+        cerr<<"[WARNING] Could not save zone to disk: "<<e.what()<<endl;
+      }
+      serials[domain] = getSerialsFromDir(dir);
+      lastCheck[domain] = now;
+    } /* for (const auto &domain : domains) */
+    sleep(10);
+  } /* while (true) */
+} /* updateThread */
 
 int main(int argc, char** argv) {
   po::options_description desc("IXFR distribution tool");
   desc.add_options()
     ("help", "produce help message")
     ("version", "Display the version of ixfrdist")
+    ("verbose", "Be verbose")
     ("listen-address", po::value< vector< string>>(), "IP Address(es) to listen on")
     ("server-address", po::value<string>()->default_value("127.0.0.1:5300"), "server address")
     ("work-dir", po::value<string>()->default_value("."), "Directory for storing AXFR and IXFR data")
@@ -82,6 +175,10 @@ int main(int argc, char** argv) {
     return EXIT_SUCCESS;
   }
 
+  if (g_vm.count("verbose")) {
+    g_verbose = true;
+  }
+
   bool had_error = false;
 
   vector<ComboAddress> listen_addresses = {ComboAddress("127.0.0.1:53")};
@@ -99,7 +196,7 @@ int main(int argc, char** argv) {
   }
 
   try {
-    ComboAddress serverAddress = ComboAddress(g_vm["server-address"].as<string>(), 53);
+    g_master = ComboAddress(g_vm["server-address"].as<string>(), 53);
   } catch(PDNSException &e) {
     cerr<<"[Error] server-address '"<<g_vm["server-address"].as<string>()<<"' is not an IP address: "<<e.reason<<endl;
     had_error = true;
@@ -120,8 +217,19 @@ int main(int argc, char** argv) {
       had_error = true;
     }
   }
+
+  g_workdir = g_vm["work-dir"].as<string>();
+
   if (had_error) {
     // We have already sent the errors to stderr, just die
     return EXIT_FAILURE;
   }
+
+  // It all starts here
+  // Init the things we need
+  reportAllTypes();
+  dns_random_init("0123456789abcdef");
+
+  // Updater thread (TODO: actually thread it :))
+  updateThread(domains);
 }
index 5be1931370bef9a5872628375ed2c11161255954..d21e107a12275d52433a5fb71260ad994b6ce57a 100644 (file)
  */
 
 #include <dirent.h>
+#include <errno.h>
 #include "ixfrutils.hh"
 #include "sstuff.hh"
 #include "dnssecinfra.hh"
 #include "zoneparser-tng.hh"
+#include "dnsparser.hh"
 
 uint32_t getSerialFromMaster(const ComboAddress& master, const DNSName& zone, shared_ptr<SOARecordContent>& sr, const TSIGTriplet& tt)
 {
@@ -53,8 +55,10 @@ uint32_t getSerialFromMaster(const ComboAddress& master, const DNSName& zone, sh
   }
   for(const auto& r: mdp.d_answers) {
     if(r.first.d_type == QType::SOA) {
-      sr = std::dynamic_pointer_cast<SOARecordContent>(r.first.d_content);
-      return sr->d_st.serial;
+      sr = getRR<SOARecordContent>(r.first);
+      if(sr != nullptr) {
+        return sr->d_st.serial;
+      }
     }
   }
   return 0;
@@ -65,7 +69,7 @@ uint32_t getSerialsFromDir(const std::string& dir)
   uint32_t ret=0;
   DIR* dirhdl=opendir(dir.c_str());
   if(!dirhdl)
-    throw runtime_error("Could not open IXFR directory");
+    throw runtime_error("Could not open IXFR directory '" + dir + "': " + strerror(errno));
   struct dirent *entry;
 
   while((entry = readdir(dirhdl))) {