]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add FFI functions to spoof multiple raw values
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 28 Jun 2021 08:59:43 +0000 (10:59 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 28 Jun 2021 08:59:43 +0000 (10:59 +0200)
pdns/dnsdistdist/dnsdist-lua-ffi-interface.h
pdns/dnsdistdist/dnsdist-lua-ffi.cc
regression-tests.dnsdist/test_Spoofing.py

index 969cd2083e4ed3660aa4b8c2ab460e5584aaa6a6..94d6bfb5ec0b2a1788623daeaf2b8a3dd1a5db82 100644 (file)
@@ -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;
 
index a90a952ebc2b22ee870fb0c7af8e9f988f0a3ef5..13fa0cd572cd65e4142ecf48e5f0615450ed9c20 100644 (file)
@@ -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<std::string> 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<ComboAddress> 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();
index 7e379ba52970eef9018fbd158c668ba3581d1041..cc86e6bff3898bfa623354cf142fdaede27586fb 100644 (file)
@@ -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 = """