return dnsQuestion.sni;
});
+ luaCtx.registerFunction<std::string (DNSQuestion::*)() const>("getIncomingInterface", [](const DNSQuestion& dnsQuestion) -> std::string {
+ if (dnsQuestion.ids.cs != nullptr) {
+ return dnsQuestion.ids.cs->interface;
+ }
+ return {};
+ });
+
luaCtx.registerFunction<std::string (DNSQuestion::*)() const>("getProtocol", [](const DNSQuestion& dnsQuestion) {
return dnsQuestion.getProtocol().toPrettyString();
});
return dnsResponse.ids.queryRealTime.udiff();
});
+ luaCtx.registerFunction<std::string (DNSResponse::*)() const>("getIncomingInterface", [](const DNSResponse& dnsResponse) -> std::string {
+ if (dnsResponse.ids.cs != nullptr) {
+ return dnsResponse.ids.cs->interface;
+ }
+ return {};
+ });
+
luaCtx.registerFunction<void (DNSResponse::*)(std::string)>("sendTrap", []([[maybe_unused]] const DNSResponse& dnsResponse, [[maybe_unused]] boost::optional<std::string> reason) {
#ifdef HAVE_NET_SNMP
if (g_snmpAgent != nullptr && dnsdist::configuration::getImmutableConfiguration().d_snmpTrapsEnabled) {
:returns: The scheme of the DoH query, for example ``http`` or ``https``
+ .. method:: DNSQuestion:getIncomingInterface() -> string
+
+ .. versionadded:: 2.0.0
+
+ Return the name of the network interface this query was received on, but only if the corresponding frontend
+ has been bound to a specific network interface via the ``interface`` parameter to :func:`addLocal`, :func:`setLocal`,
+ :func:`addTLSLocal`, :func:`addDOHLocal`, :func:`addDOQLocal` or :func:`AddDOH3Local`, or the ``interface`` parameter
+ of a :ref:`frontend <yaml-settings-BindConfiguration>` when the YAML format is used. This is useful in Virtual Routing
+ and Forwarding (VRF) environments where the destination IP address might not be enough to identify the VRF.
+
+ :returns: The name of the network interface this query was received on, or an empty string.
+
.. method:: DNSQuestion:getProtocol() -> string
.. versionadded:: 1.7.0
BOOST_CHECK_EQUAL(ids.d_protoBufData->d_deviceID, deviceID);
BOOST_CHECK_EQUAL(ids.d_protoBufData->d_deviceName, deviceName);
BOOST_CHECK_EQUAL(ids.d_protoBufData->d_requestorID, requestorID);
+
+ /* no frontend yet */
+ BOOST_CHECK(dnsdist_ffi_dnsquestion_get_incoming_interface(nullptr) == nullptr);
+ BOOST_CHECK(dnsdist_ffi_dnsquestion_get_incoming_interface(&lightDQ) == nullptr);
+ {
+ /* frontend without and interface set */
+ const std::string interface{};
+ ClientState frontend(ids.origDest, false, false, 0, interface, {}, false);
+ ids.cs = &frontend;
+ const auto* itfPtr = dnsdist_ffi_dnsquestion_get_incoming_interface(&lightDQ);
+ BOOST_REQUIRE(itfPtr != nullptr);
+ BOOST_CHECK_EQUAL(std::string(itfPtr), interface);
+ ids.cs = nullptr;
+ }
+ {
+ /* frontend with interface set */
+ const std::string interface{"interface-name-0"};
+ ClientState frontend(ids.origDest, false, false, 0, interface, {}, false);
+ ids.cs = &frontend;
+ const auto* itfPtr = dnsdist_ffi_dnsquestion_get_incoming_interface(&lightDQ);
+ BOOST_REQUIRE(itfPtr != nullptr);
+ BOOST_CHECK_EQUAL(std::string(itfPtr), interface);
+ ids.cs = nullptr;
+ }
}
BOOST_AUTO_TEST_CASE(test_Response)
--- /dev/null
+#!/usr/bin/env python
+import socket
+import unittest
+import dns
+from dnsdisttests import DNSDistTest
+
+def get_loopback_itf():
+ interfaces = socket.if_nameindex()
+ for itf in interfaces:
+ if itf[1] == 'lo':
+ return 'lo'
+ return None
+
+class TestIncomingInterface(DNSDistTest):
+ _lo_itf = get_loopback_itf()
+ _config_template = """
+ local itfName = '%s'
+ addLocal('127.0.0.1:%d', {interface=itfName})
+
+ function checkItf(dq)
+ if dq:getIncomingInterface() ~= itfName then
+ return DNSAction.Spoof, '1.2.3.4'
+ end
+ return DNSAction.None
+ end
+
+ function checkItfResponse(dr)
+ if dr:getIncomingInterface() ~= itfName then
+ return DNSResponseAction.ServFail
+ end
+ return DNSResponseAction.None
+ end
+
+ addAction(AllRule(), LuaAction(checkItf))
+ addResponseAction(AllRule(), LuaResponseAction(checkItfResponse))
+ newServer{address="127.0.0.1:%d"}
+ """
+ _config_params = ['_lo_itf', '_dnsDistPort', '_testServerPort']
+ _skipListeningOnCL = True
+
+ def testItfName(self):
+ """
+ Advanced: Check incoming interface name
+ """
+ if get_loopback_itf() is None:
+ raise unittest.SkipTest('No lo interface')
+
+ name = 'incoming-interface.advanced.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '4.3.2.1')
+ response.answer.append(rrset)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response)
+ receivedQuery.id = query.id
+ self.assertEqual(receivedQuery, query)
+ self.assertEqual(receivedResponse, response)
+
+class TestIncomingInterfaceNotSet(DNSDistTest):
+ _lo_itf = get_loopback_itf()
+ _config_template = """
+ local itfName = '%s'
+ addLocal('127.0.0.1:%d')
+
+ function checkItf(dq)
+ if dq:getIncomingInterface() ~= itfName then
+ return DNSAction.Spoof, '1.2.3.4'
+ end
+ return DNSAction.None
+ end
+
+ function checkItfResponse(dr)
+ if dr:getIncomingInterface() ~= itfName then
+ return DNSResponseAction.ServFail
+ end
+ return DNSResponseAction.None
+ end
+
+ addAction(AllRule(), LuaAction(checkItf))
+ addResponseAction(AllRule(), LuaResponseAction(checkItfResponse))
+ newServer{address="127.0.0.1:%d"}
+ """
+ _config_params = ['_lo_itf', '_dnsDistPort', '_testServerPort']
+ _skipListeningOnCL = True
+
+ def testItfName(self):
+ """
+ Advanced: Check incoming interface name (not set)
+ """
+ if get_loopback_itf() is None:
+ raise unittest.SkipTest('No lo interface')
+
+ name = 'incoming-interface-not-set.advanced.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ # dnsdist set RA = RD for spoofed responses
+ query.flags &= ~dns.flags.RD
+
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '1.2.3.4')
+ expectedResponse.answer.append(rrset)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response=None, useQueue=False)
+ self.assertEqual(receivedQuery, None)
+ self.assertEqual(receivedResponse, expectedResponse)