Skip to content

Commit

Permalink
In tests, work around musl libc and docker issues.
Browse files Browse the repository at this point in the history
Specifically: musl libc's getaddrinfo behavior always returns
a canonical name.

Docker's resolver proxy doesn't do dangling CNAMEs correctly
and also answers NXDOMAIN in some cases where it should say
no error, no data.
  • Loading branch information
rthalley committed Jan 27, 2024
1 parent 8131d0c commit 1b22985
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 17 deletions.
14 changes: 11 additions & 3 deletions tests/test_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@
except Exception:
pass

# Docker too! It not only has problems with dangling CNAME, but also says NXDOMAIN when
# it should say no error no data.
_is_docker = tests.util.is_docker()

query_addresses = []
family = socket.AF_UNSPEC
if tests.util.have_ipv4():
Expand Down Expand Up @@ -235,8 +239,10 @@ async def run5():
dns.reversename.from_address("8.8.8.8")
)

with self.assertRaises(dns.resolver.NoAnswer):
self.async_run(run5)
if not _is_docker:
# docker returns NXDOMAIN!
with self.assertRaises(dns.resolver.NoAnswer):
self.async_run(run5)

def testCanonicalNameNoCNAME(self):
cname = dns.name.from_text("www.google.com")
Expand All @@ -255,7 +261,9 @@ async def run():

self.assertEqual(self.async_run(run), cname)

@unittest.skipIf(_systemd_resolved_present, "systemd-resolved in use")
@unittest.skipIf(
_systemd_resolved_present or _is_docker, "systemd-resolved or docker in use"
)
def testCanonicalNameDangling(self):
name = dns.name.from_text("dangling-cname.dnspython.org")
cname = dns.name.from_text("dangling-target.dnspython.org")
Expand Down
21 changes: 15 additions & 6 deletions tests/test_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

from io import StringIO
import selectors
import sys
import socket
import sys
import time
import unittest
import pytest
from io import StringIO
from unittest.mock import patch

import pytest

import dns.e164
import dns.message
import dns.name
Expand Down Expand Up @@ -61,6 +62,10 @@ class Server(object):
except Exception:
pass

# Docker too! It not only has problems with dangling CNAME, but also says NXDOMAIN when
# it should say no error no data.
_is_docker = tests.util.is_docker()


resolv_conf = """
/t/t
Expand Down Expand Up @@ -690,8 +695,10 @@ def testResolveName(self):
with self.assertRaises(dns.resolver.NXDOMAIN):
dns.resolver.resolve_name("nxdomain.dnspython.org")

with self.assertRaises(dns.resolver.NoAnswer):
dns.resolver.resolve_name(dns.reversename.from_address("8.8.8.8"))
if not _is_docker:
# We skip docker as it says NXDOMAIN!
with self.assertRaises(dns.resolver.NoAnswer):
dns.resolver.resolve_name(dns.reversename.from_address("8.8.8.8"))

@patch.object(dns.message.Message, "use_edns")
def testResolveEdnsOptions(self, message_use_edns_mock):
Expand Down Expand Up @@ -792,7 +799,9 @@ def testCanonicalNameCNAME(self):
cname = dns.name.from_text("dmfrjf4ips8xa.cloudfront.net")
self.assertEqual(dns.resolver.canonical_name(name), cname)

@unittest.skipIf(_systemd_resolved_present, "systemd-resolved in use")
@unittest.skipIf(
_systemd_resolved_present or _is_docker, "systemd-resolved or docker in use"
)
def testCanonicalNameDangling(self):
name = dns.name.from_text("dangling-cname.dnspython.org")
cname = dns.name.from_text("dangling-target.dnspython.org")
Expand Down
19 changes: 12 additions & 7 deletions tests/test_resolver_override.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def test_override(self):
dns.resolver.restore_system_resolver()
self.assertTrue(socket.getaddrinfo is dns.resolver._original_getaddrinfo)

def equivalent_info(self, a, b):
def equivalent_info(self, a, b, q):
if len(a) != len(b):
return False
for x in a:
Expand All @@ -78,16 +78,21 @@ def equivalent_info(self, a, b):
# looking for a zero protocol.
y = (x[0], x[1], 0, x[3], x[4])
if y not in b:
print("NOT EQUIVALENT")
print(a)
print(b)
return False
# musl libc insists on always providing a canonical name, so
# accept that too.
y = (x[0], x[1], x[2], q, x[4])
if y not in b:
print("NOT EQUIVALENT")
print(a)
print(b)
return False
return True

def equivalent(self, *args, **kwargs):
q = args[0]
a = socket.getaddrinfo(*args, **kwargs)
b = dns.resolver._original_getaddrinfo(*args, **kwargs)
return self.equivalent_info(a, b)
return self.equivalent_info(a, b, q)

@unittest.skipIf(
sys.platform == "win32", "avoid windows original getaddrinfo issues"
Expand Down Expand Up @@ -139,7 +144,7 @@ def test_getaddrinfo_nxdomain(self):
def test_getaddrinfo_service(self):
a = socket.getaddrinfo("dns.google", "domain")
b = socket.getaddrinfo("dns.google", 53)
self.assertTrue(self.equivalent_info(a, b))
self.assertTrue(self.equivalent_info(a, b, "dns.google"))
try:
socket.getaddrinfo("dns.google", "domain", flags=socket.AI_NUMERICSERV)
self.assertTrue(False) # should not happen!
Expand Down
10 changes: 9 additions & 1 deletion tests/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import enum
import inspect
import os
import socket

import dns.message
import dns.name
Expand Down Expand Up @@ -123,3 +122,12 @@ def check_enum_exports(module, eq_callback, only=None):
for flag, value in attr.__members__.items():
# print(module, flag, value)
eq_callback(getattr(module, flag), value)


def is_docker() -> bool:
# There are a lot of answers to "am I runnning in a container" and none appear
# to work reliably, so we're settling for "am I running in docker?"
try:
return os.path.isfile("/.dockerenv")
except Exception:
return False

0 comments on commit 1b22985

Please sign in to comment.