Skip to content

Commit

Permalink
dns-rfc2136: use TCP to query SOA records (#7503)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
sorz authored and adferrand committed Nov 7, 2019
1 parent b84edfd commit f4f1660
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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

Expand Down
6 changes: 5 additions & 1 deletion certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py
Expand Up @@ -206,7 +206,11 @@ def _query_soa(self, domain_name):
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
Expand Down
20 changes: 17 additions & 3 deletions certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py
Expand Up @@ -162,7 +162,7 @@ def test_find_domain_wraps_errors(self):
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
Expand All @@ -173,7 +173,7 @@ def test_query_soa_found(self, query_mock):
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

Expand All @@ -183,7 +183,7 @@ def test_query_soa_not_found(self, query_mock):
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

Expand All @@ -193,6 +193,20 @@ def test_query_soa_wraps_errors(self, query_mock):
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

0 comments on commit f4f1660

Please sign in to comment.