Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

greendns: Use _compute_times on dnspython 2 (Updated) #722

Merged
merged 8 commits into from Sep 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/test.yaml
Expand Up @@ -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 }
Expand All @@ -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 }
temoto marked this conversation as resolved.
Show resolved Hide resolved
- { py: 3.x, toxenv: ipv6, ignore-error: false }
- { py: pypy2, toxenv: pypy2-epolls, ignore-error: true }
- { py: pypy3, toxenv: pypy3-epolls, ignore-error: true }
Expand Down
86 changes: 76 additions & 10 deletions eventlet/support/greendns.py
Expand Up @@ -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"""

Expand Down Expand Up @@ -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.

Expand All @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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.

Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -15,7 +15,7 @@
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',
Expand Down
2 changes: 1 addition & 1 deletion tests/greendns_test.py
Expand Up @@ -901,7 +901,7 @@ def test_noraise_dns_tcp(self):
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():
Expand Down
3 changes: 2 additions & 1 deletion tests/isolated/socket_resolve_green.py
Expand Up @@ -7,6 +7,7 @@
import time
import dns.message
import dns.query
import dns.flags

n = 10
delay = 0.01
Expand All @@ -17,7 +18,7 @@ def slow_udp(q, *a, **kw):
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)
Expand Down
3 changes: 2 additions & 1 deletion tox.ini
Expand Up @@ -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]
Expand Down Expand Up @@ -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/}
Expand Down