#998308 /usr/bin/drill: drill does not respect the /etc/resolv.conf nameserver order

Package:
ldnsutils
Source:
ldns
Description:
ldns library for DNS programming - tools (includes drill tool)
Submitter:
Bjørn Mork
Date:
2022-04-26 16:21:05 UTC
Severity:
minor
Tags:
#998308#5
Date:
2021-11-02 08:27:40 UTC
From:
To:
NetworkManager will add nameservers for all connected links to /etc/resolv.conf
using the same priority given to the associated default routes.  Name servers
are often only usable on the link they are associated with.  But this scheme
works fine with libc since it strictly obeys the order given in /etc/resolv.conf
It will therefore only use the topmost entries, which happen to be associated
with the link with the lowest default route metric.

This is the documented behaviour in debian.  Quoting from resolv.conf(5)

       nameserver Name server IP address
              Internet address of a name server that the resolver should query, either
              an IPv4 address (in dot notation), or an IPv6 address in colon (and pos‐
              sibly dot) notation as per RFC 2373.  Up to MAXNS (currently 3, see <re‐
              solv.h>) name servers may be listed, one per keyword.  If there are mul‐
              tiple  servers,  the  resolver library queries them in the order listed.

However, drill seems to use all entries in a random(?) order.  Or at least in an order
which changes from one run to another, causing failures which come and go depending on
whether the nameserver works on the primary link or not.

Simple example from my laptop, having both wlan0 and wwan0 connected:

bjorn@miraculix:~$ cat /etc/resolv.conf
# Generated by NetworkManager
search corp.telenor.no
nameserver 148.122.16.253
nameserver 148.122.164.253
nameserver 2001:4600:4:fff::52
# NOTE: the libc resolver may not support more than 3 nameservers.
# The nameservers listed below may not be recognized.
nameserver 2001:4600:4:1fff::52
nameserver 193.213.112.4
nameserver 130.67.15.198
bjorn@miraculix:~$ ip route
default via 10.168.72.1 dev wlan0 proto dhcp metric 600
default via 10.213.245.177 dev wwan0 proto static metric 700
10.168.72.0/24 dev wlan0 proto kernel scope link src 10.168.72.206 metric 600
10.213.245.160/27 dev wwan0 proto kernel scope link src 10.213.245.176 metric 700
bjorn@miraculix:~$ ip -6 route
::1 dev lo proto kernel metric 256 pref medium
2a02:2121:283:c2fb::/64 dev wwan0 proto kernel metric 256 pref medium
2a02:2121:283:c2fb::/64 dev wwan0 proto kernel metric 700 pref medium
fe80::/64 dev tap0 proto kernel metric 256 pref medium
fe80::/64 dev tap0.42 proto kernel metric 256 pref medium
fe80::/64 dev wlan0 proto kernel metric 600 pref medium
default via 2a02:2121:283:c2fb:89c8:30a8:6eff:bb2e dev wwan0 proto static metric 700 pref medium
default via fe80::89c8:30a8:6eff:bb2e dev wwan0 proto ra metric 1024 expires 61681sec hoplimit 255 pref medium


stracing drill shows that it uses name servers from the whole list, including
entries below the 3 server cutoff,  in an arbitrary order:

