AresDns: close c-ares gaps (socket function, IPv6 servers, ReturnAllResults)
Built with c-ares (WITH_C_ARES, the Linux default), AresDns lacked some
things it does with the bundled ares:
- The application's AfterSocketCreationFuncPtr (typically used to set
QoS/DSCP) was never called for the resolver's sockets. It is now
called as for SIP TCP connections: before each socket connects
(ares_set_socket_configure_callback(), so even a TCP SYN is marked),
and again once it is connected if post-connect calls are enabled
(ares_set_socket_callback()), for UDP and TCP sockets alike, with the
real transport type.
- Extra name servers were passed as IPv4 in_addr only; an IPv6 one left
an uninitialised entry, so c-ares queried a garbage address. They are
now set with ares_set_servers_ports_csv(), IPv4 and IPv6 alike, and a
non-zero port is honoured (0 still means 53, which is what repro
passes).
- checkDnsChange() compared IPv4 addresses only. It now compares the
full server lists, so a changed IPv6 server or port is noticed; init()
logs IPv6 servers too. The list comes from ares_get_servers_csv() on
c-ares 1.24 and later, which deprecated ares_get_servers_ports(), and
from ares_get_servers_ports() before that.
- ExternalDns::ReturnAllResults set ARES_FLAG_NOCHECKRESP only for the
bundled ares. c-ares has the same flag, so DnsStub now sees a refused
or failed answer instead of c-ares quietly trying the next server.
c-ares 1.11 or later is now required (ares_set_servers_ports_csv and
ares_set_socket_configure_callback). None of the new calls is
deprecated in current c-ares (see #445).
Connection::setEnablePostConnectSocketFuncCall() now also sets
ExternalDns::setEnablePostConnectSocketFuncCall(), so the one switch
covers DNS sockets as well as SIP TCP connections; the SipStack docs
say how DNS sockets are handled. The bundled ares called the socket
function for its UDP sockets only; it now calls it for TCP sockets too,
likewise once, after connecting.
A build without USE_IPV6 does no IPv6, for DNS as well as SIP. With
c-ares, IPv6 name servers are now dropped, both extra ones (with the
warning the bundled ares already logs) and ones c-ares reads from the
system configuration (falling back to 127.0.0.1 if none are left), as
the bundled ares does. The bundled ares's own IPv4-only path counted an
ignored IPv6 extra name server in nservers but left its in_addr
uninitialised, so it queried a garbage address; it now passes only the
IPv4 servers, and the system's own if there are none.
New rutil/test/testAresDns runs without a network: the socket function
is called (with c-ares before connecting, again once connected when
enabled, and for a TCP socket after a truncated answer), a ::1 name
server is used, checkDnsChange() spots IPv4,
IPv6 and port changes (and, without USE_IPV6, ignores IPv6 servers),
and with c-ares, ReturnAllResults returns a fake server's REFUSED where
c-ares would otherwise skip to a second fake server's answer. Against
the previous code every c-ares check fails.
Tested on Ubuntu 22.04, gcc 11, -Werror, with c-ares 1.18.1 and with
the bundled ares: testAresDns and testDnsPollGrp pass with both, and
the full ctest suite passes with c-ares. Both tests also pass against
c-ares 1.34.5, where the build shows only the three deprecation
warnings #445 reports. With USE_IPV6 off, rutil and both tests build
and pass with c-ares 1.18, c-ares 1.34.5 and the bundled ares, and with
a resolv.conf listing ::1 and 127.0.0.1 (or ::1 alone), all three use
127.0.0.1 only. With a name server on 127.0.0.1:53 that answers
truncated (in a private network namespace), the bundled ares now calls
the socket function for its TCP socket as well as the UDP one.