]> granicus.if.org Git - pdns/commitdiff
send REFUSED for UDP queries we are unable to handle
authorPeter van Dijk <peter.van.dijk@powerdns.com>
Tue, 2 Oct 2018 10:17:31 +0000 (12:17 +0200)
committerPeter van Dijk <peter.van.dijk@powerdns.com>
Tue, 2 Oct 2018 10:21:25 +0000 (12:21 +0200)
pdns/ixfrdist.cc
regression-tests.ixfrdist/test_IXFR.py

index 2e29679d943906b3882413e10bf9f190a3b8cbee..98fc97e820f82662f16f1ceeaef8c3b1af4fd0b8 100644 (file)
@@ -423,15 +423,16 @@ static bool checkQuery(const MOADNSParser& mdp, const ComboAddress& saddr, const
     if (g_domainConfigs.find(mdp.d_qname) == g_domainConfigs.end()) {
       info_msg.push_back("Domain name '" + mdp.d_qname.toLogString() + "' is not configured for distribution");
     }
-
-    const auto zoneInfo = getCurrentZoneInfo(mdp.d_qname);
-    if (zoneInfo == nullptr) {
-      info_msg.push_back("Domain has not been transferred yet");
+    else {
+      const auto zoneInfo = getCurrentZoneInfo(mdp.d_qname);
+      if (zoneInfo == nullptr) {
+        info_msg.push_back("Domain has not been transferred yet");
+      }
     }
   }
 
   if (!info_msg.empty()) {
-    g_log<<Logger::Warning<<logPrefix<<"Ignoring "<<mdp.d_qname<<"|"<<QType(mdp.d_qtype).getName()<<" query from "<<saddr.toStringWithPort();
+    g_log<<Logger::Warning<<logPrefix<<"Refusing "<<mdp.d_qname<<"|"<<QType(mdp.d_qtype).getName()<<" query from "<<saddr.toStringWithPort();
     g_log<<Logger::Warning<<": ";
     bool first = true;
     for (const auto& s : info_msg) {
@@ -449,7 +450,7 @@ static bool checkQuery(const MOADNSParser& mdp, const ComboAddress& saddr, const
 }
 
 /*
- * Returns a vector<uint8_t> that represents the full response to a SOA
+ * Returns a vector<uint8_t> that represents the full positive response to a SOA
  * query. QNAME is read from mdp.
  */
 static bool makeSOAPacket(const MOADNSParser& mdp, vector<uint8_t>& packet) {
@@ -471,6 +472,20 @@ static bool makeSOAPacket(const MOADNSParser& mdp, vector<uint8_t>& packet) {
   return true;
 }
 
+/*
+ * Returns a vector<uint8_t> that represents the full REFUSED response to a
+ * query. QNAME and type are read from mdp.
+ */
+static bool makeRefusedPacket(const MOADNSParser& mdp, vector<uint8_t>& packet) {
+  DNSPacketWriter pw(packet, mdp.d_qname, mdp.d_qtype);
+  pw.getHeader()->id = mdp.d_header.id;
+  pw.getHeader()->rd = mdp.d_header.rd;
+  pw.getHeader()->qr = 1;
+  pw.getHeader()->rcode = RCode::Refused;
+
+  return true;
+}
+
 static vector<uint8_t> getSOAPacket(const MOADNSParser& mdp, const shared_ptr<SOARecordContent>& soa) {
   vector<uint8_t> packet;
   DNSPacketWriter pw(packet, mdp.d_qname, mdp.d_qtype);
@@ -710,23 +725,24 @@ static void handleUDPRequest(int fd, boost::any&) {
   }
 
   MOADNSParser mdp(true, string(buf, res));
-  if (!checkQuery(mdp, saddr)) {
-    return;
+  vector<uint8_t> packet;
+  if (checkQuery(mdp, saddr)) {
+    /* RFC 1995 Section 2
+     *    Transport of a query may be by either UDP or TCP.  If an IXFR query
+     *    is via UDP, the IXFR server may attempt to reply using UDP if the
+     *    entire response can be contained in a single DNS packet.  If the UDP
+     *    reply does not fit, the query is responded to with a single SOA
+     *    record of the server's current version to inform the client that a
+     *    TCP query should be initiated.
+     *
+     * Let's not complicate this with IXFR over UDP (and looking if we need to truncate etc).
+     * Just send the current SOA and let the client try over TCP
+     */
+    makeSOAPacket(mdp, packet);
+  } else {
+    makeRefusedPacket(mdp, packet);
   }
 
-  /* RFC 1995 Section 2
-   *    Transport of a query may be by either UDP or TCP.  If an IXFR query
-   *    is via UDP, the IXFR server may attempt to reply using UDP if the
-   *    entire response can be contained in a single DNS packet.  If the UDP
-   *    reply does not fit, the query is responded to with a single SOA
-   *    record of the server's current version to inform the client that a
-   *    TCP query should be initiated.
-   *
-   * Let's not complicate this with IXFR over UDP (and looking if we need to truncate etc).
-   * Just send the current SOA and let the client try over TCP
-   */
-  vector<uint8_t> packet;
-  makeSOAPacket(mdp, packet);
   if(sendto(fd, &packet[0], packet.size(), 0, (struct sockaddr*) &saddr, fromlen) < 0) {
     auto savedErrno = errno;
     g_log<<Logger::Warning<<"Could not send reply for "<<mdp.d_qname<<"|"<<QType(mdp.d_qtype).getName()<<" to "<<saddr.toStringWithPort()<<": "<<strerror(savedErrno)<<endl;
index 8f99316dda3c40ad7f6c096202b2397b12513116..23624c110ad618cc8560d9cf446587938aa5a6ec 100644 (file)
@@ -35,7 +35,8 @@ class IXFRDistBasicTest(IXFRDistTest):
 
     global xfrServerPort
     _xfrDone = 0
-    _config_domains = { 'example': '127.0.0.1:' + str(xfrServerPort) }
+    _config_domains = { 'example': '127.0.0.1:' + str(xfrServerPort),
+                        'example2': '127.0.0.1:1' } # bogus port is intentional
 
     @classmethod
     def setUpClass(cls):
@@ -104,11 +105,36 @@ class IXFRDistBasicTest(IXFRDistTest):
         # answers[1].sort(key=lambda rrset: (rrset.name, rrset.rdtype))
         self.assertEqual(answers, expected)
 
-    def testXFR(self):
+    def test_a_XFR(self):
         self.waitUntilCorrectSerialIsLoaded(1)
         self.checkFullZone(1)
 
         self.waitUntilCorrectSerialIsLoaded(2)
         self.checkFullZone(2)
 
-        self.checkIXFR(1,2)
\ No newline at end of file
+        self.checkIXFR(1,2)
+
+    # _b_ because we expect post-XFR testing state
+    def test_b_UDP_SOA_existing(self):
+        query = dns.message.make_query('example.', 'SOA')
+        expected = dns.message.make_response(query)
+        expected.answer.append(xfrServer._getSOAForSerial(2))
+
+        response = self.sendUDPQuery(query)
+        self.assertEquals(expected, response)
+
+    def test_b_UDP_SOA_not_loaded(self):
+        query = dns.message.make_query('example2.', 'SOA')
+        expected = dns.message.make_response(query)
+        expected.set_rcode(dns.rcode.REFUSED)
+
+        response = self.sendUDPQuery(query)
+        self.assertEquals(expected, response)
+
+    def test_b_UDP_SOA_not_configured(self):
+        query = dns.message.make_query('example3.', 'SOA')
+        expected = dns.message.make_response(query)
+        expected.set_rcode(dns.rcode.REFUSED)
+
+        response = self.sendUDPQuery(query)
+        self.assertEquals(expected, response)