]> granicus.if.org Git - pdns/commitdiff
Load AXFR-SOURCE when provided for domains
authorAki Tuomi <cmouse@desteem.org>
Wed, 30 Oct 2013 12:16:04 +0000 (14:16 +0200)
committerAki Tuomi <cmouse@desteem.org>
Mon, 18 Nov 2013 17:44:29 +0000 (19:44 +0200)
modules/tinydnsbackend/data
modules/tinydnsbackend/data.cdb
pdns/resolver.cc
pdns/resolver.hh
pdns/slavecommunicator.cc
regression-tests.nobackend/tinydns-data-check/expected_result
regression-tests/check_stest_source [new file with mode: 0644]
regression-tests/named.conf
regression-tests/start-test-stop
regression-tests/stest.com [new file with mode: 0644]
regression-tests/verify-dnssec-zone/expected_result

index 53daee570d747d52e6fb6592eba96c5f923f56cc..357a06b6fc32eefe095bfcf018171d6bbac86722 100644 (file)
@@ -20262,3 +20262,7 @@ Zminimal.com:ns1.example.com.:ahu.example.com.:2000081501:28800:7200:604800:8640
 Ztsig.com:ns1.example.com.:ahu.example.com.:2000081501:28800:7200:604800:86400:120
 &tsig.com::ns1.example.com.:120
 &tsig.com::ns2.example.com.:120
+#2000081501 auto axfr-get
+Zstest.com:ns1.example.com.:ahu.example.com.:2000081501:28800:7200:604800:86400:120
+&stest.com::ns1.example.com.:120
+&stest.com::ns2.example.com.:120
index 31341c202f159778483d32eca53442fb70890095..9b76b88f1298b8b4f9c31186f8f25a9e381509d4 100644 (file)
Binary files a/modules/tinydnsbackend/data.cdb and b/modules/tinydnsbackend/data.cdb differ
index e18ac2541eee73ac18d88f0d87005dd4c5e90357..fbf9fae5f8c8f54a617e34da2c153bdd793f7335 100644 (file)
@@ -88,28 +88,27 @@ int makeQuerySocket(const ComboAddress& local, bool udpOrTCP)
 Resolver::Resolver()
 try
 {
-  d_sock4 = d_sock6 = 0;
-  d_sock4 = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true);
+  locals["default4"] = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true);
   if(!::arg()["query-local-address6"].empty())
-    d_sock6 = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true);
+    locals["default6"] = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true);
   else 
-    d_sock6 = -1;
+    locals["default6"] = -1;
 }
 catch(...) {
-  if(d_sock4>=0)
-    close(d_sock4);
+  if(locals["default4"]>=0)
+    close(locals["default4"]);
   throw;
 }
 
 Resolver::~Resolver()
 {
-  if(d_sock4>=0)
-    Utility::closesocket(d_sock4);
-  if(d_sock6>=0)
-    Utility::closesocket(d_sock6);
+   for(std::map<std::string,int>::iterator iter = locals.begin(); iter != locals.end(); iter++) {
+       close(iter->second);
+   }
 }
 
