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")));
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;
}
}
+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();
# 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 = """