From: Remi Gacogne Date: Mon, 28 Jun 2021 08:59:43 +0000 (+0200) Subject: dnsdist: Add FFI functions to spoof multiple raw values X-Git-Tag: dnsdist-1.7.0-alpha1~119^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4b9274b62d940c5caf8c001158bcdbe3028238ab;p=thirdparty%2Fpdns.git dnsdist: Add FFI functions to spoof multiple raw values --- diff --git a/pdns/dnsdistdist/dnsdist-lua-ffi-interface.h b/pdns/dnsdistdist/dnsdist-lua-ffi-interface.h index 969cd2083e..94d6bfb5ec 100644 --- a/pdns/dnsdistdist/dnsdist-lua-ffi-interface.h +++ b/pdns/dnsdistdist/dnsdist-lua-ffi-interface.h @@ -43,6 +43,10 @@ typedef struct dnsdist_ffi_tag { const char* value; } dnsdist_ffi_tag_t; +typedef struct dnsdist_ffi_raw_value { + char* value; + uint16_t size; +} dnsdist_ffi_raw_value_t; void dnsdist_ffi_dnsquestion_get_localaddr(const dnsdist_ffi_dnsquestion_t* dq, const void** addr, size_t* addrSize) __attribute__ ((visibility ("default"))); uint16_t dnsdist_ffi_dnsquestion_get_local_port(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default"))); @@ -99,6 +103,11 @@ bool dnsdist_ffi_dnsquestion_set_trailing_data(dnsdist_ffi_dnsquestion_t* dq, co void dnsdist_ffi_dnsquestion_send_trap(dnsdist_ffi_dnsquestion_t* dq, const char* reason, size_t reasonLen) __attribute__ ((visibility ("default"))); +// the content of values should contain raw DNS record data ('\192\000\002\001' for A, '\034this text has a comma at the end,' for TXT, etc) +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"))); + 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 a90a952ebc..13fa0cd572 100644 --- a/pdns/dnsdistdist/dnsdist-lua-ffi.cc +++ b/pdns/dnsdistdist/dnsdist-lua-ffi.cc @@ -438,6 +438,49 @@ 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) +{ + std::vector data; + data.reserve(valuesCount); + + for (size_t idx = 0; idx < valuesCount; idx++) { + data.emplace_back(values[idx].value, values[idx].size); + } + + std::string result; + SpoofAction sa(data); + sa(dq->dq, &result); +} + +void dnsdist_ffi_dnsquestion_spoof_addrs(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_ffi_raw_value_t* values, size_t valuesCount) +{ + std::vector data; + data.reserve(valuesCount); + + for (size_t idx = 0; idx < valuesCount; idx++) { + if (values[idx].size == 4) { + sockaddr_in sin; + sin.sin_family = AF_INET; + sin.sin_port = 0; + memcpy(&sin.sin_addr.s_addr, values[idx].value, sizeof(sin.sin_addr.s_addr)); + data.emplace_back(&sin); + } + else if (values[idx].size == 16) { + sockaddr_in6 sin6; + sin6.sin6_family = AF_INET6; + sin6.sin6_port = 0; + sin6.sin6_scope_id = 0; + sin6.sin6_flowinfo = 0; + memcpy(&sin6.sin6_addr.s6_addr, values[idx].value, sizeof(sin6.sin6_addr.s6_addr)); + data.emplace_back(&sin6); + } + } + + std::string result; + SpoofAction sa(data); + sa(dq->dq, &result); +} + size_t dnsdist_ffi_servers_list_get_count(const dnsdist_ffi_servers_list_t* list) { return list->ffiServers.size(); diff --git a/regression-tests.dnsdist/test_Spoofing.py b/regression-tests.dnsdist/test_Spoofing.py index 7e379ba529..cc86e6bff3 100644 --- a/regression-tests.dnsdist/test_Spoofing.py +++ b/regression-tests.dnsdist/test_Spoofing.py @@ -760,6 +760,98 @@ class TestSpoofingLuaSpoofMulti(DNSDistTest): # sorry, we can't set the TTL from the Lua API right now #self.assertEqual(receivedResponse.answer[0].ttl, 3600) +class TestSpoofingLuaFFISpoofMulti(DNSDistTest): + + _config_template = """ + local ffi = require("ffi") + + function spoofrawmultirule(dq) + local qtype = ffi.C.dnsdist_ffi_dnsquestion_get_qtype(dq) + + if qtype == DNSQType.A then + local records = ffi.new("dnsdist_ffi_raw_value_t [2]") + + local str = "\\192\\000\\002\\001" + records[0].size = #str + records[0].value = ffi.new("char[?]", #str) + ffi.copy(records[0].value, str, #str) + + local str = "\\192\\000\\002\\255" + records[1].value = ffi.new("char[?]", #str) + ffi.copy(records[1].value, str, #str) + records[1].size = #str + + ffi.C.dnsdist_ffi_dnsquestion_spoof_raw(dq, records, 2) + return DNSAction.HeaderModify + elseif qtype == DNSQType.TXT then + local records = ffi.new("dnsdist_ffi_raw_value_t [2]") + + local str = "\\033this text has a comma at the end," + records[0].size = #str + records[0].value = ffi.new("char[?]", #str) + ffi.copy(records[0].value, str, #str) + + local str = "\\003aaa\\004bbbb" + records[1].size = #str + records[1].value = ffi.new("char[?]", #str) + ffi.copy(records[1].value, str, #str) + + ffi.C.dnsdist_ffi_dnsquestion_spoof_raw(dq, records, 2) + return DNSAction.HeaderModify + end + + return DNSAction.None, "" + end + + addAction("lua-raw-multi.ffi-spoofing.tests.powerdns.com.", LuaFFIAction(spoofrawmultirule)) + newServer{address="127.0.0.1:%s"} + """ + _verboseMode = True + + def testLuaSpoofMultiRawAction(self): + """ + Spoofing via Lua FFI: Spoof responses from raw bytes via Lua FFI + """ + name = 'lua-raw-multi.ffi-spoofing.tests.powerdns.com.' + + # A + 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, + 60, + dns.rdataclass.IN, + dns.rdatatype.A, + '192.0.2.1', '192.0.2.255') + 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, 60) + + # TXT + query = dns.message.make_query(name, 'TXT', 'IN') + query.flags &= ~dns.flags.RD + expectedResponse = dns.message.make_response(query) + expectedResponse.flags &= ~dns.flags.AA + rrset = dns.rrset.from_text(name, + 60, + dns.rdataclass.IN, + dns.rdatatype.TXT, + '"this text has a comma at the end,"', '"aaa" "bbbb"') + 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, 60) + class TestSpoofingLuaWithStatistics(DNSDistTest): _config_template = """