From: Pieter Lexis Date: Tue, 7 Jul 2026 13:22:14 +0000 (+0200) Subject: feat(dnsdist): Allow setting TTL in DNSQuestion:spoof X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e87d9d8223e2bd987592bbad7003eebc289998a5;p=thirdparty%2Fpdns.git feat(dnsdist): Allow setting TTL in DNSQuestion:spoof Closes: #16470 --- diff --git a/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc b/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc index 599f7247e6..a03ec04f4c 100644 --- a/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc +++ b/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc @@ -314,8 +314,9 @@ void setupLuaBindingsDNSQuestion([[maybe_unused]] LuaContext& luaCtx) return true; }); - luaCtx.registerFunction, LuaArray>&, std::optional)>("spoof", [](DNSQuestion& dnsQuestion, const boost::variant, LuaArray>& response, std::optional typeForAny) { + luaCtx.registerFunction, LuaArray>&, std::optional, std::optional)>("spoof", [](DNSQuestion& dnsQuestion, const boost::variant, LuaArray>& response, std::optional typeForAny, std::optional ttl) { dnsdist::ResponseConfig responseConfig; + responseConfig.ttl = ttl.value_or(60); if (response.type() == typeid(LuaArray)) { std::vector data; auto responses = boost::get>(response); diff --git a/pdns/dnsdistdist/docs/reference/dq.rst b/pdns/dnsdistdist/docs/reference/dq.rst index a4f3038237..469b517fc9 100644 --- a/pdns/dnsdistdist/docs/reference/dq.rst +++ b/pdns/dnsdistdist/docs/reference/dq.rst @@ -373,11 +373,14 @@ This state can be modified from the various hooks. :param string tail: The new data :returns: true if the operation succeeded, false otherwise - .. method:: spoof(ip|ips|raw|raws [, typeForAny]) + .. method:: spoof(ip|ips|raw|raws [, typeForAny[, ttl]]) .. versionchanged:: 1.9.0 Optional parameter ``typeForAny`` added. + .. versionchanged:: 2.2.0 + Optional parameter ``ttl`` added. + Forge a response with the specified record data as raw bytes. If you specify list of raws (it is assumed they match the query type), all will get spoofed in. :param ComboAddress ip: The `ComboAddress` to be spoofed, e.g. `newCA("192.0.2.1")`. @@ -385,6 +388,7 @@ This state can be modified from the various hooks. :param string raw: The raw string to be spoofed, e.g. `"\\192\\000\\002\\001"`. :param table raws: The raw strings to be spoofed, e.g. `{ "\\192\\000\\002\\001", "\\192\\000\\002\\002" }`. :param int typeForAny: The type to use for raw responses when the requested type is ``ANY``, as using ``ANY`` for the type of the response record would not make sense. + :param int ttl: The TTL to set for the spoofed response, with a default value of 60. .. method:: suspend(asyncID, queryID, timeoutMS) -> bool diff --git a/regression-tests.dnsdist/test_Spoofing.py b/regression-tests.dnsdist/test_Spoofing.py index 5446534fe9..88220948ce 100644 --- a/regression-tests.dnsdist/test_Spoofing.py +++ b/regression-tests.dnsdist/test_Spoofing.py @@ -399,9 +399,38 @@ class SpoofingTests(object): self.checkMessageNoEDNS(expectedResponse, receivedResponse) self.assertEqual(receivedResponse.answer[0].ttl, 60) + def testSpoofLuaTtl(self): + """ + Spoofing: With a TTL + """ + name = "lua-ttl.spoofing.tests.powerdns.com." + query = dns.message.make_query(name, "A", "IN") + query.flags &= ~dns.flags.RD + expectedResponse = dns.message.make_response(query) + expectedResponse.flags &= ~dns.flags.AA + rrset = dns.rrset.from_text(name, 300, dns.rdataclass.IN, dns.rdatatype.A, "192.0.2.1") + expectedResponse.answer.append(rrset) + + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (_, receivedResponse) = sender(query, response=None, useQueue=False) + self.assertTrue(receivedResponse) + self.assertEqual(expectedResponse, receivedResponse) + self.assertEqual(receivedResponse.answer[0].ttl, 300) + class TestSpoofingViaLuaConfig(DNSDistTest, SpoofingTests): _config_template = """ + function spoofWithTTL(dq) + if(dq.qtype==DNSQType.A) + then + dq:spoof({ newCA("192.0.2.1") }, nil, 300) + return DNSAction.HeaderModify + else + return DNSAction.None + end + end + addAction(SuffixMatchNodeRule("spoofaction.spoofing.tests.powerdns.com."), SpoofAction({"192.0.2.1", "2001:DB8::1"})) addAction(SuffixMatchNodeRule("spoofaction-aa.spoofing.tests.powerdns.com."), SpoofAction({"192.0.2.1", "2001:DB8::1"}, {aa=true})) addAction(SuffixMatchNodeRule("spoofaction-ad.spoofing.tests.powerdns.com."), SpoofAction({"192.0.2.1", "2001:DB8::1"}, {ad=true})) @@ -418,6 +447,7 @@ class TestSpoofingViaLuaConfig(DNSDistTest, SpoofingTests): addAction(AndRule{SuffixMatchNodeRule("multiraw.spoofing.tests.powerdns.com"), QTypeRule(DNSQType.A)}, SpoofRawAction({"\\192\\000\\002\\001", "\\192\\000\\002\\002"})) -- rfc8482 addAction(AndRule{SuffixMatchNodeRule("raw-any.spoofing.tests.powerdns.com"), QTypeRule(DNSQType.ANY)}, SpoofRawAction("\\007rfc\\056\\052\\056\\050\\000", { typeForAny=DNSQType.HINFO })) + addAction(SuffixMatchNodeRule("lua-ttl.spoofing.tests.powerdns.com."), LuaAction(spoofWithTTL)) newServer{address="127.0.0.1:%d"} """ @@ -626,6 +656,22 @@ query_rules: - '\\007rfc\\056\\052\\056\\050\\000' vars: ttl: 60 + - selector: + type: "QNameSuffix" + suffixes: + - "lua-ttl.spoofing.tests.powerdns.com." + action: + type: "Lua" + function_code: | + return function(dq) + if(dq.qtype==DNSQType.A) + then + dq:spoof({ newCA("192.0.2.1") }, nil, 300) + return DNSAction.HeaderModify + else + return DNSAction.None + end + end """ _yaml_config_params = ["_testServerPort"] _config_params = []