Skip to content

Commit

Permalink
dnsdist: Add TCPRule. Make addAnyTCRule set TC=1 over UDP, not TCP.
Browse files Browse the repository at this point in the history
  • Loading branch information
rgacogne committed Jan 17, 2016
1 parent 060dc73 commit e4f305f
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 6 deletions.
8 changes: 6 additions & 2 deletions pdns/README-dnsdist.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand All @@ -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.

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
9 changes: 8 additions & 1 deletion pdns/dnsdist-lua.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});

Expand Down Expand Up @@ -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)
{
Expand Down
18 changes: 18 additions & 0 deletions pdns/dnsrulactions.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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 "TCP";
}
private:
bool d_tcp;
};

class DropAction : public DNSAction
{
public:
Expand Down
2 changes: 2 additions & 0 deletions regression-tests.dnsdist/dnsdisttests.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ def UDPResponder(cls, port):
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,
Expand Down Expand Up @@ -211,6 +212,7 @@ def TCPResponder(cls, port):
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,
Expand Down
20 changes: 17 additions & 3 deletions regression-tests.dnsdist/test_Basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def testAnyIsTruncated(self):
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')
Expand All @@ -96,9 +97,22 @@ def testAnyIsTruncated(self):
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):
"""
Expand Down

0 comments on commit e4f305f

Please sign in to comment.