From: Charles-Henri Bruyand Date: Fri, 3 Dec 2021 11:13:38 +0000 (+0100) Subject: dnsdist: add DNSAction::Action::SpoofPacket so that spoofing can be done within a... X-Git-Tag: auth-4.7.0-alpha1~116^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F11051%2Fhead;p=thirdparty%2Fpdns.git dnsdist: add DNSAction::Action::SpoofPacket so that spoofing can be done within a regular LuaAction - fix small (and bigger) nits reported by rgacogne, thanks. - update regression tests with better boundaries on queries beeing spoofed --- diff --git a/pdns/dnsdist-lua-actions.cc b/pdns/dnsdist-lua-actions.cc index efaca54fdd..fe2f2efebb 100644 --- a/pdns/dnsdist-lua-actions.cc +++ b/pdns/dnsdist-lua-actions.cc @@ -784,7 +784,7 @@ DNSAction::Action SpoofAction::operator()(DNSQuestion* dq, std::string* ruleresu if (d_raw.size() >= sizeof(dnsheader)) { auto id = dq->getHeader()->id; - dq->getMutableData() = std::move(d_raw); + dq->getMutableData() = d_raw; dq->getHeader()->id = id; return Action::HeaderModify; } diff --git a/pdns/dnsdist-lua-vars.cc b/pdns/dnsdist-lua-vars.cc index 82c4a8e482..ed5cf1e5d7 100644 --- a/pdns/dnsdist-lua-vars.cc +++ b/pdns/dnsdist-lua-vars.cc @@ -32,6 +32,7 @@ void setupLuaVars(LuaContext& luaCtx) {"Nxdomain", (int)DNSAction::Action::Nxdomain}, {"Refused", (int)DNSAction::Action::Refused}, {"Spoof", (int)DNSAction::Action::Spoof}, + {"SpoofPacket", (int)DNSAction::Action::SpoofPacket}, {"SpoofRaw", (int)DNSAction::Action::SpoofRaw}, {"Allow", (int)DNSAction::Action::Allow}, {"HeaderModify", (int)DNSAction::Action::HeaderModify}, diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 4a18e7e7ad..33f7835170 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -768,6 +768,14 @@ static void spoofResponseFromString(DNSQuestion& dq, const string& spoofContent, } } +static void spoofPacketFromString(DNSQuestion& dq, const string& spoofContent) +{ + string result; + + SpoofAction sa(spoofContent.c_str(), spoofContent.size()); + sa(&dq, &result); +} + bool processRulesResult(const DNSAction::Action& action, DNSQuestion& dq, std::string& ruleresult, bool& drop) { switch(action) { @@ -801,6 +809,10 @@ bool processRulesResult(const DNSAction::Action& action, DNSQuestion& dq, std::s spoofResponseFromString(dq, ruleresult, false); return true; break; + case DNSAction::Action::SpoofPacket: + spoofPacketFromString(dq, ruleresult); + return true; + break; case DNSAction::Action::SpoofRaw: spoofResponseFromString(dq, ruleresult, true); return true; diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 6ee0b33828..865f6dec26 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -204,7 +204,7 @@ struct DNSResponse : DNSQuestion class DNSAction { public: - enum class Action : uint8_t { Drop, Nxdomain, Refused, Spoof, Allow, HeaderModify, Pool, Delay, Truncate, ServFail, None, NoOp, NoRecurse, SpoofRaw }; + enum class Action : uint8_t { Drop, Nxdomain, Refused, Spoof, Allow, HeaderModify, Pool, Delay, Truncate, ServFail, None, NoOp, NoRecurse, SpoofRaw, SpoofPacket }; static std::string typeToString(const Action& action) { switch(action) { @@ -216,6 +216,8 @@ public: return "Send Refused"; case Action::Spoof: return "Spoof an answer"; + case Action::SpoofPacket: + return "Spoof a raw answer from bytes"; case Action::SpoofRaw: return "Spoof an answer from raw bytes"; case Action::Allow: diff --git a/pdns/dnsdistdist/docs/reference/constants.rst b/pdns/dnsdistdist/docs/reference/constants.rst index d81c2d44f7..c391741b26 100755 --- a/pdns/dnsdistdist/docs/reference/constants.rst +++ b/pdns/dnsdistdist/docs/reference/constants.rst @@ -108,6 +108,9 @@ DNSAction .. versionchanged:: 1.5.0 ``DNSAction.SpoofRaw`` has been added. +.. versionchanged:: 1.8.0 + ``DNSAction.SpoofPacket`` has been added. + These constants represent an Action that can be returned from :func:`LuaAction` functions. * ``DNSAction.Allow``: let the query pass, skipping other rules @@ -121,6 +124,7 @@ These constants represent an Action that can be returned from :func:`LuaAction` * ``DNSAction.Refused``: return a response with a Refused rcode * ``DNSAction.ServFail``: return a response with a ServFail rcode * ``DNSAction.Spoof``: spoof the response using the supplied IPv4 (A), IPv6 (AAAA) or string (CNAME) value. TTL will be 60 seconds. + * ``DNSAction.SpoofPacket``: spoof the response using the supplied raw packet * ``DNSAction.SpoofRaw``: spoof the response using the supplied raw value as record data (see also :meth:`DNSQuestion:spoof` and :func:`dnsdist_ffi_dnsquestion_spoof_raw` to spoof multiple values) * ``DNSAction.Truncate``: truncate the response * ``DNSAction.NoRecurse``: set rd=0 on the query diff --git a/pdns/dnsdistdist/docs/rules-actions.rst b/pdns/dnsdistdist/docs/rules-actions.rst index 07374e3613..16dcdd188b 100644 --- a/pdns/dnsdistdist/docs/rules-actions.rst +++ b/pdns/dnsdistdist/docs/rules-actions.rst @@ -1561,7 +1561,7 @@ The following actions exist. Spoof a raw self-generated answer :param string rawPacket: The raw wire-ready DNS answer - :param int len: The len of the packet + :param int len: The length of the packet .. function:: TagAction(name, value) diff --git a/regression-tests.dnsdist/test_Spoofing.py b/regression-tests.dnsdist/test_Spoofing.py index 5ae66249b7..ab46b536fd 100644 --- a/regression-tests.dnsdist/test_Spoofing.py +++ b/regression-tests.dnsdist/test_Spoofing.py @@ -917,46 +917,59 @@ class TestSpoofingLuaWithStatistics(DNSDistTest): 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" + if dq.qtype == DNSQType.A then + return DNSAction.SpoofPacket, "\\000\\000\\129\\133\\000\\001\\000\\000\\000\\000\\000\\000\\014lua\\045raw\\045packet\\008spoofing\\005tests\\008powerdns\\003com\\000\\000\\001\\000\\001" + end + return DNSAction.None, "" + end + + addAction("lua-raw-packet.spoofing.tests.powerdns.com.", LuaAction(spoofpacket)) + local rawResponse="\\000\\000\\129\\133\\000\\001\\000\\000\\000\\000\\000\\000\\019rule\\045lua\\045raw\\045packet\\008spoofing\\005tests\\008powerdns\\003com\\000\\000\\001\\000\\001" + addAction(AndRule{QTypeRule(DNSQType.A), makeRule("rule-lua-raw-packet.spoofing.tests.powerdns.com.")}, SpoofPacketAction(rawResponse, string.len(rawResponse))) + + local ffi = require("ffi") + + function spoofpacketffi(dq) 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 + if qtype == DNSQType.A then + -- REFUSED answer + local refusedResponse="\\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" + + ffi.C.dnsdist_ffi_dnsquestion_spoof_packet(dq, refusedResponse, string.len(refusedResponse)) + return DNSAction.HeaderModify + end + return DNSAction.None, "" 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))) + addAction("lua-raw-packet.ffi-spoofing.tests.powerdns.com.", LuaFFIAction(spoofpacketffi)) newServer{address="127.0.0.1:%s"} """ _verboseMode = True - def testLuaFFISpoofPacket(self): + def testLuaSpoofPacket(self): """ Spoofing via Lua FFI: Spoof raw response via Lua FFI """ - name = 'lua-raw-packet.ffi-spoofing.tests.powerdns.com.' + for name in ('lua-raw-packet.spoofing.tests.powerdns.com.', 'rule-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) + 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) + 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): + def testLuaFFISpoofPacket(self): """ - Spoofing via Lua : Spoof raw response via Lua + Spoofing via Lua FFI: Spoof raw response via Lua FFI """ - name = 'lua-raw-packet.spoofing.tests.powerdns.com.' + name = 'lua-raw-packet.ffi-spoofing.tests.powerdns.com.' # query = dns.message.make_query(name, 'A', 'IN')