Fwiw, i replaced eventlet/support/greendns.py by the version shipped in eventlet 0.32.0 (eg after https://github.com/eventlet/eventlet/commit/aeb0390094a1c3f29bb4f25a8dab96587a86b3e8) and the minimal testcase now works.

[30/11 14:07] [email protected]:/tmp $sudo mv greendns.py /usr/lib/python3/dist-packages/eventlet/support/greendns.py
[30/11 14:08] [email protected]:/tmp $python3 ./testdnspy.py
dmfrjf4ips8xa.cloudfront.net. 60 IN A 143.204.229.111
dmfrjf4ips8xa.cloudfront.net. 60 IN A 143.204.229.121
dmfrjf4ips8xa.cloudfront.net. 60 IN A 143.204.229.30
dmfrjf4ips8xa.cloudfront.net. 60 IN A 143.204.229.26

cf attached patch between both files.

would it be possible to expand https://sources.debian.org/patches/python-eventlet/0.26.1-7/Replace-dnspython-_compute_expiration-by-_compute_times.patch/ with the full commit and ship 0.26.1-8 to bullseye, or i will have to live with /etc/hosts kludges for now ?

i'm not sure, but maybe sid and bookworm are also affected by the same bug as eventlet is only 0.30.0 there.
--- /usr/lib/python3/dist-packages/eventlet/support/greendns.py.orig	2021-11-30 14:07:41.871155411 +0100
+++ /usr/lib/python3/dist-packages/eventlet/support/greendns.py	2021-11-30 14:07:06.890889076 +0100
@@ -120,12 +120,13 @@
     return is_ipv4_addr(host) or is_ipv6_addr(host)
 
 
-def compute_expiration(query, timeout):
-    # NOTE(ralonsoh): in dnspython v2.0.0, "_compute_expiration" was replaced
-    # by "_compute_times".
-    if hasattr(query, '_compute_expiration'):
+# NOTE(ralonsoh): in dnspython v2.0.0, "_compute_expiration" was replaced
+# by "_compute_times".
+if hasattr(dns.query, '_compute_expiration'):
+    def compute_expiration(query, timeout):
         return query._compute_expiration(timeout)
-    else:
+else:
+    def compute_expiration(query, timeout):
         return query._compute_times(timeout)[1]
 
 
@@ -669,8 +670,21 @@
                 raise dns.exception.Timeout
 
 
+# Test if raise_on_truncation is an argument we should handle.
+# It was newly added in dnspython 2.0
+try:
+    dns.message.from_wire("", raise_on_truncation=True)
+except dns.message.ShortHeader:
+    _handle_raise_on_truncation = True
+except TypeError:
+    # Argument error, there is no argument "raise_on_truncation"
+    _handle_raise_on_truncation = False
+
+
 def udp(q, where, timeout=DNS_QUERY_TIMEOUT, port=53,
-        af=None, source=None, source_port=0, ignore_unexpected=False):
+        af=None, source=None, source_port=0, ignore_unexpected=False,
+        one_rr_per_rrset=False, ignore_trailing=False,
+        raise_on_truncation=False, sock=None):
     """coro friendly replacement for dns.query.udp
     Return the response obtained after sending a query via UDP.
 
@@ -695,7 +709,21 @@
     @type source_port: int
     @param ignore_unexpected: If True, ignore responses from unexpected
     sources.  The default is False.
-    @type ignore_unexpected: bool"""
+    @type ignore_unexpected: bool
+    @param one_rr_per_rrset: If True, put each RR into its own
+    RRset.
+    @type one_rr_per_rrset: bool
+    @param ignore_trailing: If True, ignore trailing
+    junk at end of the received message.
+    @type ignore_trailing: bool
+    @param raise_on_truncation: If True, raise an exception if
+    the TC bit is set.
+    @type raise_on_truncation: bool
+    @param sock: the socket to use for the
+    query.  If None, the default, a socket is created.  Note that
+    if a socket is provided, it must be a nonblocking datagram socket,
+    and the source and source_port are ignored.
+    @type sock: socket.socket | None"""
 
     wire = q.to_wire()
     if af is None:
@@ -717,7 +745,10 @@
         if source is not None:
             source = (source, source_port, 0, 0)
 
-    s = socket.socket(af, socket.SOCK_DGRAM)
+    if sock:
+        s = sock
+    else:
+        s = socket.socket(af, socket.SOCK_DGRAM)
     s.settimeout(timeout)
     try:
         expiration = compute_expiration(dns.query, timeout)
@@ -765,14 +796,23 @@
     finally:
         s.close()
 
-    r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac)
+    if _handle_raise_on_truncation:
+        r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
+                                  one_rr_per_rrset=one_rr_per_rrset,
+                                  ignore_trailing=ignore_trailing,
+                                  raise_on_truncation=raise_on_truncation)
+    else:
+        r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
+                                  one_rr_per_rrset=one_rr_per_rrset,
+                                  ignore_trailing=ignore_trailing)
     if not q.is_response(r):
         raise dns.query.BadResponse()
     return r
 
 
 def tcp(q, where, timeout=DNS_QUERY_TIMEOUT, port=53,
-        af=None, source=None, source_port=0):
+        af=None, source=None, source_port=0,
+        one_rr_per_rrset=False, ignore_trailing=False, sock=None):
     """coro friendly replacement for dns.query.tcp
     Return the response obtained after sending a query via TCP.
 
@@ -794,7 +834,19 @@
     @type source: string
     @param source_port: The port from which to send the message.
     The default is 0.
-    @type source_port: int"""
+    @type source_port: int
+    @type ignore_unexpected: bool
+    @param one_rr_per_rrset: If True, put each RR into its own
+    RRset.
+    @type one_rr_per_rrset: bool
+    @param ignore_trailing: If True, ignore trailing
+    junk at end of the received message.
+    @type ignore_trailing: bool
+    @param sock: the socket to use for the
+    query.  If None, the default, a socket is created.  Note that
+    if a socket is provided, it must be a nonblocking datagram socket,
+    and the source and source_port are ignored.
+    @type sock: socket.socket | None"""
 
     wire = q.to_wire()
     if af is None:
@@ -810,7 +862,10 @@
         destination = (where, port, 0, 0)
         if source is not None:
             source = (source, source_port, 0, 0)
-    s = socket.socket(af, socket.SOCK_STREAM)
+    if sock:
+        s = sock
+    else:
+        s = socket.socket(af, socket.SOCK_STREAM)
     s.settimeout(timeout)
     try:
         expiration = compute_expiration(dns.query, timeout)
@@ -838,7 +893,9 @@
         wire = bytes(_net_read(s, l, expiration))
     finally:
         s.close()
-    r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac)
+    r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
+                              one_rr_per_rrset=one_rr_per_rrset,
+                              ignore_trailing=ignore_trailing)
     if not q.is_response(r):
         raise dns.query.BadResponse()
     return r

Reply via email to