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