Source: dnspython
Version: 2.2.1-1
Severity: normal
Tags: patch
X-Debbugs-Cc: daniel.bung...@canonical.com

Dear Maintainer,

During testing of dnspython on Ubuntu, we have found that the servers
required for some of the tests may not be available, which could cause
an autopkgtest failure.

The Ubuntu bug for this can be found at:
https://bugs.launchpad.net/ubuntu/+source/dnspython/+bug/1976565

I have prepared a patch, based on not-yet merged work by dnspython
upstream.

-Dan
diff -Nru dnspython-2.2.1/debian/changelog dnspython-2.2.1/debian/changelog
--- dnspython-2.2.1/debian/changelog    2022-05-16 12:59:36.000000000 -0600
+++ dnspython-2.2.1/debian/changelog    2022-06-07 13:32:40.000000000 -0600
@@ -1,3 +1,10 @@
+dnspython (2.2.1-2) unstable; urgency=medium
+
+  [Bob Halley]
+  * Update test suite to improve network checking (Closes: #FIXME)
+
+ -- Dan Bungert <daniel.bung...@canonical.com>  Tue, 07 Jun 2022 13:32:40 -0600
+
 dnspython (2.2.1-1) unstable; urgency=medium
 
   [ Benjamin Drung ]
diff -Nru 
dnspython-2.2.1/debian/patches/0003-Improve-network-checking-812.patch 
dnspython-2.2.1/debian/patches/0003-Improve-network-checking-812.patch
--- dnspython-2.2.1/debian/patches/0003-Improve-network-checking-812.patch      
1969-12-31 17:00:00.000000000 -0700
+++ dnspython-2.2.1/debian/patches/0003-Improve-network-checking-812.patch      
2022-06-07 13:32:40.000000000 -0600
@@ -0,0 +1,193 @@
+Description: Improve network checking [#812].
+Author: Bob Halley <hal...@dnspython.org>
+Forwarded: not-needed
+Last-Update: 2022-06-04
+---
+ tests/test_doh.py      | 19 +++-----------
+ tests/test_query.py    | 16 ++++--------
+ tests/test_resolver.py |  1 +
+ tests/util.py          | 70 ++++++++++++++++++++++++++++++++++++++++++++++----
+ 4 files changed, 75 insertions(+), 31 deletions(-)
+
+diff --git a/tests/test_doh.py b/tests/test_doh.py
+index fb41723..38e2ada 100644
+--- a/tests/test_doh.py
++++ b/tests/test_doh.py
+@@ -37,31 +37,20 @@ if dns.query._have_httpx:
+ 
+ import tests.util
+ 
+-# Probe for IPv4 and IPv6
+ resolver_v4_addresses = []
+ resolver_v6_addresses = []
+-try:
+-    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
+-        s.settimeout(4)
+-        s.connect(('8.8.8.8', 53))
++if tests.util.have_ipv4():
+     resolver_v4_addresses = [
+         '1.1.1.1',
+         '8.8.8.8',
+         # '9.9.9.9',
+     ]
+-except Exception:
+-    pass
+-try:
+-    with socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) as s:
+-        s.connect(('2001:4860:4860::8888', 53))
++if tests.util.have_ipv6():
+     resolver_v6_addresses = [
+-        '2606:4700:4700::1111',
+-        # Google says 404
+-        # '2001:4860:4860::8888',
++        "2606:4700:4700::1111",
++        "2001:4860:4860::8888",
+         # '2620:fe::fe',
+     ]
+-except Exception:
+-    pass
+ 
+ KNOWN_ANYCAST_DOH_RESOLVER_URLS = ['https://cloudflare-dns.com/dns-query',
+                                    'https://dns.google/dns-query',
+diff --git a/tests/test_query.py b/tests/test_query.py
+index 6ffa694..1089fd0 100644
+--- a/tests/test_query.py
++++ b/tests/test_query.py
+@@ -48,18 +48,12 @@ except ImportError:
+     class Server(object):
+         pass
+ 
+-# Probe for IPv4 and IPv6
++
+ query_addresses = []
+-for (af, address) in ((socket.AF_INET, '8.8.8.8'),
+-                      (socket.AF_INET6, '2001:4860:4860::8888')):
+-    try:
+-        with socket.socket(af, socket.SOCK_DGRAM) as s:
+-            # Connecting a UDP socket is supposed to return ENETUNREACH if
+-            # no route to the network is present.
+-            s.connect((address, 53))
+-        query_addresses.append(address)
+-    except Exception:
+-        pass
++if tests.util.have_ipv4():
++    query_addresses.append("8.8.8.8")
++if tests.util.have_ipv6():
++    query_addresses.append("2001:4860:4860::8888")
+ 
+ keyring = dns.tsigkeyring.from_text({'name': 'tDz6cfXXGtNivRpQ98hr6A=='})
+ 
+diff --git a/tests/test_resolver.py b/tests/test_resolver.py
+index 36f1b7f..3f289c0 100644
+--- a/tests/test_resolver.py
++++ b/tests/test_resolver.py
+@@ -667,6 +667,7 @@ class LiveResolverTests(unittest.TestCase):
+             self.assertIn(qname, nx.qnames())
+             self.assertGreaterEqual(len(nx.responses()), 1)
+ 
++    @unittest.skipIf(not tests.util.have_ipv4(), "IPv4 not reachable")
+     def testResolveCacheHit(self):
+         res = dns.resolver.Resolver(configure=False)
+         res.nameservers = ['8.8.8.8']
+diff --git a/tests/util.py b/tests/util.py
+index c9aef6a..2461ab6 100644
+--- a/tests/util.py
++++ b/tests/util.py
+@@ -20,32 +20,92 @@ import inspect
+ import os
+ import socket
+ 
++import dns.message
++import dns.name
++import dns.query
++import dns.rdataclass
++import dns.rdatatype
++
+ # Cache for is_internet_reachable()
+ _internet_reachable = None
++_have_ipv4 = False
++_have_ipv6 = False
+ 
+ def here(filename):
+     return os.path.join(os.path.dirname(__file__), filename)
+ 
+ 
++def check_networking(addresses):
++    """Can we do a DNS resolution via UDP and TCP to at least one of the 
addresses?"""
++    for address in addresses:
++        try:
++            q = dns.message.make_query(dns.name.root, dns.rdatatype.NS)
++            ok = False
++            # We try UDP a few times in case we get unlucky and a packet is 
lost.
++            for i in range(5):
++                # We don't check the answer other than make sure there is one.
++                try:
++                    r = dns.query.udp(q, address, timeout=4)
++                    ns = r.find_rrset(
++                        r.answer, dns.name.root, dns.rdataclass.IN, 
dns.rdatatype.NS
++                    )
++                    ok = True
++                    break
++                except Exception:
++                    continue  # UDP try loop
++            if not ok:
++                continue  # addresses loop
++            try:
++                r = dns.query.tcp(q, address, timeout=4)
++                ns = r.find_rrset(
++                    r.answer, dns.name.root, dns.rdataclass.IN, 
dns.rdatatype.NS
++                )
++                # UDP and TCP both work!
++                return True
++            except Exception:
++                continue
++        except Exception as e:
++            pass
++    return False
++
++
+ def is_internet_reachable():
+     """Check if the Internet is reachable.
+ 
+     Setting the environment variable `NO_INTERNET` will let this
+     function always return False. The result is cached.
++
++    We check using the Google and Cloudflare public resolvers as they are 
highly
++    available and have well-known stable addresses.
+     """
+     global _internet_reachable
+     if _internet_reachable is None:
+         if os.environ.get("NO_INTERNET"):
+             _internet_reachable = False
+         else:
+-            try:
+-                socket.gethostbyname("dnspython.org")
+-                _internet_reachable = True
+-            except socket.gaierror:
+-                _internet_reachable = False
++            global _have_ipv4
++            _have_ipv4 = check_networking(["8.8.8.8", "1.1.1.1"])
++            global _have_ipv6
++            _have_ipv6 = check_networking(
++                ["2001:4860:4860::8888", "2606:4700:4700::1111"]
++            )
++            print(_have_ipv4 or _have_ipv6)
++            _internet_reachable = _have_ipv4 or _have_ipv6
+     return _internet_reachable
+ 
+ 
++def have_ipv4():
++    if not is_internet_reachable():
++        return False
++    return _have_ipv4
++
++
++def have_ipv6():
++    if not is_internet_reachable():
++        return False
++    return _have_ipv6
++
++
+ def enumerate_module(module, super_class):
+     """Yield module attributes which are subclasses of given class"""
+     for attr_name in dir(module):
diff -Nru 
dnspython-2.2.1/debian/patches/0004-tests-Extend-connectivity-check-to-test_async.patch
 
dnspython-2.2.1/debian/patches/0004-tests-Extend-connectivity-check-to-test_async.patch
--- 
dnspython-2.2.1/debian/patches/0004-tests-Extend-connectivity-check-to-test_async.patch
     1969-12-31 17:00:00.000000000 -0700
+++ 
dnspython-2.2.1/debian/patches/0004-tests-Extend-connectivity-check-to-test_async.patch
     2022-06-07 13:32:40.000000000 -0600
@@ -0,0 +1,38 @@
+Date: Tue, 7 Jun 2022 13:31:00 -0600
+Description: Extend connectivity check to test_async
+Author: Dan Bungert <daniel.bung...@canonical.com>
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/dnspython/+bug/1976565
+Forwarded: https://github.com/rthalley/dnspython/pull/815#discussion_r891618064
+Last-Update: 2022-06-07
+---
+ tests/test_async.py | 16 +++++-----------
+ 1 file changed, 5 insertions(+), 11 deletions(-)
+
+diff --git a/tests/test_async.py b/tests/test_async.py
+index 17c44b4..7bda0b7 100644
+--- a/tests/test_async.py
++++ b/tests/test_async.py
+@@ -53,18 +53,12 @@ try:
+ except Exception:
+     pass
+ 
+-# Probe for IPv4 and IPv6
++
+ query_addresses = []
+-for (af, address) in ((socket.AF_INET, '8.8.8.8'),
+-                      (socket.AF_INET6, '2001:4860:4860::8888')):
+-    try:
+-        with socket.socket(af, socket.SOCK_DGRAM) as s:
+-            # Connecting a UDP socket is supposed to return ENETUNREACH if
+-            # no route to the network is present.
+-            s.connect((address, 53))
+-        query_addresses.append(address)
+-    except Exception:
+-        pass
++if tests.util.have_ipv4():
++    query_addresses.append("8.8.8.8")
++if tests.util.have_ipv6():
++    query_addresses.append("2001:4860:4860::8888")
+ 
+ KNOWN_ANYCAST_DOH_RESOLVER_URLS = ['https://cloudflare-dns.com/dns-query',
+                                    'https://dns.google/dns-query',
diff -Nru dnspython-2.2.1/debian/patches/series 
dnspython-2.2.1/debian/patches/series
--- dnspython-2.2.1/debian/patches/series       2022-05-16 12:50:39.000000000 
-0600
+++ dnspython-2.2.1/debian/patches/series       2022-06-07 13:32:05.000000000 
-0600
@@ -1,2 +1,4 @@
 no-setup-requires.patch
 Allow-skipping-test-cases-that-need-Internet-access.patch
+0003-Improve-network-checking-812.patch
+0004-tests-Extend-connectivity-check-to-test_async.patch

Reply via email to