]> granicus.if.org Git - pdns/commitdiff
dnsdist: Add TCPRule. Make addAnyTCRule set TC=1 over UDP, not TCP.
authorRemi Gacogne <remi.gacogne@powerdns.com>
Sun, 17 Jan 2016 15:15:18 +0000 (16:15 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Sun, 17 Jan 2016 15:15:18 +0000 (16:15 +0100)
pdns/README-dnsdist.md
pdns/dnsdist-lua.cc
pdns/dnsrulactions.hh
regression-tests.dnsdist/dnsdisttests.py
regression-tests.dnsdist/test_Basics.py

index 8a7316bdab4ea68559781bd83fd23ea0bdc10daa..40718e13c832befbf4b59ad4d1a8b5f0f8945653 100644 (file)
@@ -301,6 +301,7 @@ Rules have selectors and actions. Current selectors are:
  * QType (QTypeRule)
  * RegexRule on query name
  * Packet requests DNSSEC processing
+ * Query received over UDP or TCP
 
 A special rule is `AndRule{rule1, rule2}`, which only matches if all of its subrules match.
 
@@ -336,6 +337,7 @@ A DNS rule can be:
  * a QTypeRule
  * a RegexRule
  * a SuffixMatchNodeRule
+ * a TCPRule
 
 A convenience function `makeRule()` is supplied which will make a NetmaskGroupRule for you or a SuffixMatchNodeRule
 depending on how you call it. `makeRule("0.0.0.0/0")` will for example match all IPv4 traffic, `makeRule{"be","nl","lu"}` will
@@ -354,7 +356,8 @@ the exact definition of `blockFilter()` is at the end of this document.
 
 ANY or whatever to TC
 ---------------------
-The `blockFilter()` also gets passed read/writable copy of the DNS Header.
+The `blockFilter()` also gets passed read/writable copy of the DNS Header,
+via `dq.dh`.
 If you invoke setQR(1) on that, `dnsdist` knows you turned the packet into
 a response, and will send the answer directly to the original client.
 
@@ -815,6 +818,7 @@ instantiate a server with additional parameters
    * `QTypeRule(qtype)`: matches queries with the specified qtype
    * `RegexRule(regex)`: matches the query name against the supplied regex
    * `SuffixMatchNodeRule()`: matches based on a group of domain suffixes for rapid testing of membership
+   * `TCPRule(tcp)`: matches question received over TCP if `tcp` is true, over UDP otherwise
  * Rule management related:
    * `showRules()`: show all defined rules (Pool, Block, QPS, addAnyTCRule)
    * `rmRule(n)`: remove rule n
@@ -836,7 +840,7 @@ instantiate a server with additional parameters
    * `SpoofCNAMEAction()`: forge a response with the specified CNAME value
    * `TCAction()`: create answer to query with TC and RD bits set, to move to TCP/IP
  * Specialist rule generators
-   * `addAnyTCRule()`: generate TC=1 answers to ANY queries, moving them to TCP
+   * `addAnyTCRule()`: generate TC=1 answers to ANY queries received over UDP, moving them to TCP
    * `addDomainSpoof(domain, ip[, ip6])`: generate answers for A queries using the ip parameter (AAAA if ip is an IPv6). If ip6 is supplied, generate answers for AAAA queries too
    * `addDomainCNAMESpoof(domain, cname)`: generate CNAME answers for queries using the specified value
    * `addDisableValidationRule(domain)`: set the CD flags to 1 for all queries matching the specified domain
index 38f48aa5226426030bb8d03d00ce62c2e199e065..c4793dde314cc27dd9b8b2016000bc20b5f25b7a 100644 (file)
@@ -312,10 +312,14 @@ vector<std::function<void(void)>> setupLua(bool client, const std::string& confi
                      } );
 
   g_lua.writeFunction("makeRule", makeRule);
+
   g_lua.writeFunction("addAnyTCRule", []() {
       setLuaSideEffect();
       auto rules=g_rulactions.getCopy();
-      rules.push_back({ std::make_shared<QTypeRule>(0xff), std::make_shared<TCAction>()});
+      std::vector<pair<int, shared_ptr<DNSRule> >> v;
+      v.push_back({1, std::make_shared<QTypeRule>(0xff)});
+      v.push_back({2, std::make_shared<TCPRule>(false)});
+      rules.push_back({ std::shared_ptr<DNSRule>(new AndRule(v)), std::make_shared<TCAction>()});
       g_rulactions.setState(rules);
     });
 
@@ -699,6 +703,9 @@ vector<std::function<void(void)>> setupLua(bool client, const std::string& confi
       return std::shared_ptr<DNSRule>(new AndRule(a));
     });
 
+  g_lua.writeFunction("TCPRule", [](bool tcp) {
+      return std::shared_ptr<DNSRule>(new TCPRule(tcp));
+    });
 
   g_lua.writeFunction("addAction", [](luadnsrule_t var, std::shared_ptr<DNSAction> ea) 
                      {
index b242b709db1030a5c20d04371696ae4958aa3f36..4b6aa5f51520940cea816684a2bd8e5b1dd56263 100644 (file)
@@ -211,6 +211,24 @@ private:
   uint16_t d_qtype;
 };
 
+class TCPRule : public DNSRule
+{
+public:
+  TCPRule(bool tcp): d_tcp(tcp)
+  {
+  }
+  bool matches(const DNSQuestion* dq) const override
+  {
+    return dq->tcp == d_tcp;
+  }
+  string toString() const override
+  {
+    return (d_tcp ? "TCP" : "UDP");
+  }
+private:
+  bool d_tcp;
+};
+
 class DropAction : public DNSAction
 {
 public:
index c92cb3ea4592de69c895403b11cbd0eafc09fa45..e7833d2425cd25555bdde6403d2ab080d99c4dff 100644 (file)
@@ -157,6 +157,7 @@ class DNSDistTest(unittest.TestCase):
             if not answered:
                 # unexpected query, or health check
                 response = dns.message.make_response(request)
+                rrset = None
                 if request.question[0].rdclass == dns.rdataclass.IN:
                     if request.question[0].rdtype == dns.rdatatype.A:
                         rrset = dns.rrset.from_text(request.question[0].name,
@@ -211,6 +212,7 @@ class DNSDistTest(unittest.TestCase):
             if not answered:
                 # unexpected query, or health check
                 response = dns.message.make_response(request)
+                rrset = None
                 if request.question[0].rdclass == dns.rdataclass.IN:
                     if request.question[0].rdtype == dns.rdatatype.A:
                         rrset = dns.rrset.from_text(request.question[0].name,
index 286d7cad0c7c316d2918f3e5ce9de147bcea74fc..4414481bb1cf3c9e1cd317944ea371482e82ac15 100644 (file)
@@ -86,6 +86,7 @@ class TestBasics(DNSDistTest):
 
         dnsdist is configured to reply with TC to ANY queries,
         send an ANY query and check the result.
+        It should be truncated over UDP, not over TCP.
         """
         name = 'any.tests.powerdns.com.'
         query = dns.message.make_query(name, 'ANY', 'IN')
@@ -96,9 +97,22 @@ class TestBasics(DNSDistTest):
         receivedResponse.id = expectedResponse.id
         self.assertEquals(receivedResponse, expectedResponse)
 
-        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
-        receivedResponse.id = expectedResponse.id
-        self.assertEquals(receivedResponse, expectedResponse)
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text('any.tests.powerdns.com.',
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+
+        response.answer.append(rrset)
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(receivedResponse, response)
 
     def testTruncateTC(self):
         """