py-mine / mcstatus

A Python library for checking the status of Minecraft servers
https://mcstatus.readthedocs.io
Apache License 2.0
468 stars 37 forks source link

dns: use search suffixes properly #858

Closed katrinafyi closed 1 month ago

katrinafyi commented 1 month ago

DNS resolvers support a list of search domain suffixes, which are appended to hostnames before querying the DNS server. This enables resolving, e.g. internal LAN-only addresses such as http://go/.

Currently, the dns library used by mcstatus fails to do so when using the query functionality. This leads to a confusing discrepancy between status (which succeeds by using socket resolution methods), and query which uses the external dnspython.

```bash-session [rina@leng mcstatus]$ tail -n4 /etc/resolv.conf nameserver 127.0.0.53 options edns0 trust-ad search lan [rina@leng mcstatus]$ ping pluto PING pluto.lan (100.89.2.34) 56(84) bytes of data. 64 bytes from pluto.lan (100.89.2.34): icmp_seq=1 ttl=64 time=5.47 ms 64 bytes from pluto.lan (100.89.2.34): icmp_seq=2 ttl=64 time=5.62 ms ^C --- pluto.siren-koi.ts.net ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 5.466/5.543/5.620/0.077 ms [rina@leng mcstatus]$ poetry run python -m mcstatus pluto status version: v1.12.2 (protocol 340) motd: "Motd(parsed=['', , '', , 'SevTech: Ages Server', , ' - ', , 'v3.2.3', ], raw={'text': '§d§oSevTech: Ages Server§r - §4v3.2.3'}, bedrock=False)" players: 0/20 No players online [rina@leng mcstatus]$ poetry run python -m mcstatus pluto query Traceback (most recent call last): File "/home/rina/progs/mcstatus/mcstatus/address.py", line 139, in resolve_ip ip = ipaddress.ip_address(host) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/ipaddress.py", line 54, in ip_address raise ValueError(f'{address!r} does not appear to be an IPv4 or IPv6 address') ValueError: 'pluto' does not appear to be an IPv4 or IPv6 address During handling of the above exception, another exception occurred: Traceback (most recent call last): File "", line 198, in _run_module_as_main File "", line 88, in _run_code File "/home/rina/progs/mcstatus/mcstatus/__main__.py", line 104, in main() File "/home/rina/progs/mcstatus/mcstatus/__main__.py", line 100, in main args.func(server) File "/home/rina/progs/mcstatus/mcstatus/__main__.py", line 56, in query response = server.query() ^^^^^^^^^^^^^^ File "/home/rina/progs/mcstatus/mcstatus/server.py", line 159, in query ip = str(self.address.resolve_ip()) ^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/rina/progs/mcstatus/mcstatus/address.py", line 144, in resolve_ip ip_addr = mcstatus.dns.resolve_a_record(self.host, lifetime=lifetime) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/rina/progs/mcstatus/mcstatus/dns.py", line 22, in resolve_a_record answers = dns.resolver.resolve(hostname, RdataType.A, lifetime=lifetime) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/rina/.cache/pypoetry/virtualenvs/mcstatus-txFvW3ui-py3.12/lib/python3.12/site-packages/dns/resolver.py", line 1565, in resolve return get_default_resolver().resolve( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/rina/.cache/pypoetry/virtualenvs/mcstatus-txFvW3ui-py3.12/lib/python3.12/site-packages/dns/resolver.py", line 1318, in resolve (nameserver, tcp, backoff) = resolution.next_nameserver() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/rina/.cache/pypoetry/virtualenvs/mcstatus-txFvW3ui-py3.12/lib/python3.12/site-packages/dns/resolver.py", line 763, in next_nameserver raise NoNameservers(request=self.request, errors=self.errors) dns.resolver.NoNameservers: All nameservers failed to answer the query pluto. IN A: Server Do53:127.0.0.53@53 answered The DNS operation timed out.; Server Do53:127.0.0.53@53 answered REFUSED [rina@leng mcstatus]$ poetry run python -m mcstatus pluto.lan query host: 127.0.0.2:25565 software: v1.12.2 vanilla plugins: [] motd: "Motd(parsed=['', , '', , 'SevTech: Ages Server', , ' - ', , 'v3.2.3'], raw='§d§oSevTech: Ages Server§r - §4v3.2.3', bedrock=False)" players: 0/20 [] ```

Alternatively, you can also set https://dnspython.readthedocs.io/en/latest/resolver-class.html#dns.resolver.Resolver.use_search_by_default.

Also, just using socket.getaddrinfo would be much simpler.

katrinafyi commented 1 month ago

Test cases adjusted but I don't think these are a good way to approach testing.