From f4f16605eda7274b1390e01eba66c2266085f415 Mon Sep 17 00:00:00 2001 From: Shell Chen Date: Fri, 8 Nov 2019 01:37:12 +0800 Subject: [PATCH] dns-rfc2136: use TCP to query SOA records (#7503) * Use tcp query on dns-rfc2136 plugin To improve network robust; fixes #7502. * Update CHANGELOG.md * Fix dns-rfc2136 test cases * Add UDP fallback to dns-rfc2136 --- CHANGELOG.md | 1 + .../certbot_dns_rfc2136/dns_rfc2136.py | 6 +++++- .../certbot_dns_rfc2136/dns_rfc2136_test.py | 20 ++++++++++++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d30694873..b317b4069 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ More details about these changes can be found on our GitHub repo. * acme.standalone.BaseRequestHandlerWithLogging and acme.standalone.simple_tls_sni_01_server have been deprecated and will be removed in a future release of the library. +* certbot-dns-rfc2136 now use TCP to query SOA records. ### Fixed diff --git a/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py b/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py index 2061374e0..ee71c9681 100644 --- a/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py +++ b/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py @@ -206,7 +206,11 @@ class _RFC2136Client(object): request.flags ^= dns.flags.RD try: - response = dns.query.udp(request, self.server, port=self.port) + try: + response = dns.query.tcp(request, self.server, port=self.port) + except OSError as e: + logger.debug('TCP query failed, fallback to UDP: %s', e) + response = dns.query.udp(request, self.server, port=self.port) rcode = response.rcode() # Authoritative Answer bit should be set diff --git a/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py b/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py index d800f1ec7..1950ee62e 100644 --- a/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py +++ b/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py @@ -162,7 +162,7 @@ class RFC2136ClientTest(unittest.TestCase): self.rfc2136_client._find_domain, 'foo.bar.'+DOMAIN) - @mock.patch("dns.query.udp") + @mock.patch("dns.query.tcp") def test_query_soa_found(self, query_mock): query_mock.return_value = mock.MagicMock(answer=[mock.MagicMock()], flags=dns.flags.AA) query_mock.return_value.rcode.return_value = dns.rcode.NOERROR @@ -173,7 +173,7 @@ class RFC2136ClientTest(unittest.TestCase): query_mock.assert_called_with(mock.ANY, SERVER, port=PORT) self.assertTrue(result) - @mock.patch("dns.query.udp") + @mock.patch("dns.query.tcp") def test_query_soa_not_found(self, query_mock): query_mock.return_value.rcode.return_value = dns.rcode.NXDOMAIN @@ -183,7 +183,7 @@ class RFC2136ClientTest(unittest.TestCase): query_mock.assert_called_with(mock.ANY, SERVER, port=PORT) self.assertFalse(result) - @mock.patch("dns.query.udp") + @mock.patch("dns.query.tcp") def test_query_soa_wraps_errors(self, query_mock): query_mock.side_effect = Exception @@ -193,6 +193,20 @@ class RFC2136ClientTest(unittest.TestCase): self.rfc2136_client._query_soa, DOMAIN) + @mock.patch("dns.query.udp") + @mock.patch("dns.query.tcp") + def test_query_soa_fallback_to_udp(self, tcp_mock, udp_mock): + tcp_mock.side_effect = OSError + udp_mock.return_value = mock.MagicMock(answer=[mock.MagicMock()], flags=dns.flags.AA) + udp_mock.return_value.rcode.return_value = dns.rcode.NOERROR + + # _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) + self.assertTrue(result) + if __name__ == "__main__": unittest.main() # pragma: no cover