-uint16_t Resolver::sendResolve(const ComboAddress& remote, const char *domain, int type, bool dnssecOK, 
+uint16_t Resolver::sendResolve(const ComboAddress& remote, const ComboAddress& local,
+                               const char *domain, int type, bool dnssecOK, 
                                const string& tsigkeyname, const string& tsigalgorithm, 
                                const string& tsigsecret)
 {
@@ -136,8 +135,26 @@ uint16_t Resolver::sendResolve(const ComboAddress& remote, const char *domain, i
     trc.d_eRcode=0;
     addTSIG(pw, &trc, tsigkeyname, tsigsecret, "", false);
   }
-    
-  int sock = remote.sin4.sin_family == AF_INET ? d_sock4 : d_sock6;
+  
+  int sock;
+
+  // choose socket based on local
+  if (local.sin4.sin_family == 0) {
+     // up to us. 
+     sock = remote.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
+  } else {
+     std::string lstr = local.toString();
+     std::map<std::string, int>::iterator lptr;
+     // see if there is a local
+
+     if ((lptr = locals.find(lstr)) != locals.end()) {
+        sock = lptr->second;
+     } else {
+        // try to make socket
+        sock = makeQuerySocket(local, true); 
+        locals[lstr] = sock;
+     }
+  }
   
   if(sendto(sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&remote), remote.getSocklen()) < 0) {
     throw ResolverException("Unable to ask query of "+remote.toStringWithPort()+": "+stringerror());
@@ -145,6 +162,16 @@ uint16_t Resolver::sendResolve(const ComboAddress& remote, const char *domain, i
   return randomid;
 }
 
+uint16_t Resolver::sendResolve(const ComboAddress& remote, const char *domain,
+                               int type, bool dnssecOK,
+                               const string& tsigkeyname, const string& tsigalgorithm,
+                               const string& tsigsecret)
+{
+   ComboAddress local;
+   local.sin4.sin_family = 0;
+   return this->sendResolve(remote, local, domain, type, dnssecOK, tsigkeyname, tsigalgorithm, tsigsecret);
+}
+
 static int parseResult(MOADNSParser& mdp, const std::string& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result)
 {
   result->clear();
@@ -203,13 +230,34 @@ static int parseResult(MOADNSParser& mdp, const std::string& origQname, uint16_t
 
 bool Resolver::tryGetSOASerial(string* domain, uint32_t *theirSerial, uint32_t *theirInception, uint32_t *theirExpire, uint16_t* id)
 {
-  Utility::setNonBlocking( d_sock4 );
-  Utility::setNonBlocking( d_sock6 );
-  
+  struct pollfd *fds = new struct pollfd[locals.size()];
+  size_t i = 0, k;
   int sock;
-  if(!waitFor2Data(d_sock4, d_sock6, 0, 250000, &sock)) // lame function, I know.. 
-    return false;
-  
+
+  for(std::map<string,int>::iterator iter=locals.begin(); iter != locals.end(); iter++, i++) {
+    fds[i].fd = iter->second;
+    fds[i].events = POLLIN;
+  }
+
+  if (poll(fds, i, 250) < 1) { // wait for 0.25s
+     delete [] fds;
+     return false;
+  }
+
+  sock = -1;
+
+  // determine who
+  for(k=0;k<i;k++) {
+    if ((fds[k].revents & POLLIN) == POLLIN) {
+      sock = fds[k].fd;
+      break;
+    }
+  }
+
+  delete [] fds;
+  if (sock < 0) return false; // false alarm
   int err;
   ComboAddress fromaddr;
   socklen_t addrlen=fromaddr.getSocklen();
@@ -218,10 +266,10 @@ bool Resolver::tryGetSOASerial(string* domain, uint32_t *theirSerial, uint32_t *
   if(err < 0) {
     if(errno == EAGAIN)
       return false;
-    
+
     throw ResolverException("recvfrom error waiting for answer: "+stringerror());
   }
-  
+
   MOADNSParser mdp((char*)buf, err);
   *id=mdp.d_header.id;
   *domain = stripDot(mdp.d_qname);
@@ -253,13 +301,27 @@ bool Resolver::tryGetSOASerial(string* domain, uint32_t *theirSerial, uint32_t *
   return true;
 }
 
-int Resolver::resolve(const string &ipport, const char *domain, int type, Resolver::res_t* res)
+int Resolver::resolve(const string &ipport, const char *domain, int type, Resolver::res_t* res, const ComboAddress &local)
 {
   try {
     ComboAddress to(ipport, 53);
 
-    int id = sendResolve(to, domain, type);
-    int sock =  to.sin4.sin_family == AF_INET ? d_sock4 : d_sock6;
+    int id = sendResolve(to, local, domain, type);
+    int sock;
+
+    // choose socket based on local
+    if (local.sin4.sin_family == 0) {
+       // up to us.
+       sock = to.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
+    } else {
+       std::string lstr = local.toString();
+       std::map<std::string, int>::iterator lptr;
+       // see if there is a local
+
+       if ((lptr = locals.find(lstr)) != locals.end()) sock = lptr->second;
+       else throw ResolverException("sendResolve did not create socket for " + lstr);
+    }
+
     int err=waitForData(sock, 0, 3000000); 
   
     if(!err) {
@@ -285,7 +347,11 @@ int Resolver::resolve(const string &ipport, const char *domain, int type, Resolv
   return -1;
 }
 
-
+int Resolver::resolve(const string &ipport, const char *domain, int type, Resolver::res_t* res) {
+   ComboAddress local;
+   local.sin4.sin_family = 0;
+   return resolve(ipport, domain, type, res, local);
+}
 
 void Resolver::getSoaSerial(const string &ipport, const string &domain, uint32_t *serial)
 {
index c917543c33ba747e7aac8f236b6283794d1c3972..d74c03df978d93d8cefcd9d05d801247662e3c87 100644 (file)
@@ -56,12 +56,17 @@ public:
 
   typedef vector<DNSResourceRecord> res_t;
   //! synchronously resolve domain|type at IP, store result in result, rcode in ret
+  int resolve(const string &ip, const char *domain, int type, res_t* result, const ComboAddress& local);
+
   int resolve(const string &ip, const char *domain, int type, res_t* result);
-  
+
   //! only send out a resolution request
+  uint16_t sendResolve(const ComboAddress& remote, const ComboAddress& local, const char *domain, int type, bool dnssecOk=false,
+    const string& tsigkeyname="", const string& tsigalgorithm="", const string& tsigsecret="");
+
   uint16_t sendResolve(const ComboAddress& remote, const char *domain, int type, bool dnssecOk=false,
     const string& tsigkeyname="", const string& tsigalgorithm="", const string& tsigsecret="");
-  
+
   //! see if we got a SOA response from our sendResolve
   bool tryGetSOASerial(string* theirDomain, uint32_t* theirSerial, uint32_t* theirInception, uint32_t* theirExpire, uint16_t* id);
   
@@ -69,8 +74,8 @@ public:
   void getSoaSerial(const string &, const string &, uint32_t *);
   
 private:
-  int d_sock4, d_sock6;
-  
+  std::map<std::string, int> locals;
   uint16_t d_randomid;
 };
 
index 8223b06c0dc393666880edd782a0812a2f4149e1..14f0cad44362dcf028dbc334eb14cf7da5dd5469 100644 (file)
@@ -393,6 +393,7 @@ struct DomainNotificationInfo
 {
   DomainInfo di;
   bool dnssecOk;
+  ComboAddress localaddr;
   string tsigkeyname, tsigalgname, tsigsecret;
 };
 }
@@ -423,12 +424,21 @@ struct SlaveSenderReceiver
     random_shuffle(dni.di.masters.begin(), dni.di.masters.end());
     try {
       ComboAddress remote(*dni.di.masters.begin());
-      return make_pair(dni.di.zone, 
-        d_resolver.sendResolve(ComboAddress(*dni.di.masters.begin(), 53), 
-          dni.di.zone.c_str(), 
-          QType::SOA, 
-          dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret)
-      );
+      if (dni.localaddr.sin4.sin_family == 0) {
+        return make_pair(dni.di.zone, 
+          d_resolver.sendResolve(ComboAddress(*dni.di.masters.begin(), 53), 
+            dni.di.zone.c_str(), 
+            QType::SOA, 
+            dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret)
+        );
+      } else {
+        return make_pair(dni.di.zone,
+          d_resolver.sendResolve(ComboAddress(*dni.di.masters.begin(), 53), dni.localaddr,
+            dni.di.zone.c_str(),
+            QType::SOA,
+            dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret)
+        );
+      }
     }
     catch(PDNSException& e) {
       throw runtime_error("While attempting to query freshness of '"+dni.di.zone+"': "+e.reason);
@@ -504,6 +514,7 @@ void CommunicatorClass::slaveRefresh(PacketHandler *P)
     domains_by_name_t& nameindex=boost::multi_index::get<IDTag>(d_suckdomains);
 
     BOOST_FOREACH(DomainInfo& di, rdomains) {
+      std::vector<std::string> localaddr;
       SuckRequest sr;
       sr.domain=di.zone;
       if(di.masters.empty()) // slave domains w/o masters are ignored
@@ -522,6 +533,22 @@ void CommunicatorClass::slaveRefresh(PacketHandler *P)
         B->getTSIGKey(dni.tsigkeyname, &dni.tsigalgname, &secret64);
         B64Decode(secret64, dni.tsigsecret);
       }
+
+      localaddr.clear();
+      // check for AXFR-SOURCE
+      if(B->getDomainMetadata(di.zone, "AXFR-SOURCE", localaddr) && !localaddr.empty()) {
+        try {
+          dni.localaddr = ComboAddress(localaddr[0]);
+          L<<Logger::Info<<"Freshness check source (AXFR-SOURCE) for domain '"<<di.zone<<"' set to "<<localaddr[0]<<endl;
+        }
+        catch(std::exception& e) {
+          L<<Logger::Error<<"Failed to load freshness check source '"<<localaddr[0]<<"' for '"<<di.zone<<"': "<<e.what()<<endl;
+          return;
+        }
+      } else {
+        dni.localaddr.sin4.sin_family = 0;
+      }
+
       sdomains.push_back(dni);
     }
   }
index dec523c5141b00a63cead360e6109f2fb14ac364..8507d6fea577e569a743c5d0ff1239249298b327 100644 (file)
@@ -8,4 +8,5 @@ a2dd754820cb88fdd3d80b54a212a270  ../regression-tests/test.com
 a63dc120391d9df0003f2ec4f461a6af  ../regression-tests/secure-delegated.dnssec-parent.com
 24514dc104b22206daeb973ff9303545  ../regression-tests/minimal.com
 0b20d7a0250576451135483b863750bf  ../regression-tests/tsig.com
-113e496cbfb8528775697bdc44deea9d  ../modules/tinydnsbackend/data.cdb
+b1f775045fa2cf0a3b91aa834af06e49  ../regression-tests/stest.com
+4d3d89309ee74b0b7c44042de92eb9e1  ../modules/tinydnsbackend/data.cdb
diff --git a/regression-tests/check_stest_source b/regression-tests/check_stest_source
new file mode 100644 (file)
index 0000000..b7d3bf1
--- /dev/null
@@ -0,0 +1,7 @@
+function prequery ( dnspacket )
+       qname, qtype = dnspacket:getQuestion()
+        remote = dnspacket:getRemote()
+       if qname == "stest.com" and remote != "127.0.0.2":
+               return false
+       return true
+end
index 6a81965fc44ca1de3582a78419e78e0932b13903..aecf40956317739715d4ba1aced458f8cc056cd3 100644 (file)
@@ -59,3 +59,8 @@ zone "tsig.com"{
        type master;
        file "./tsig.com";
 };
+
+zone "stest.com"{
+       type master;
+       file "./stest.com";
+};
index 4582eac3eb74dc1d2350dfb0a57e3bc77fa6382d..f146523cd261b6beb12f0f58b2b0d7d6151f67f1 100755 (executable)
@@ -369,6 +369,16 @@ then
 fi
 
 
+# check for lua
+if grep -q "^#define HAVE_LUA 1" ../config.h
+then
+  lua_prequery="--lua-prequery-script=./check_stest_source"
+  skiplua=0
+else
+  lua_prequery=""
+  skiplua=1
+fi
+
 case $context in
                ext-nsd | ext-nsd-nsec | ext-nsd-nsec3 | ext-nsd-nsec3-optout)
                        case $context in
@@ -477,7 +487,7 @@ case $context in
                                --send-root-referral \
                                --allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes --experimental-direct-dnskey=yes \
                                --cache-ttl=$cachettl --no-config \
-                               --bind-ignore-broken-records=yes &
+                               --bind-ignore-broken-records=yes $lua_prequery &
                        bindwait
                        ;;
 
@@ -606,7 +616,7 @@ __EOF__
                                --gmysql-dbname="$GMYSQLDB" \
                                --gmysql-user="$GMYSQLUSER" \
                                --gmysql-host="$GMYSQLHOST" \
-                               --gmysql-password="$GMYSQLPASSWD" &
+                               --gmysql-password="$GMYSQLPASSWD" $lua_prequery &
                        if [ $context = gmysql-nsec3 ]
                        then
                                extracontexts="dnssec nsec3"
@@ -660,7 +670,7 @@ __EOF__
                                --allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes \
                                --cache-ttl=$cachettl --no-config \
                                --gpgsql-dbname="$GPGSQLDB" \
-                               --gpgsql-user="$GPGSQLUSER" &
+                               --gpgsql-user="$GPGSQLUSER" $lua_prequery &
                        if [ $context = gpgsql-nsec3 ]
                        then
                                extracontexts="dnssec nsec3"
@@ -779,7 +789,7 @@ __EOF__
                                --send-root-referral \
                                --allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes \
                                --cache-ttl=$cachettl --no-config \
-                               --gsqlite3-database=pdns.sqlite3 &
+                               --gsqlite3-database=pdns.sqlite3 $lua_prequery &
                        if [ $context = gsqlite3-nsec3 ]
                        then
                                extracontexts="dnssec nsec3"
@@ -918,6 +928,10 @@ EOF
                        exit 1
 esac
 
+if [ "$skiplua" == "1" ]; then
+  skipreasons="$skipreaasons nolua"
+fi
+
 check_process
 
 startslave ()
@@ -953,6 +967,11 @@ startslave ()
                        -e "INSERT INTO tsigkeys (name, algorithm,secret) VALUES('test', '$ALGORITHM', '$KEY')"
                        mysql --user="$GMYSQL2USER" --password="$GMYSQL2PASSWD" --host="$GMYSQL2HOST" "$GMYSQL2DB" \
                        -e "INSERT INTO domainmetadata (domain_id, kind, content) SELECT id, 'AXFR-MASTER-TSIG', 'test' FROM domains WHERE name = 'tsig.com'"
+                       echo $skipreasons | grep -q nolua
+                       if [ $? -ne 0 ]; then
+                               mysql --user="$GMYSQL2USER" --password="$GMYSQL2PASSWD" --host="$GMYSQL2HOST" "$GMYSQL2DB" \
+                               -e "INSERT INTO domainmetadata (domain_id,kind,content) SELECT id,'AXFR-SOURCE','127.0.0.2' FROM domains WHERE name = 'stest.com'"
+                       fi
                fi
                set -e
 
@@ -1001,6 +1020,10 @@ startslave ()
                if [ $? -ne 0 ]; then
                        sqlite3 pdns.sqlite31 "INSERT INTO tsigkeys (name,algorithm,secret) VALUES('test','$ALGORITHM', '$KEY')"
                        sqlite3 pdns.sqlite31 "INSERT INTO domainmetadata (domain_id, kind, content) SELECT id, 'AXFR-MASTER-TSIG', 'test' FROM domains WHERE name = 'tsig.com'"
+                       echo $skipreasons | grep -q nolua
+                       if [ $? -ne 0 ]; then
+                               sqlite3 pdns.sqlite31  "INSERT INTO domainmetadata (domain_id,kind,content) SELECT id,'AXFR-SOURCE','127.0.0.2' FROM domains WHERE name = 'stest.com'"
+                       fi
                fi
                set -e
 
@@ -1052,6 +1075,10 @@ startslave ()
                if [ $? -ne 0 ]; then
                        sqlite3 dnssec-slave.sqlite3 "INSERT INTO tsigkeys (name, algorithm,secret) VALUES('test', '$ALGORITHM', '$KEY')"
                        sqlite3 dnssec-slave.sqlite3 "INSERT INTO domainmetadata (domain, kind, content) SELECT 'tsig.com', 'AXFR-MASTER-TSIG', 'test'"
+                       echo $skipreasons | grep -q nolua
+                       if [ $? -ne 0 ]; then
+                                sqlite3 dnssec-slave.sqlite3 "INSERT INTO domainmetadata (domain,kind,content) SELECT 'stest.com','AXFR-SOURCE','127.0.0.2'"
+                       fi
                fi
                set -e
 
diff --git a/regression-tests/stest.com b/regression-tests/stest.com
new file mode 100644 (file)
index 0000000..848a7a0
--- /dev/null
@@ -0,0 +1,12 @@
+$TTL 120
+$ORIGIN stest.com.
+@              IN      SOA     ns1.example.com.        ahu.example.com. (
+                       2000081501
+                       8H ; refresh
+                       2H ; retry
+                       1W ; expire
+                       1D ; default_ttl
+                       )
+
+@                      IN      NS      ns1.example.com.
+@                      IN      NS      ns2.example.com.
index ce5da3377909bbbee11da021df7542e692379947..68f1d54ba19bc45a7c3b5512971322d659aea352 100644 (file)
@@ -121,3 +121,18 @@ zone tsig.com/IN: loaded serial 2000081501 (DNSSEC signed)
 OK
 RETVAL: 0
 
+--- ldns-verify-zone -V2 stest.com
+RETVAL: 0
+
+--- validns stest.com
+RETVAL: 0
+
+--- jdnssec-verifyzone stest.com
+zone verified.
+RETVAL: 0
+
+--- named-checkzone stest.com
+zone stest.com/IN: loaded serial 2000081501 (DNSSEC signed)
+OK
+RETVAL: 0
+