Ruby should set AI_V4MAPPED | AI_ADDRCONFIG getaddrinfo flags by default
Currently, DNS lookups made with
getaddrinfo from Ruby (i.e. not from the
Resolv module) cause both A and AAAA DNS requests to be made, even on systems that don’t actually have an IPv6 address that could possibly make the AAAA response useful. I wouldn’t really care about this, normally, but glibc has a bug (https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1961697) which can cause a 5-second delay in DNS lookups when both A and AAAA records are queried in parallel. This bug is fixed in glibc upstream but still present in some LTS linux distros (Ubuntu 18.04 and 20.04 at least), so I think it’s worthwhile to try and work around it in circumstances where the AAAA request is pointless anyway.
The dual A/AAAA lookup happens because whenever Ruby calls getaddrinfo to perform DNS lookups, it always sets
hints, and sets
hints->ai_flags to zero by default unless flags are specified by the caller (e.g.
AI_PASSIVE is set when binding a TCP server socket in
This matches the default value of
ai_flags specified by POSIX, which is zero. However, glibc behaves differently. When glibc’s
getaddrinfo function is called with
NULL for the
hints parameter, it defaults the
ai_flags value to
(AI_V4MAPPED | AI_ADDRCONFIG). The manpage (from the Linux man-pages project - https://man7.org/linux/man-pages/man3/getaddrinfo.3.html) claims “this is an improvement on the standard” (although I couldn’t find this mentioned in the glibc manual itself).
Of course, we’re not actually ever calling
getaddrinfo with NULL
hints; so, we never actually use these flags on glibc systems (unless they’re explicitly specified by the caller).
My proposal is that we should change Ruby to set these two flags by default, when they’re available, in the following circumstances:
- In all calls made internally to
rsock_getaddrinfoas a result of socket functions like
- EXCEPT when
AI_PASSIVEis also set (i.e. when we’re trying to get an address to bind for listener socket - see below)
- In calls made to
rsock_getaddrinfoas a direct result of calling
Addrinfo.getaddrinfofrom Ruby with nil flags
- EXCEPT calls to
Addrinfo.getaddrinfowhere explicit flags are provided
Both of these seem like something you would almost always want to be doing in any outgoing connection scenario:
AI_V4MAPPEDensures that, if AF_INET6 is explicitly specified as the desired protocol, and there is no AAAA record in DNS, that any A record that is present gets converted to an IPv4-mapped IPv6 address so it can be used e.g. with NAT64.
AI_ADDRCONFIGensures that, if a machine has no IPv6 address, it doesn’t bother making an AAAA lookup that will return IPv6 addresses that can’t actually be used for anything (and vice versa for IPv4).
The reason why we wouldn’t want to set
AI_ADDRCONFIG in circumstances where Ruby currently sets
AI_PASSIVE is that loopback addresses are not considered in deciding if a system has an IPv4/IPv6 address. Conceivably, you might want to bind to a
::1 loopback address, and allow other processes on the same machine to connect to that.
Does changing this default sound reasonable? If so I can prepare a patch. Another option I considered is doing this only when Ruby is built against glibc (so that other system behaviour is most closely matched).