* DNSAction.None: continue to the next rule
* DNSAction.Nxdomain: return a response with a NXDomain rcode
* DNSAction.Pool: use the specified pool to forward this query
- * DNSAction.Spoof: currently not implemented, see addDomainSpoof() and SpoofAction()
+ * DNSAction.Spoof: spoof the response using the supplied IPv4 (A), IPv6 (AAAA) or string (CNAME) value
DNSSEC
------
* `QPSPoolAction()`: set the packet into the specified pool only if it does not exceed the specified QPS limits
* `QPSAction()`: drop these packets if the QPS limits are exceeded
* `RCodeAction()`: reply immediatly by turning the query into a response with the specified rcode
- * `SpoofAction()`: forge a response with the specified IPv4 (for an A query). If you specify both an IPv4 and an IPv6, IPv4 will be used for A and IPv6 for an AAAA
+ * `SpoofAction()`: forge a response with the specified IPv4 (for an A query) or IPv6 (for an AAAA). If you specify two addresses, the first one should be an IPv4 and will be used for A, the second an IPv6 for an AAAA
* `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
- * `addDomainSpoof(domain, ip[, ip6])`: generate answers for A queries using the ip parameter. If ip6 is supplied, generate answers for AAAA queries too
+ * `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
* `addNoRecurseRule(domain)`: clear the RD flag for all queries matching the specified domain
return 0x100 * (*z) + *(z+1);
}
+void spoofResponseFromString(const ComboAddress& remote, const DNSName& qname, uint16_t qtype, dnsheader* dh, uint16_t& len, uint16_t querySize, const string& spoofContent)
+{
+ string result;
+ try {
+ ComboAddress spoofAddr(spoofContent);
+ SpoofAction sa(spoofAddr);
+ sa(remote, qname, qtype, dh, len, querySize, &result);
+ }
+ catch(PDNSException &e) {
+ SpoofAction sa(spoofContent);
+ sa(remote, qname, qtype, dh, len, querySize, &result);
+ }
+}
+
static ssize_t udpClientSendRequestToBackend(DownstreamState* ss, const int sd, const char* request, const size_t requestLen)
{
if (ss->sourceItf == 0) {
pool=ruleresult;
break;
case DNSAction::Action::Spoof:
- ;
+ spoofResponseFromString(remote, qname, qtype, dh, len, querySize, ruleresult);
+ /* fall-through */;
case DNSAction::Action::HeaderModify:
break;
case DNSAction::Action::Delay:
self.assertEquals(query, receivedQuery)
self.assertEquals(response, receivedResponse)
self.assertTrue((end - begin) < timedelta(0, 1));
+
+class TestAdvancedLuaSpoof(DNSDistTest):
+
+ _config_template = """
+ function spoof1rule(remote, qname, qtype, dh, len)
+ if(qtype==1) -- A
+ then
+ return DNSAction.Spoof, "192.0.2.1"
+ elseif(qtype == 28) -- AAAA
+ then
+ return DNSAction.Spoof, "2001:DB8::1"
+ else
+ return DNSAction.None, ""
+ end
+ end
+ function spoof2rule(remote, qname, qtype, dh, len)
+ return DNSAction.Spoof, "spoofedcname.tests.powerdns.com."
+ end
+ addLuaAction("luaspoof1.tests.powerdns.com.", spoof1rule)
+ addLuaAction("luaspoof2.tests.powerdns.com.", spoof2rule)
+ newServer{address="127.0.0.1:%s"}
+ """
+
+ def testLuaSpoofA(self):
+ """
+ Advanced: Spoofing an A via Lua
+
+ Send an A query to "luaspoof1.tests.powerdns.com.",
+ check that dnsdist sends a spoofed result.
+ """
+ name = 'luaspoof1.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ # dnsdist set RA = RD for spoofed responses
+ query.flags &= ~dns.flags.RD
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ expectedResponse.answer.append(rrset)
+
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ receivedResponse.id = expectedResponse.id
+ self.assertEquals(expectedResponse, receivedResponse)
+
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ receivedResponse.id = expectedResponse.id
+ self.assertEquals(expectedResponse, receivedResponse)
+
+ def testLuaSpoofAAAA(self):
+ """
+ Advanced: Spoofing an AAAA via Lua
+
+ Send an AAAA query to "luaspoof1.tests.powerdns.com.",
+ check that dnsdist sends a spoofed result.
+ """
+ name = 'luaspoof1.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'AAAA', 'IN')
+ # dnsdist set RA = RD for spoofed responses
+ query.flags &= ~dns.flags.RD
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.AAAA,
+ '2001:DB8::1')
+ expectedResponse.answer.append(rrset)
+
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ receivedResponse.id = expectedResponse.id
+ self.assertEquals(expectedResponse, receivedResponse)
+
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ receivedResponse.id = expectedResponse.id
+ self.assertEquals(expectedResponse, receivedResponse)
+
+ def testLuaSpoofAWithCNAME(self):
+ """
+ Advanced: Spoofing an A with a CNAME via Lua
+
+ Send an A query to "luaspoof2.tests.powerdns.com.",
+ check that dnsdist sends a spoofed result.
+ """
+ name = 'luaspoof2.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ # dnsdist set RA = RD for spoofed responses
+ query.flags &= ~dns.flags.RD
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.CNAME,
+ 'spoofedcname.tests.powerdns.com.')
+ expectedResponse.answer.append(rrset)
+
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ receivedResponse.id = expectedResponse.id
+ self.assertEquals(expectedResponse, receivedResponse)
+
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ receivedResponse.id = expectedResponse.id
+ self.assertEquals(expectedResponse, receivedResponse)
+
+ def testLuaSpoofAAAAWithCNAME(self):
+ """
+ Advanced: Spoofing an AAAA with a CNAME via Lua
+
+ Send an AAAA query to "luaspoof2.tests.powerdns.com.",
+ check that dnsdist sends a spoofed result.
+ """
+ name = 'luaspoof2.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'AAAA', 'IN')
+ # dnsdist set RA = RD for spoofed responses
+ query.flags &= ~dns.flags.RD
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.CNAME,
+ 'spoofedcname.tests.powerdns.com.')
+ expectedResponse.answer.append(rrset)
+
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ receivedResponse.id = expectedResponse.id
+ self.assertEquals(expectedResponse, receivedResponse)
+
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ receivedResponse.id = expectedResponse.id
+ self.assertEquals(expectedResponse, receivedResponse)