bjorn@miraculix:~$ strace -f -e sendto /usr/bin/drill -S debian.org
;; Number of trusted keys: 1
sendto(3, "\373\213\1\20\0\1\0\0\0\0\0\1\6debian\3org\0\0\1\0\1\0\0)\20"..., 39, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("193.213.112.4")}, 16) = 39
;; Chasing: debian.org. A
sendto(3, "\347\212\1\20\0\1\0\0\0\0\0\1\6debian\3org\0\0000\0\1\0\0)\20"..., 39, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("130.67.15.198")}, 16) = 39
sendto(3, "\357@\1\20\0\1\0\0\0\0\0\1\6debian\3org\0\0+\0\1\0\0)\20"..., 39, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("148.122.164.253")}, 16) = 39
sendto(3, "!\317\1\20\0\1\0\0\0\0\0\1\3org\0\0000\0\1\0\0)\20\0\0\0\200\0\0\0", 32, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("148.122.16.253")}, 16) = 32
sendto(3, "\327$\1\20\0\1\0\0\0\0\0\1\3org\0\0+\0\1\0\0)\20\0\0\0\200\0\0\0", 32, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("148.122.164.253")}, 16) = 32
sendto(3, "\320\300\1\20\0\1\0\0\0\0\0\1\0\0000\0\1\0\0)\20\0\0\0\200\0\0\0", 28, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("193.213.112.4")}, 16) = 28
sendto(3, "\212\177\1\20\0\1\0\0\0\0\0\1\0\0+\0\1\0\0)\20\0\0\0\200\0\0\0", 28, 0, {sa_family=AF_INET6, sin6_port=htons(53), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "2001:4600:4:fff::52", &sin6_addr), sin6_scope_id=0}, 28) = 28
sendto(3, "<Q\1\20\0\1\0\0\0\0\0\1\0\0000\0\1\0\0)\20\0\0\0\200\0\0\0", 28, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("130.67.15.198")}, 16) = 28
sendto(3, "\10\265\1\20\0\1\0\0\0\0\0\1\3org\0\0000\0\1\0\0)\20\0\0\0\200\0\0\0", 32, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("130.67.15.198")}, 16) = 32
sendto(3, "\6\214\1\20\0\1\0\0\0\0\0\1\6debian\3org\0\0000\0\1\0\0)\20"..., 39, 0, {sa_family=AF_INET6, sin6_port=htons(53), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "2001:4600:4:1fff::52", &sin6_addr), sin6_scope_id=0}, 28) = 39


DNSSEC Trust tree:
debian.org. (A)
|---debian.org. (DNSKEY keytag: 33921 alg: 8 flags: 256)
    |---debian.org. (DNSKEY keytag: 8500 alg: 8 flags: 257)
    |---debian.org. (DS keytag: 8500 digest type: 2)
        |---org. (DNSKEY keytag: 63966 alg: 8 flags: 256)
            |---org. (DNSKEY keytag: 26974 alg: 8 flags: 257)
            |---org. (DS keytag: 26974 digest type: 2)
                |---. (DNSKEY keytag: 14748 alg: 8 flags: 256)
                    |---. (DNSKEY keytag: 20326 alg: 8 flags: 257)
;; Chase successful
+++ exited with 0 +++

This happens to work in this case since all the servers are reachable and will answer
queries over the primary link.  But that is often not so.


Bjørn


- -- System Information:
Debian Release: 11.1
  APT prefers stable-security
  APT policy: (700, 'stable-security'), (700, 'stable'), (600, 'unstable')
Architecture: amd64 (x86_64)
Foreign Architectures: i386, arm64

Kernel: Linux 5.10.0-9-amd64 (SMP w/4 CPU threads)
Kernel taint flags: TAINT_USER
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE=en_US:en
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)

Versions of packages ldnsutils depends on:
ii  libc6       2.31-13+deb11u2
ii  libldns3    1.7.1-2+b1
ii  libpcap0.8  1.10.0-2
ii  libssl1.1   1.1.1k-1+deb11u1

ldnsutils recommends no packages.

ldnsutils suggests no packages.

- -- no debconf information
-----BEGIN PGP SIGNATURE-----

iIQEARYKACwWIQRoe+CASfFh7aZ6shIiBE7Lv6RhXQUCYYD2eQ4cYmpvcm5AbW9y
ay5ubwAKCRAiBE7Lv6RhXTgmAQCME2fHN7LtW769GwePP/I3osA/hKaOUwb4zpMJ
rbLHDQD/R9YE++Zp8ZK4oKCtc3AA+ooad9j7VxZpAJ5YMFIVMAc=
=HU5L
-----END PGP SIGNATURE-----

#998308#10
Date:
2022-04-26 16:14:52 UTC
From:
To:
Control: severity -1 minor
Control: tag -1 + wontfix upstream
...
...

Drill is a dns debugging tool using a special-purpose dns library.

What do you read about resolv.conf is being said for the standard
glibc resolver, the manpage describes how the glibc resolver works.

Other tools may use the information in there in some other ways,
there's no obligation an information is used only the way it was
initially supposed to be used.

Drill has another tidbit here: it does not use default nameserver
of 127.0.0.1 if no nameserver line is specified in resolv.conf.

While this might be unexpected to a new user of drill, I don't
see it is a bug per se. It is the way how it works (but the
lack of default nameserver is annoying).

I'm lowering severity of this bug and tagging with wontfix.

Rewriting the upstream-decided algorithm of nameserver query
order in Debian is definitely not an option.  If you think
the behavior is incorrect, please file upstream bugreport
about this issue.

Thank you for the bug report!

/mjt