By default, DNS resolution is done by using the UDP transport layer. However I have noticed that on some network with a WiFi connection, this often yields a 5-second timeout before the resolution is automatically retried, whatever the DNS servers, including the DNS server provided by the ADSL modem router. I sometimes got two timeout in a row, so that DNS resolution could take 10 seconds! This issue is worsened by the fact that GNU/Linux does not use a local DNS cache by default.
Thus I wanted to try DNS with the TCP transport layer. For that, one can use the undocumented use-vc option. However not all DNS servers support TCP, and in particular, this did not work with the DNS server of the modem router or the ones of the ISP. Fortunately, this works with Google Public DNS. Here is the corresponding /etc/resolv.conf file:
options use-vc nameserver 8.8.8.8 nameserver 8.8.4.4
Then, one may want to use this configuration (or another one) only on some given networks. While some network management tools allow one to specify the name servers for some given networks, I have not seen a way to give options like the use-vc one. There are several alternative solutions. Except when mentioned, resolvconf is assumed not to be used, otherwise there may be some clashes. Ditto for local DNS caches.
For ifupdown and its /etc/network/interfaces configuration file: One can use a mapping stanza with the guessnet tool (/usr/sbin/guessnet-ifupdown as the mapping script), for instance to test the MAC address of the router. For the selected networks, one can generate the specific /etc/resolv.conf configuration via a up (a.k.a. post-up) command. Or if resolvconf is used, in the simplest cases, one can define a pre-up command to create the /etc/resolvconf/resolv.conf.d/head file with the above configuration, and a down command to remove this configuration (so that it is not reused on another network).
For wicd and WiFi connections: One can add a script in the /etc/wicd/scripts/postconnect directory. There one can test $3, whose value is the BSSID, which is the MAC address of the WiFi access point, typically the same as above. This was what I was initially doing (but I now have a better solution, see below):
#!/bin/sh logger "wicd script postconnect/dns-with-tcp $*" if [ "x$3" = x00:1F:33:89:73:4E ] && [ ! -L /etc/resolv.conf ]; then logger "Google Public DNS with TCP to avoid recurrent timeout" cat > /etc/resolv.conf <<EOF options use-vc nameserver 8.8.8.8 nameserver 8.8.4.4 EOF fi
Note: The wicd local script feature might be removed in the future. That's why I do not use such scripts. Their status and documentation are still unclear. See Debian bug 537198.
If DHCP is used, as this is usually the case: With Internet Software Consortium's DHCP client (I have not tried other DHCP clients), one can use an enter hook script for BOUND. This is a better solution because the /etc/resolv.conf file is normally re-created here and an enter hook script is needed anyway (see below). However the MAC address of the router is not provided and it is not in the ARP table yet (/proc/net/arp does not contain any entry). Moreover, an ARP lookup with the arping command fails. The idea I got from this message is to ping the router in order to fill the ARP table. This is a bit more tricky here as the interface has not been configured yet; so, one needs to tell the ping command which interface to use:
ping -n -c 1 -I "$interface" "$new_routers"
The packet is lost, but this does what we want. Well… This did for me in 2015-08, but in 2016-08, this no longer works. An alternative solution is to do this in an exit hook script: an incorrect /etc/resolv.conf file will be created first, but it will be rewritten in this exit hook script, so that this is not much a problem, but it is best to make sure that this script is the first exit hook script.
Now, for DHCP (usual case), there is another problem: the /etc/resolv.conf file is normally rewritten when the lease is renewed, meaning that the user's configuration done above is overwritten. For Internet Software Consortium's DHCP client, a solution is to prevent this from occurring on the concerned networks by redefining the make_resolv_conf
shell function in an enter hook script for RENEW.
Here is an example of a DHCP hook script for both BOUND and RENEW (one may need to add other reasons, but they do not occur for me, so that I could not test them), to be used as both an enter hook script and an exit hook script. The make_resolv_conf
function is executed by dhclient-script just after the enter hook scripts; for BOUND, when this script is run as an exit hook script, we do the same thing and execute this function.
#!/bin/sh if [ ! -L /etc/resolv.conf ]; then logger "$1 with reason=$reason" # the MAC address of the router mac=00:1f:33:89:73:4e case "$reason" in BOUND) # The ping has an effect to fill the ARP table "/proc/net/arp". # This trick is inspired by: # https://www.debian-fr.org/serveur-dns-timout-resolv-conf-et-dhclient-t24401.html # In 2015-08, this was working as an enter hook, but in 2016-08, # the ping fails (ditto with arping, after trying various options). # So, this script should be used as an exit hook too. ping -n -c 1 -I "$interface" "$new_routers" > /dev/null if grep -i -q $mac /proc/net/arp; then logger "Google Public DNS with TCP to avoid recurrent timeout" make_resolv_conf() { logger "creating /etc/resolv.conf (Google Public DNS with TCP)" cat > /etc/resolv.conf <<EOF options use-vc nameserver 8.8.8.8 nameserver 8.8.4.4 EOF } case $1 in */dhclient-exit-hooks.d/*) make_resolv_conf;; esac fi ;; RENEW) case $1 in */dhclient-enter-hooks.d/*) if grep -i -q $mac /proc/net/arp; then logger "do not overwrite /etc/resolv.conf" make_resolv_conf() { : ; } fi;; esac ;; esac fi
Note: For the RENEW case, instead of testing the MAC address, one can also test the presence of some text in the /etc/resolv.conf file, e.g. do not overwrite on lease renewal
, if such a text was written when the file was created.