From: Otto Moerbeek Date: Thu, 9 Oct 2025 10:19:18 +0000 (+0200) Subject: Add tests, list gnutls as dependency X-Git-Tag: rec-5.4.0-alpha1~190^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a33707fda378fce57acf0da8ef163c5468515cfb;p=thirdparty%2Fpdns.git Add tests, list gnutls as dependency Signed-off-by: Otto Moerbeek --- diff --git a/pdns/recursordist/lwres.cc b/pdns/recursordist/lwres.cc index d409b677b3..23640b4e2d 100644 --- a/pdns/recursordist/lwres.cc +++ b/pdns/recursordist/lwres.cc @@ -416,14 +416,27 @@ static bool tcpconnect(const OptLog& log, const ComboAddress& remote, const std: if (dnsOverTLS) { subjectName = nsName; std::string subjectAddress; - tlsCtx = TCPOutConnectionManager::getTLSContext(nsName, remote, connection.d_verboseLogging, subjectName, subjectAddress); + std::string configName; + tlsCtx = TCPOutConnectionManager::getTLSContext(nsName, remote, connection.d_verboseLogging, subjectName, subjectAddress, configName); if (tlsCtx == nullptr) { g_slogout->info(Logr::Error, "DoT requested but not available", "server", Logging::Loggable(remote)); dnsOverTLS = false; } - else if (subjectName.empty() && !subjectAddress.empty()) { - subjectName = subjectAddress; - subjectIsAddress = true; + else { + if (subjectName.empty() && !subjectAddress.empty()) { + subjectName = subjectAddress; + subjectIsAddress = true; + } + if (subjectName.empty()) { + subjectName = remote.toString(); + subjectIsAddress = true; + } + } + if (dnsOverTLS && connection.d_verboseLogging) { + g_slogout->info(Logr::Debug, "Connecting with DoT", "server", Logging::Loggable(remote), + "subjectName", Logging::Loggable(subjectName), + "subjectIsAddress", Logging::Loggable(subjectIsAddress), + "configName", Logging::Loggable(configName)); } } connection.d_handler = std::make_shared(subjectName, subjectIsAddress, sock.releaseHandle(), timeout, tlsCtx); diff --git a/pdns/recursordist/rec-tcpout.cc b/pdns/recursordist/rec-tcpout.cc index c090e1fab6..762178478d 100644 --- a/pdns/recursordist/rec-tcpout.cc +++ b/pdns/recursordist/rec-tcpout.cc @@ -86,7 +86,8 @@ TCPOutConnectionManager::Connection TCPOutConnectionManager::get(const endpoints return Connection{}; } -struct OutgoingTLSConfigTable { +struct OutgoingTLSConfigTable +{ SuffixMatchTree d_suffixToConfig; NetmaskTree d_netmaskToConfig; }; @@ -111,7 +112,7 @@ void TCPOutConnectionManager::setupOutgoingTLSConfigTables(pdns::rust::settings: } } -std::shared_ptr TCPOutConnectionManager::getTLSContext(const std::string& name, const ComboAddress& address, bool& verboseLogging, std::string& subjectName, std::string &subjectAddress) +std::shared_ptr TCPOutConnectionManager::getTLSContext(const std::string& name, const ComboAddress& address, bool& verboseLogging, std::string& subjectName, std::string& subjectAddress, std::string& configName) { TLSContextParameters tlsParams; tlsParams.d_provider = "openssl"; @@ -119,6 +120,7 @@ std::shared_ptr TCPOutConnectionManager::getTLSContext(const std::string const pdns::rust::settings::rec::OutgoingTLSConfiguration* config{nullptr}; { + configName = ""; auto table = s_outgoingTLSConfigTable.lock(); if (auto* node = table->d_netmaskToConfig.lookup(address); node != nullptr) { config = &node->second; @@ -127,6 +129,7 @@ std::shared_ptr TCPOutConnectionManager::getTLSContext(const std::string config = found; } if (config != nullptr) { + configName = std::string(config->name); tlsParams.d_provider = std::string(config->provider); tlsParams.d_validateCertificates = config->validate_certificate; tlsParams.d_caStore = std::string(config->ca_store); diff --git a/pdns/recursordist/rec-tcpout.hh b/pdns/recursordist/rec-tcpout.hh index 23fc5723f3..df88b406ce 100644 --- a/pdns/recursordist/rec-tcpout.hh +++ b/pdns/recursordist/rec-tcpout.hh @@ -75,7 +75,7 @@ public: } static void setupOutgoingTLSConfigTables(pdns::rust::settings::rec::Recursorsettings& settings); - static std::shared_ptr getTLSContext(const std::string& name, const ComboAddress& address, bool& verboseLogging, std::string& subjectName, std::string& subjectAddress); + static std::shared_ptr getTLSContext(const std::string& name, const ComboAddress& address, bool& verboseLogging, std::string& subjectName, std::string& subjectAddress, std::string& configName); private: // This does not take into account that we can have multiple connections with different hosts (via SNI) to the same IP. diff --git a/regression-tests.recursor-dnssec/test_DoT.py b/regression-tests.recursor-dnssec/test_DoT.py new file mode 100644 index 0000000000..e5f4257acb --- /dev/null +++ b/regression-tests.recursor-dnssec/test_DoT.py @@ -0,0 +1,238 @@ +import pytest +import dns +import os +import subprocess +from recursortests import RecursorTest + +class SimpleDoTTest(RecursorTest): + """ + This tests DoT to auth server in a very basic way and is dependent on powerdns.com nameservers having DoT enabled. + """ + + _confdir = 'SimpleDoT' + _config_template = """ +dnssec=validate +dot-to-auth-names=powerdns.com +devonly-regression-test-mode + """ + + _roothints = None + + @pytest.mark.external + def testTXT(self): + query = dns.message.make_query('.', 'DNSKEY', want_dnssec=True) + query.flags |= dns.flags.AD + + # As this test uses external servers, be more generous wrt timeouts than the default 2.0s + res = self.sendUDPQuery(query, timeout=5.0) + + self.assertMessageIsAuthenticated(res) + self.assertRcodeEqual(res, 0); + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get tcp-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + tcpcount = ret + + except subprocess.CalledProcessError as e: + print(e.output) + raise + + expected = dns.rrset.from_text('dot-test-target.powerdns.org.', 0, dns.rdataclass.IN, 'TXT', 'https://github.com/PowerDNS/pdns/pull/12825') + query = dns.message.make_query('dot-test-target.powerdns.org', 'TXT', want_dnssec=True) + query.flags |= dns.flags.AD + + # As this test uses external servers, be a more generous wrt timeouts than the default 2.0s + res = self.sendUDPQuery(query, timeout=5.0) + + self.assertMessageIsAuthenticated(res) + self.assertRRsetInAnswer(res, expected) + self.assertMatchingRRSIGInAnswer(res, expected) + + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get dot-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + self.assertNotEqual(ret, b'UNKNOWN\n') + self.assertNotEqual(ret, b'0\n') + + except subprocess.CalledProcessError as e: + print(e.output) + raise + + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get tcp-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + self.assertEqual(ret, tcpcount) + + except subprocess.CalledProcessError as e: + print(e.output) + raise + +class DoTTest(RecursorTest): + """ + This tests DoT to auth server with validation and is dependent on powerdns.com nameservers having DoT enabled. + """ + + _confdir = 'DoT' + _config_template = """ +dnssec: + validation: validate +outgoing: + dot_to_auth_names: [powerdns.com] + tls_configurations: + - name: dotwithverify + suffixes: [powerdns.com] + validate_certificate: true + verbose_logging: true +recursor: + devonly_regression_test_mode: true + """ + + _roothints = None + + @classmethod + def generateRecursorConfig(cls, confdir): + super(DoTTest, cls).generateRecursorYamlConfig(confdir, False) + + @pytest.mark.external + def testTXT(self): + query = dns.message.make_query('.', 'DNSKEY', want_dnssec=True) + query.flags |= dns.flags.AD + + # As this test uses external servers, be more generous wrt timeouts than the default 2.0s + res = self.sendUDPQuery(query, timeout=5.0) + + self.assertMessageIsAuthenticated(res) + self.assertRcodeEqual(res, 0); + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get tcp-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + tcpcount = ret + + except subprocess.CalledProcessError as e: + print(e.output) + raise + + expected = dns.rrset.from_text('dot-test-target.powerdns.org.', 0, dns.rdataclass.IN, 'TXT', 'https://github.com/PowerDNS/pdns/pull/12825') + query = dns.message.make_query('dot-test-target.powerdns.org', 'TXT', want_dnssec=True) + query.flags |= dns.flags.AD + + # As this test uses external servers, be a more generous wrt timeouts than the default 2.0s + res = self.sendUDPQuery(query, timeout=5.0) + + self.assertMessageIsAuthenticated(res) + self.assertRRsetInAnswer(res, expected) + self.assertMatchingRRSIGInAnswer(res, expected) + + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get dot-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + self.assertNotEqual(ret, b'UNKNOWN\n') + self.assertNotEqual(ret, b'0\n') + + except subprocess.CalledProcessError as e: + print(e.output) + raise + + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get tcp-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + self.assertEqual(ret, tcpcount) + + except subprocess.CalledProcessError as e: + print(e.output) + raise + +class DoTWithGNUTLSTest(RecursorTest): + """ + This tests DoT to auth server with validation and is dependent on powerdns.com nameservers having DoT enabled. + """ + + _confdir = 'DoTWithGNUTLS' + _config_template = """ +dnssec: + validation: validate +outgoing: + dot_to_auth_names: [powerdns.com] + tls_configurations: + - name: dotwithverifygnu + provider: gnutls + suffixes: [powerdns.com] + validate_certificate: true + verbose_logging: true +recursor: + devonly_regression_test_mode: true + """ + + _roothints = None + + @classmethod + def generateRecursorConfig(cls, confdir): + super(DoTWithGNUTLSTest, cls).generateRecursorYamlConfig(confdir, False) + + @pytest.mark.external + def testTXT(self): + query = dns.message.make_query('.', 'DNSKEY', want_dnssec=True) + query.flags |= dns.flags.AD + + # As this test uses external servers, be more generous wrt timeouts than the default 2.0s + res = self.sendUDPQuery(query, timeout=5.0) + + self.assertMessageIsAuthenticated(res) + self.assertRcodeEqual(res, 0); + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get tcp-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + tcpcount = ret + + except subprocess.CalledProcessError as e: + print(e.output) + raise + + expected = dns.rrset.from_text('dot-test-target.powerdns.org.', 0, dns.rdataclass.IN, 'TXT', 'https://github.com/PowerDNS/pdns/pull/12825') + query = dns.message.make_query('dot-test-target.powerdns.org', 'TXT', want_dnssec=True) + query.flags |= dns.flags.AD + + # As this test uses external servers, be a more generous wrt timeouts than the default 2.0s + res = self.sendUDPQuery(query, timeout=5.0) + + self.assertMessageIsAuthenticated(res) + self.assertRRsetInAnswer(res, expected) + self.assertMatchingRRSIGInAnswer(res, expected) + + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get dot-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + self.assertNotEqual(ret, b'UNKNOWN\n') + self.assertNotEqual(ret, b'0\n') + + except subprocess.CalledProcessError as e: + print(e.output) + raise + + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get tcp-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + self.assertEqual(ret, tcpcount) + + except subprocess.CalledProcessError as e: + print(e.output) + raise + diff --git a/regression-tests.recursor-dnssec/test_ForwardOverDoT.py b/regression-tests.recursor-dnssec/test_ForwardOverDoT.py new file mode 100644 index 0000000000..42fbd07c5b --- /dev/null +++ b/regression-tests.recursor-dnssec/test_ForwardOverDoT.py @@ -0,0 +1,119 @@ +import pytest +import dns +import os +import subprocess +from recursortests import RecursorTest + +class SimpleForwardOverDoTTest(RecursorTest): + """ + This is forwarding to DoT servers in a very basic way and is dependent on the forwards working for DoT + """ + + _confdir = 'SimpleForwardOverDoT' + _config_template = """ +dnssec=validate +forward-zones-recurse=.=1.1.1.1:853;8.8.8.8:853;9.9.9.9:853 +devonly-regression-test-mode + """ + + @pytest.mark.external + def testA(self): + expected = dns.rrset.from_text('dns.google.', 0, dns.rdataclass.IN, 'A', '8.8.8.8', '8.8.4.4') + query = dns.message.make_query('dns.google', 'A', want_dnssec=True) + query.flags |= dns.flags.AD + + res = self.sendUDPQuery(query) + + self.assertMessageIsAuthenticated(res) + self.assertRRsetInAnswer(res, expected) + self.assertMatchingRRSIGInAnswer(res, expected) + + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get dot-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + self.assertNotEqual(ret, b'UNKNOWN\n') + self.assertNotEqual(ret, b'0\n') + + except subprocess.CalledProcessError as e: + print(e.output) + raise + + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get tcp-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + self.assertEqual(ret, b'0\n') + + except subprocess.CalledProcessError as e: + print(e.output) + raise + +class ForwardOverDoTTest(RecursorTest): + """ + This is forwarding to DoT servers with validation and is dependent on the forwards working for DoT + """ + + _confdir = 'ForwardOverDoT' + _config_template = """ +dnssec: + validation: validate +outgoing: + tls_configurations: + - name: fwtopublic + subnets: [1.1.1.1,9.9.9.9] + validate_certificate: true + verbose_logging: true + - name: fwtogoogle + subnets: [8.8.8.8] + subject_name: dns.google + validate_certificate: true + verbose_logging: true +recursor: + forward_zones_recurse: + - zone: . + forwarders: [1.1.1.1:853,8.8.8.8:853,9.9.9.9:853] + devonly_regression_test_mode: true + """ + + @classmethod + def generateRecursorConfig(cls, confdir): + super(ForwardOverDoTTest, cls).generateRecursorYamlConfig(confdir, False) + + @pytest.mark.external + def testA(self): + expected = dns.rrset.from_text('dns.google.', 0, dns.rdataclass.IN, 'A', '8.8.8.8', '8.8.4.4') + query = dns.message.make_query('dns.google', 'A', want_dnssec=True) + query.flags |= dns.flags.AD + + res = self.sendUDPQuery(query) + + self.assertMessageIsAuthenticated(res) + self.assertRRsetInAnswer(res, expected) + self.assertMatchingRRSIGInAnswer(res, expected) + + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get dot-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + self.assertNotEqual(ret, b'UNKNOWN\n') + self.assertNotEqual(ret, b'0\n') + + except subprocess.CalledProcessError as e: + print(e.output) + raise + + rec_controlCmd = [os.environ['RECCONTROL'], + '--config-dir=%s' % 'configs/' + self._confdir, + 'get tcp-outqueries'] + try: + ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) + self.assertEqual(ret, b'0\n') + + except subprocess.CalledProcessError as e: + print(e.output) + raise + diff --git a/regression-tests.recursor-dnssec/test_SimpleDoT.py b/regression-tests.recursor-dnssec/test_SimpleDoT.py deleted file mode 100644 index af79b9bc36..0000000000 --- a/regression-tests.recursor-dnssec/test_SimpleDoT.py +++ /dev/null @@ -1,75 +0,0 @@ -import pytest -import dns -import os -import subprocess -from recursortests import RecursorTest - -class SimpleDoTTest(RecursorTest): - """ - This tests DoT to auth server in a very basic way and is dependent on powerdns.com nameservers having DoT enabled. - """ - - _confdir = 'SimpleDoT' - _config_template = """ -dnssec=validate -dot-to-auth-names=powerdns.com -devonly-regression-test-mode - """ - - _roothints = None - - @pytest.mark.external - def testTXT(self): - query = dns.message.make_query('.', 'DNSKEY', want_dnssec=True) - query.flags |= dns.flags.AD - - # As this test uses external servers, be more generous wrt timeouts than the default 2.0s - res = self.sendUDPQuery(query, timeout=5.0) - - self.assertMessageIsAuthenticated(res) - self.assertRcodeEqual(res, 0); - rec_controlCmd = [os.environ['RECCONTROL'], - '--config-dir=%s' % 'configs/' + self._confdir, - 'get tcp-outqueries'] - try: - ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) - tcpcount = ret - - except subprocess.CalledProcessError as e: - print(e.output) - raise - - expected = dns.rrset.from_text('dot-test-target.powerdns.org.', 0, dns.rdataclass.IN, 'TXT', 'https://github.com/PowerDNS/pdns/pull/12825') - query = dns.message.make_query('dot-test-target.powerdns.org', 'TXT', want_dnssec=True) - query.flags |= dns.flags.AD - - # As this test uses external servers, be a more generous wrt timeouts than the default 2.0s - res = self.sendUDPQuery(query, timeout=5.0) - - self.assertMessageIsAuthenticated(res) - self.assertRRsetInAnswer(res, expected) - self.assertMatchingRRSIGInAnswer(res, expected) - - rec_controlCmd = [os.environ['RECCONTROL'], - '--config-dir=%s' % 'configs/' + self._confdir, - 'get dot-outqueries'] - try: - ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) - self.assertNotEqual(ret, b'UNKNOWN\n') - self.assertNotEqual(ret, b'0\n') - - except subprocess.CalledProcessError as e: - print(e.output) - raise - - rec_controlCmd = [os.environ['RECCONTROL'], - '--config-dir=%s' % 'configs/' + self._confdir, - 'get tcp-outqueries'] - try: - ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) - self.assertEqual(ret, tcpcount) - - except subprocess.CalledProcessError as e: - print(e.output) - raise - diff --git a/regression-tests.recursor-dnssec/test_SimpleForwardOverDoT.py b/regression-tests.recursor-dnssec/test_SimpleForwardOverDoT.py deleted file mode 100644 index b0927cc6d7..0000000000 --- a/regression-tests.recursor-dnssec/test_SimpleForwardOverDoT.py +++ /dev/null @@ -1,53 +0,0 @@ -import pytest -import dns -import os -import subprocess -from recursortests import RecursorTest - -class SimpleForwardOverDoTTest(RecursorTest): - """ - This is forwarding to DoT servers in a very basic way and is dependent on the forwards working for DoT - """ - - _confdir = 'SimpleForwardOverDoT' - _config_template = """ -dnssec=validate -forward-zones-recurse=.=1.1.1.1:853;8.8.8.8:853;9.9.9.9:853 -devonly-regression-test-mode - """ - - @pytest.mark.external - def testA(self): - expected = dns.rrset.from_text('dns.google.', 0, dns.rdataclass.IN, 'A', '8.8.8.8', '8.8.4.4') - query = dns.message.make_query('dns.google', 'A', want_dnssec=True) - query.flags |= dns.flags.AD - - res = self.sendUDPQuery(query) - - self.assertMessageIsAuthenticated(res) - self.assertRRsetInAnswer(res, expected) - self.assertMatchingRRSIGInAnswer(res, expected) - - rec_controlCmd = [os.environ['RECCONTROL'], - '--config-dir=%s' % 'configs/' + self._confdir, - 'get dot-outqueries'] - try: - ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) - self.assertNotEqual(ret, b'UNKNOWN\n') - self.assertNotEqual(ret, b'0\n') - - except subprocess.CalledProcessError as e: - print(e.output) - raise - - rec_controlCmd = [os.environ['RECCONTROL'], - '--config-dir=%s' % 'configs/' + self._confdir, - 'get tcp-outqueries'] - try: - ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT) - self.assertEqual(ret, b'0\n') - - except subprocess.CalledProcessError as e: - print(e.output) - raise - diff --git a/tasks.py b/tasks.py index d0dbb6c343..8cbd6576b4 100644 --- a/tasks.py +++ b/tasks.py @@ -56,6 +56,7 @@ auth_build_deps = [ # FIXME: perhaps we should be stealing these from the deb rec_build_deps = [ 'libcap-dev', 'libfstrm-dev', + 'libgnutls28-dev', 'libsnmp-dev', ] rec_bulk_deps = [