From: Charles-Henri Bruyand Date: Tue, 30 Nov 2021 15:00:26 +0000 (+0100) Subject: dnsdist: add support to spoof a full self-generated response X-Git-Tag: auth-4.7.0-alpha1~116^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1907b33071e18dee044efdbc5a084bc7f7451e1d;p=thirdparty%2Fpdns.git dnsdist: add support to spoof a full self-generated response --- diff --git a/pdns/dnsdist-lua-actions.cc b/pdns/dnsdist-lua-actions.cc index 03475f79b9..efaca54fdd 100644 --- a/pdns/dnsdist-lua-actions.cc +++ b/pdns/dnsdist-lua-actions.cc @@ -776,10 +776,18 @@ DNSAction::Action SpoofAction::operator()(DNSQuestion* dq, std::string* ruleresu // do we even have a response? if (d_cname.empty() && d_rawResponses.empty() && + // make sure pre-forged response is greater than sizeof(dnsheader) + (d_raw.size() < sizeof(dnsheader)) && d_types.count(qtype) == 0) { return Action::None; } + if (d_raw.size() >= sizeof(dnsheader)) { + auto id = dq->getHeader()->id; + dq->getMutableData() = std::move(d_raw); + dq->getHeader()->id = id; + return Action::HeaderModify; + } vector addrs; vector rawResponses; unsigned int totrdatalen = 0; @@ -2180,6 +2188,14 @@ void setupLuaActions(LuaContext& luaCtx) return ret; }); + luaCtx.writeFunction("SpoofPacketAction", [](const std::string& response, size_t len) { + if (len < sizeof(dnsheader)) { + throw std::runtime_error(std::string("SpoofPacketAction: given packet len is too small")); + } + auto ret = std::shared_ptr(new SpoofAction(response.c_str(), len)); + return ret; + }); + luaCtx.writeFunction("DropAction", []() { return std::shared_ptr(new DropAction); }); diff --git a/pdns/dnsdist-lua.hh b/pdns/dnsdist-lua.hh index b8230ebad6..1231b4f187 100644 --- a/pdns/dnsdist-lua.hh +++ b/pdns/dnsdist-lua.hh @@ -55,6 +55,10 @@ public: { } + SpoofAction(const char* rawresponse, size_t len): d_raw(rawresponse, rawresponse + len) + { + } + SpoofAction(const vector& raws): d_rawResponses(raws) { } @@ -84,6 +88,7 @@ private: std::vector d_addrs; std::set d_types; std::vector d_rawResponses; + PacketBuffer d_raw; DNSName d_cname; }; diff --git a/pdns/dnsdistdist/dnsdist-lua-ffi-interface.h b/pdns/dnsdistdist/dnsdist-lua-ffi-interface.h index e371d9e1db..92f7cd10a1 100644 --- a/pdns/dnsdistdist/dnsdist-lua-ffi-interface.h +++ b/pdns/dnsdistdist/dnsdist-lua-ffi-interface.h @@ -107,6 +107,8 @@ void dnsdist_ffi_dnsquestion_send_trap(dnsdist_ffi_dnsquestion_t* dq, const char void dnsdist_ffi_dnsquestion_spoof_raw(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_ffi_raw_value_t* values, size_t valuesCount) __attribute__ ((visibility ("default"))); // the content of values should contain raw IPv4 or IPv6 addresses in network byte-order void dnsdist_ffi_dnsquestion_spoof_addrs(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_ffi_raw_value_t* values, size_t valuesCount) __attribute__ ((visibility ("default"))); +// spoof raw response. will just replace qid to match question +void dnsdist_ffi_dnsquestion_spoof_packet(dnsdist_ffi_dnsquestion_t* dq, const char* rawresponse, size_t len) __attribute__ ((visibility ("default"))); typedef struct dnsdist_ffi_servers_list_t dnsdist_ffi_servers_list_t; typedef struct dnsdist_ffi_server_t dnsdist_ffi_server_t; diff --git a/pdns/dnsdistdist/dnsdist-lua-ffi.cc b/pdns/dnsdistdist/dnsdist-lua-ffi.cc index 25e36c218f..28a742ac7b 100644 --- a/pdns/dnsdistdist/dnsdist-lua-ffi.cc +++ b/pdns/dnsdistdist/dnsdist-lua-ffi.cc @@ -434,6 +434,13 @@ void dnsdist_ffi_dnsquestion_send_trap(dnsdist_ffi_dnsquestion_t* dq, const char } } +void dnsdist_ffi_dnsquestion_spoof_packet(dnsdist_ffi_dnsquestion_t* dq, const char* raw, size_t len) +{ + std::string result; + SpoofAction sa(raw, len); + sa(dq->dq, &result); +} + void dnsdist_ffi_dnsquestion_spoof_raw(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_ffi_raw_value_t* values, size_t valuesCount) { std::vector data; diff --git a/pdns/dnsdistdist/docs/rules-actions.rst b/pdns/dnsdistdist/docs/rules-actions.rst index fe322f52ff..07374e3613 100644 --- a/pdns/dnsdistdist/docs/rules-actions.rst +++ b/pdns/dnsdistdist/docs/rules-actions.rst @@ -1554,6 +1554,15 @@ The following actions exist. * ``ra``: bool - Set the RA bit to this value (true means the bit is set, false means it's cleared). Default is to copy the value of the RD bit from the incoming query. * ``ttl``: int - The TTL of the record. +.. function:: SpoofPacketAction(rawPacket, len) + + .. versionadded:: 1.8.0 + + Spoof a raw self-generated answer + + :param string rawPacket: The raw wire-ready DNS answer + :param int len: The len of the packet + .. function:: TagAction(name, value) .. deprecated:: 1.6.0 diff --git a/regression-tests.dnsdist/test_Spoofing.py b/regression-tests.dnsdist/test_Spoofing.py index cc86e6bff3..5ae66249b7 100644 --- a/regression-tests.dnsdist/test_Spoofing.py +++ b/regression-tests.dnsdist/test_Spoofing.py @@ -913,3 +913,59 @@ class TestSpoofingLuaWithStatistics(DNSDistTest): (_, receivedResponse) = sender(query, response=None, useQueue=False) self.assertTrue(receivedResponse) self.assertEqual(expectedResponseAfterwards, receivedResponse) + +class TestSpoofingLuaSpoofPacket(DNSDistTest): + + _config_template = """ + local ffi = require("ffi") + + function spoofpacket(dq) + -- REFUSED answer + local rawResponse="\\000\\000\\129\\133\\000\\001\\000\\000\\000\\000\\000\\000\\014lua\\045raw\\045packet\\012ffi\\045spoofing\\005tests\\008powerdns\\003com\\000\\000\\001\\000\\001" + local qtype = ffi.C.dnsdist_ffi_dnsquestion_get_qtype(dq) + ffi.C.dnsdist_ffi_dnsquestion_spoof_packet(dq, rawResponse, string.len(rawResponse)) + return DNSAction.HeaderModify + end + + addAction("lua-raw-packet.ffi-spoofing.tests.powerdns.com.", LuaFFIAction(spoofpacket)) + local otherResponse="\\000\\000\\129\\133\\000\\001\\000\\000\\000\\000\\000\\000\\014lua\\045raw\\045packet\\008spoofing\\005tests\\008powerdns\\003com\\000\\000\\001\\000\\001" + addAction("lua-raw-packet.spoofing.tests.powerdns.com.", SpoofPacketAction(otherResponse, string.len(otherResponse))) + newServer{address="127.0.0.1:%s"} + """ + _verboseMode = True + + def testLuaFFISpoofPacket(self): + """ + Spoofing via Lua FFI: Spoof raw response via Lua FFI + """ + name = 'lua-raw-packet.ffi-spoofing.tests.powerdns.com.' + + # + query = dns.message.make_query(name, 'A', 'IN') + expectedResponse = dns.message.make_response(query) + expectedResponse.flags |= dns.flags.RA + expectedResponse.set_rcode(dns.rcode.REFUSED) + + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (_, receivedResponse) = sender(query, response=None, useQueue=False) + self.assertTrue(receivedResponse) + self.assertEqual(expectedResponse, receivedResponse) + + def testLuaSpoofPacket(self): + """ + Spoofing via Lua : Spoof raw response via Lua + """ + name = 'lua-raw-packet.spoofing.tests.powerdns.com.' + + # + query = dns.message.make_query(name, 'A', 'IN') + expectedResponse = dns.message.make_response(query) + expectedResponse.flags |= dns.flags.RA + expectedResponse.set_rcode(dns.rcode.REFUSED) + + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (_, receivedResponse) = sender(query, response=None, useQueue=False) + self.assertTrue(receivedResponse) + self.assertEqual(expectedResponse, receivedResponse)