summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Yan <felixonmars@archlinux.org>2021-09-01 16:40:28 +0800
committerGitHub <noreply@github.com>2021-09-01 11:40:28 +0300
commitaeb0390094a1c3f29bb4f25a8dab96587a86b3e8 (patch)
treea447d1950cede2fa62116532886150bf41d47f6b
parentc6c350eaa9eb819c6bcabe25113464aed75b9cf5 (diff)
downloadeventlet-aeb0390094a1c3f29bb4f25a8dab96587a86b3e8.tar.gz
greendns: compatibility with dnspython v2
Compatibility with dnspython v2: - `_compute_expiration` was replaced by `_compute_times` - `dns.query.{tcp,udp}` take new arguments Main issue for tracking: https://github.com/eventlet/eventlet/issues/619 This patch discussion: https://github.com/eventlet/eventlet/pull/722 This patch deprecates dnspython<2 pin: https://github.com/eventlet/eventlet/issues/629 Co-authored-by: John Vandenberg <jayvdb@gmail.com> Co-authored-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
-rw-r--r--.github/workflows/test.yaml2
-rw-r--r--eventlet/support/greendns.py86
-rw-r--r--setup.py2
-rw-r--r--tests/greendns_test.py2
-rw-r--r--tests/isolated/socket_resolve_green.py3
-rw-r--r--tox.ini3
6 files changed, 84 insertions, 14 deletions
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index d5b7c45..3801d2a 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -32,6 +32,7 @@ jobs:
- { py: 2.7, toxenv: py27-epolls, ignore-error: false }
- { py: 2.7, toxenv: py27-poll, ignore-error: false }
- { py: 2.7, toxenv: py27-selects, ignore-error: false }
+ - { py: 2.7, toxenv: py27-dnspython1, ignore-error: false }
- { py: 3.5, toxenv: py35-epolls, ignore-error: false }
- { py: 3.5, toxenv: py35-poll, ignore-error: false }
- { py: 3.5, toxenv: py35-selects, ignore-error: false }
@@ -48,6 +49,7 @@ jobs:
- { py: 3.9, toxenv: py39-epolls, ignore-error: false }
- { py: 3.9, toxenv: py39-poll, ignore-error: false }
- { py: 3.9, toxenv: py39-selects, ignore-error: false }
+ - { py: 3.9, toxenv: py39-dnspython1, ignore-error: false }
- { py: 3.x, toxenv: ipv6, ignore-error: false }
- { py: pypy2, toxenv: pypy2-epolls, ignore-error: true }
- { py: pypy3, toxenv: pypy3-epolls, ignore-error: true }
diff --git a/eventlet/support/greendns.py b/eventlet/support/greendns.py
index 10dd04b..76545c7 100644
--- a/eventlet/support/greendns.py
+++ b/eventlet/support/greendns.py
@@ -120,6 +120,16 @@ def is_ip_addr(host):
return is_ipv4_addr(host) or is_ipv6_addr(host)
+# 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:
+ def compute_expiration(query, timeout):
+ return query._compute_times(timeout)[1]
+
+
class HostsAnswer(dns.resolver.Answer):
"""Answer class for HostsResolver object"""
@@ -660,8 +670,21 @@ def _net_write(sock, data, expiration):
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.
@@ -686,7 +709,21 @@ def udp(q, where, timeout=DNS_QUERY_TIMEOUT, port=53,
@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:
@@ -708,10 +745,13 @@ def udp(q, where, timeout=DNS_QUERY_TIMEOUT, port=53,
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 = dns.query._compute_expiration(timeout)
+ expiration = compute_expiration(dns.query, timeout)
if source is not None:
s.bind(source)
while True:
@@ -756,14 +796,23 @@ def udp(q, where, timeout=DNS_QUERY_TIMEOUT, port=53,
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.
@@ -785,7 +834,19 @@ def tcp(q, where, timeout=DNS_QUERY_TIMEOUT, port=53,
@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:
@@ -801,10 +862,13 @@ def tcp(q, where, timeout=DNS_QUERY_TIMEOUT, port=53,
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 = dns.query._compute_expiration(timeout)
+ expiration = compute_expiration(dns.query, timeout)
if source is not None:
s.bind(source)
while True:
@@ -829,7 +893,9 @@ def tcp(q, where, timeout=DNS_QUERY_TIMEOUT, port=53,
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
diff --git a/setup.py b/setup.py
index cf5150a..3a446e7 100644
--- a/setup.py
+++ b/setup.py
@@ -15,7 +15,7 @@ setuptools.setup(
url='http://eventlet.net',
packages=setuptools.find_packages(exclude=['benchmarks', 'tests', 'tests.*']),
install_requires=(
- 'dnspython >= 1.15.0, < 2.0.0',
+ 'dnspython >= 1.15.0',
'greenlet >= 0.3',
'monotonic >= 1.4;python_version<"3.5"',
'six >= 1.10.0',
diff --git a/tests/greendns_test.py b/tests/greendns_test.py
index a6faae5..7419b35 100644
--- a/tests/greendns_test.py
+++ b/tests/greendns_test.py
@@ -901,7 +901,7 @@ class TinyDNSTests(tests.LimitedTestCase):
resolver.nameserver_ports[dnsaddr[0]] = dnsaddr[1]
response = resolver.query('host.example.com', 'a', tcp=True)
self.assertIsInstance(response, Answer)
- self.assertEqual(response.rrset.items[0].address, expected_ip)
+ self.assertEqual(list(response.rrset.items)[0].address, expected_ip)
def test_reverse_name():
diff --git a/tests/isolated/socket_resolve_green.py b/tests/isolated/socket_resolve_green.py
index 6017d5e..611d7dc 100644
--- a/tests/isolated/socket_resolve_green.py
+++ b/tests/isolated/socket_resolve_green.py
@@ -7,6 +7,7 @@ if __name__ == '__main__':
import time
import dns.message
import dns.query
+ import dns.flags
n = 10
delay = 0.01
@@ -17,7 +18,7 @@ if __name__ == '__main__':
addr = addr_map[qname.to_text()]
r = dns.message.make_response(q)
r.index = None
- r.flags = 256
+ r.flags = dns.flags.QR | dns.flags.RD
r.answer.append(dns.rrset.from_text(str(qname), 60, 'IN', 'A', addr))
r.time = 0.001
eventlet.sleep(delay)
diff --git a/tox.ini b/tox.ini
index 59fd833..3697f7e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -16,7 +16,7 @@ statistics = 1
[tox]
minversion=2.5
envlist =
- ipv6, pep8, py{27,35,36,37,38,39,py2,py3}-{selects,poll,epolls}, py38-openssl
+ ipv6, pep8, py{27,35,36,37,38,39,py2,py3}-{selects,poll,epolls}, py38-openssl, py27-dnspython1, py39-dnspython1
skipsdist = True
[testenv:ipv6]
@@ -74,6 +74,7 @@ deps =
py{38,39}: psycopg2-binary==2.8.4
setuptools==38.5.1
{selects,poll,epolls}: pyzmq==19.0.2
+ dnspython1: dnspython<2
usedevelop = True
commands =
nosetests --verbose {env:tox_cover_args} {posargs:tests/}