diff --git a/certbot-dns-rfc2136/certbot_dns_rfc2136/_internal/dns_rfc2136.py b/certbot-dns-rfc2136/certbot_dns_rfc2136/_internal/dns_rfc2136.py index 3bb4f444b..57e9506f2 100644 --- a/certbot-dns-rfc2136/certbot_dns_rfc2136/_internal/dns_rfc2136.py +++ b/certbot-dns-rfc2136/certbot_dns_rfc2136/_internal/dns_rfc2136.py @@ -18,6 +18,7 @@ from certbot.plugins import dns_common logger = logging.getLogger(__name__) +DEFAULT_NETWORK_TIMEOUT = 45 @zope.interface.implementer(interfaces.IAuthenticator) @zope.interface.provider(interfaces.IPluginFactory) @@ -91,13 +92,15 @@ class _RFC2136Client(object): """ Encapsulates all communication with the target DNS server. """ - def __init__(self, server, port, key_name, key_secret, key_algorithm): + def __init__(self, server, port, key_name, key_secret, key_algorithm, + timeout=DEFAULT_NETWORK_TIMEOUT): self.server = server self.port = port self.keyring = dns.tsigkeyring.from_text({ key_name: key_secret }) self.algorithm = key_algorithm + self._default_timeout = timeout def add_txt_record(self, record_name, record_content, record_ttl): """ @@ -122,7 +125,7 @@ class _RFC2136Client(object): update.add(rel, record_ttl, dns.rdatatype.TXT, record_content) try: - response = dns.query.tcp(update, self.server, port=self.port) + response = dns.query.tcp(update, self.server, self._default_timeout, self.port) except Exception as e: raise errors.PluginError('Encountered error adding TXT record: {0}' .format(e)) @@ -157,7 +160,7 @@ class _RFC2136Client(object): update.delete(rel, dns.rdatatype.TXT, record_content) try: - response = dns.query.tcp(update, self.server, port=self.port) + response = dns.query.tcp(update, self.server, self._default_timeout, self.port) except Exception as e: raise errors.PluginError('Encountered error deleting TXT record: {0}' .format(e)) @@ -207,10 +210,10 @@ class _RFC2136Client(object): try: try: - response = dns.query.tcp(request, self.server, port=self.port) - except OSError as e: + response = dns.query.tcp(request, self.server, self._default_timeout, self.port) + except (OSError, dns.exception.Timeout) as e: logger.debug('TCP query failed, fallback to UDP: %s', e) - response = dns.query.udp(request, self.server, port=self.port) + response = dns.query.udp(request, self.server, self._default_timeout, self.port) rcode = response.rcode() # Authoritative Answer bit should be set diff --git a/certbot-dns-rfc2136/tests/dns_rfc2136_test.py b/certbot-dns-rfc2136/tests/dns_rfc2136_test.py index 4c14a8072..de5e2912b 100644 --- a/certbot-dns-rfc2136/tests/dns_rfc2136_test.py +++ b/certbot-dns-rfc2136/tests/dns_rfc2136_test.py @@ -21,7 +21,7 @@ PORT = 53 NAME = 'a-tsig-key.' SECRET = 'SSB3b25kZXIgd2hvIHdpbGwgYm90aGVyIHRvIGRlY29kZSB0aGlzIHRleHQK' VALID_CONFIG = {"rfc2136_server": SERVER, "rfc2136_name": NAME, "rfc2136_secret": SECRET} - +TIMEOUT = 45 class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthenticatorTest): @@ -78,7 +78,8 @@ class RFC2136ClientTest(unittest.TestCase): def setUp(self): from certbot_dns_rfc2136._internal.dns_rfc2136 import _RFC2136Client - self.rfc2136_client = _RFC2136Client(SERVER, PORT, NAME, SECRET, dns.tsig.HMAC_MD5) + self.rfc2136_client = _RFC2136Client(SERVER, PORT, NAME, SECRET, dns.tsig.HMAC_MD5, + TIMEOUT) @mock.patch("dns.query.tcp") def test_add_txt_record(self, query_mock): @@ -88,7 +89,7 @@ class RFC2136ClientTest(unittest.TestCase): self.rfc2136_client.add_txt_record("bar", "baz", 42) - query_mock.assert_called_with(mock.ANY, SERVER, port=PORT) + query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT) self.assertTrue("bar. 42 IN TXT \"baz\"" in str(query_mock.call_args[0][0])) @mock.patch("dns.query.tcp") @@ -121,7 +122,7 @@ class RFC2136ClientTest(unittest.TestCase): self.rfc2136_client.del_txt_record("bar", "baz") - query_mock.assert_called_with(mock.ANY, SERVER, port=PORT) + query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT) self.assertTrue("bar. 0 NONE TXT \"baz\"" in str(query_mock.call_args[0][0])) @mock.patch("dns.query.tcp") @@ -173,7 +174,7 @@ class RFC2136ClientTest(unittest.TestCase): # _query_soa | pylint: disable=protected-access result = self.rfc2136_client._query_soa(DOMAIN) - query_mock.assert_called_with(mock.ANY, SERVER, port=PORT) + query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT) self.assertTrue(result) @mock.patch("dns.query.tcp") @@ -183,7 +184,7 @@ class RFC2136ClientTest(unittest.TestCase): # _query_soa | pylint: disable=protected-access result = self.rfc2136_client._query_soa(DOMAIN) - query_mock.assert_called_with(mock.ANY, SERVER, port=PORT) + query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT) self.assertFalse(result) @mock.patch("dns.query.tcp") @@ -206,8 +207,8 @@ class RFC2136ClientTest(unittest.TestCase): # _query_soa | pylint: disable=protected-access result = self.rfc2136_client._query_soa(DOMAIN) - tcp_mock.assert_called_with(mock.ANY, SERVER, port=PORT) - udp_mock.assert_called_with(mock.ANY, SERVER, port=PORT) + tcp_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT) + udp_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT) self.assertTrue(result) diff --git a/certbot/CHANGELOG.md b/certbot/CHANGELOG.md index 71ebde382..8de570966 100644 --- a/certbot/CHANGELOG.md +++ b/certbot/CHANGELOG.md @@ -6,6 +6,7 @@ Certbot adheres to [Semantic Versioning](https://semver.org/). ### Added +* Added timeout to DNS query function calls for dns-rfc2136 plugin. * ### Changed