From 98194c31cc0380ce94cf436008b82904ab6143bd Mon Sep 17 00:00:00 2001 From: Otto Date: Mon, 14 Jun 2021 12:51:53 +0200 Subject: [PATCH] Basic test for dot-to-auth-names and don't setup auths for DoT tests, we don't need them. --- pdns/pdns_recursor.cc | 31 +++++---- pdns/recursordist/docs/settings.rst | 3 +- pdns/syncres.hh | 3 +- .../test_SimpleDoT.py | 65 +++++++++++++++++++ .../test_SimpleForwardOverDoT.py | 12 ++++ 5 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 regression-tests.recursor-dnssec/test_SimpleDoT.py diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 1aeaf0b223..0e782a1a63 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -417,13 +417,20 @@ LWResult::Result asendtcp(const PacketBuffer& data, shared_ptr& ha pident->highState = TCPAction::DoingWrite; - TCPLOG("Initial tryWrite: " << pident->outPos << '/' << pident->outMSG.size() << ' ' << " -> "); - IOState state = handler->tryWrite(pident->outMSG, pident->outPos, pident->outMSG.size()); - TCPLOG(pident->outPos << '/' << pident->outMSG.size() << endl); + IOState state; + try { + TCPLOG("Initial tryWrite: " << pident->outPos << '/' << pident->outMSG.size() << ' ' << " -> "); + state = handler->tryWrite(pident->outMSG, pident->outPos, pident->outMSG.size()); + TCPLOG(pident->outPos << '/' << pident->outMSG.size() << endl); - if (state == IOState::Done) { - TCPLOG("asendtcp success A" << endl); - return LWResult::Result::Success; + if (state == IOState::Done) { + TCPLOG("asendtcp success A" << endl); + return LWResult::Result::Success; + } + } + catch (const std::exception& e) { + TCPLOG("tryWrite() exception..." << e.what() << endl); + return LWResult::Result::PermanentError; } // Will set pident->lowState @@ -433,16 +440,17 @@ LWResult::Result asendtcp(const PacketBuffer& data, shared_ptr& ha int ret = MT->waitEvent(*pident, &packet, g_networkTimeoutMsec); TCPLOG("asendtcp waitEvent returned " << ret << ' ' << packet.size() << '/' << data.size() << ' '); if (ret == 0) { - t_fdm->removeWriteFD(handler->getDescriptor()); TCPLOG("timeout" << endl); + TCPIOHandlerStateChange(pident->lowState, IOState::Done, pident); return LWResult::Result::Timeout; } else if (ret == -1) { // error - t_fdm->removeWriteFD(handler->getDescriptor()); TCPLOG("PermanentError" << endl); + TCPIOHandlerStateChange(pident->lowState, IOState::Done, pident); return LWResult::Result::PermanentError; } else if (packet.size() != data.size()) { // main loop tells us what it sent out, or empty in case of an error + // fd housekeeping done by TCPIOHandlerIO TCPLOG("PermanentError size mismatch" << endl); return LWResult::Result::PermanentError; } @@ -500,15 +508,16 @@ LWResult::Result arecvtcp(PacketBuffer& data, const size_t len, shared_ptrremoveReadFD(handler->getDescriptor()); + TCPIOHandlerStateChange(pident->lowState, IOState::Done, pident); return LWResult::Result::Timeout; } else if (ret == -1) { TCPLOG("PermanentError" << endl); - t_fdm->removeWriteFD(handler->getDescriptor()); + TCPIOHandlerStateChange(pident->lowState, IOState::Done, pident); return LWResult::Result::PermanentError; } else if (data.empty()) {// error, EOF or other + // fd housekeeping done by TCPIOHandlerIO TCPLOG("EOF" << endl); return LWResult::Result::PermanentError; } @@ -4164,7 +4173,7 @@ static void TCPIOHandlerIO(int fd, FDMultiplexer::funcparam_t& var) // In arecvtcp, the buffer was resized already so inWanted bytes will fit // try reading try { - newstate = pid->tcphandler->tryRead(pid->inMSG, pid->inPos, pid->inWanted - pid->inPos); + newstate = pid->tcphandler->tryRead(pid->inMSG, pid->inPos, pid->inWanted); switch (newstate) { case IOState::Done: case IOState::NeedRead: diff --git a/pdns/recursordist/docs/settings.rst b/pdns/recursordist/docs/settings.rst index c32b659247..dd4a245b54 100644 --- a/pdns/recursordist/docs/settings.rst +++ b/pdns/recursordist/docs/settings.rst @@ -396,8 +396,7 @@ ratio. - Default: (empty). Force DoT to the listed authoritative nameservers. For this to work, DoT support has to be compiled in. -Currently, the certificate is checked for validity, but the name in the certificate is not checked against -the name of the host. +Currently, the certificate is not checked for validity in any way. .. _settings-dot-to-port-853: diff --git a/pdns/syncres.hh b/pdns/syncres.hh index 1bcdf712e9..254645502b 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -946,7 +946,7 @@ struct PacketID typedef set chain_t; mutable chain_t chain; shared_ptr tcphandler{nullptr}; - string::size_type inPos{0}; // how for are we along in the inMSG + string::size_type inPos{0}; // how far are we along in the inMSG size_t inWanted{0}; // if this is set, we'll read until inWanted bytes are read string::size_type outPos{0}; // how far we are along in the outMSG mutable uint32_t nearMisses{0}; // number of near misses - host correct, id wrong @@ -1161,3 +1161,4 @@ struct ThreadTimes return *this; } }; + diff --git a/regression-tests.recursor-dnssec/test_SimpleDoT.py b/regression-tests.recursor-dnssec/test_SimpleDoT.py new file mode 100644 index 0000000000..0994a47ec9 --- /dev/null +++ b/regression-tests.recursor-dnssec/test_SimpleDoT.py @@ -0,0 +1,65 @@ +import dns +import os +import subprocess +from recursortests import RecursorTest + +class testSimpleDoT(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 + """ + + _roothints = None + + @classmethod + def setUpClass(cls): + + # we don't need all the auth stuff + cls.setUpSockets() + cls.startResponders() + + confdir = os.path.join('configs', cls._confdir) + cls.createConfigDir(confdir) + + cls.generateRecursorConfig(confdir) + cls.startRecursor(confdir, cls._recursorPort) + + def testA(self): + expected = dns.rrset.from_text('www.powerdns.com.', 0, dns.rdataclass.IN, 'A', '188.166.104.92') + query = dns.message.make_query('www.powerdns.com', '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_SimpleForwardOverDoT.py b/regression-tests.recursor-dnssec/test_SimpleForwardOverDoT.py index ae44e0f086..95e2fbca04 100644 --- a/regression-tests.recursor-dnssec/test_SimpleForwardOverDoT.py +++ b/regression-tests.recursor-dnssec/test_SimpleForwardOverDoT.py @@ -14,6 +14,18 @@ dnssec=validate forward-zones-recurse=.=9.9.9.9:853 """ + @classmethod + def setUpClass(cls): + + # we don't need all the auth stuff + cls.setUpSockets() + + confdir = os.path.join('configs', cls._confdir) + cls.createConfigDir(confdir) + + cls.generateRecursorConfig(confdir) + cls.startRecursor(confdir, cls._recursorPort) + 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) -- 2.47.2