Skip to content

dns: literals for any family, c-ares error text, link-local nameservers - #4

Open
david-yu wants to merge 3 commits into
ipv6/02-dns-ipv6-nameserversfrom
ipv6/03-dns-literals-errors
Open

david-yu wants to merge 3 commits into
ipv6/02-dns-ipv6-nameserversfrom
ipv6/03-dns-literals-errors

Conversation

@david-yu

@david-yu david-yu commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Part 3 of 10 of the IPv6 series, split out of #1 and rebased on current scylladb/seastar master. Based on #3, so the diff here is only this PR's 3 commits.

Why

Three more resolver gaps, which show up once an IPv6 nameserver is reachable:

  • A numeric literal with a requested family went to c-ares, which cannot parse a zone (fe80::1%eth0 became a DNS query that failed with ARES_ENOTFOUND) and ignores the family for literals. The literal shortcut only ran for an unspecified family, the case b48ec904a made return whichever family answers first.
  • The error category's hand-written copy of c-ares' table stopped at ARES_ECANCELLED, so ARES_ENOSERVER ("No DNS servers were configured") printed as "Unknown error". Those codes now get c-ares' own text; the texts already in the table stay, because applications and their tests match on them.
  • aif_nametoindex/aif_indextoname were null, so c-ares dropped a link-local nameserver (nameserver fe80::1%eth0) whose zone it could not resolve.

What

  • dns: resolve numeric literals without c-ares, for any requested family (fdcbe43b2)
  • dns: fall back to c-ares' error text and wire the interface-name callbacks (23f154aa8)
  • tests: cover the IPv6 side of the resolver (dbfe0849a) — AAAA answers, ip6.arpa PTR, TCP transport to [::1], literals per family; the mock nameserver now answers by QTYPE.

Behaviour change

before after
IPv4 literal resolved with family INET6 an AF_INET answer, ignoring the family ARES_EBADFAMILY

Testing

test_error_category_texts pins the table's texts that applications match on (Not found, Connection refused) and checks that a code the table predates no longer renders as "Unknown error". An earlier version of this PR switched every code to ares_strerror; Redpanda's full ducktape run then failed four crash-loop tests whose log allow-lists expect C-Ares:4, unreachable_host.com: Not found.

On the core-aws VM (Ubuntu 24.04, GCC 14, C++23, RelWithDebInfo, GnuTLS and OpenSSL in one tree), this PR's head builds and passes dns, ipv6, socket, rpc, httpd, httpd_openssl, tls, tls_openssl, network_interface, websocket and unix_domain with SEASTAR_TEST_REQUIRE_IPV6=1, so none of their IPv6 cases skipped.

Stack

  1. tests, ci: fail instead of skip when IPv6 is expected #2 tests, ci: fail instead of skip when IPv6 is expected
  2. dns: reach nameservers over IPv6 #3 dns: reach nameservers over IPv6
  3. dns: literals for any family, c-ares error text, link-local nameservers #4 dns: literals for any family, c-ares error text, link-local nameservers ← this PR
  4. net, http, rpc: dual-stack listeners (IPV6_V6ONLY, IPv4-mapped peers) #5 net, http, rpc: dual-stack listeners (IPV6_V6ONLY, IPv4-mapped peers)
  5. net: IPv6 zones and strict literal parsing #6 net: IPv6 zones and strict literal parsing
  6. tls: do not send IP literals in the SNI extension #7 tls: do not send IP literals in the SNI extension
  7. tls: verify server_name on OpenSSL, opt-in #8 tls: verify server_name on OpenSSL, opt-in
  8. net: ipv6_addr equality and hash, ipv4_addr(socket_address) throws instead of terminating #9 net: ipv6_addr equality and hash, ipv4_addr(socket_address) throws instead of terminating
  9. net: listen on an unspecified address binds any family #10 net: listen on an unspecified address binds any family
  10. http: build the Host header from an address in authority form #11 http: build the Host header from an address in authority form

get_host_by_name short-circuited numeric literals only when no address
family was requested. With a family set the literal went to c-ares,
which cannot parse a scope id ("fe80::1%eth0" turned into a real DNS
query that failed with ARES_ENOTFOUND) and which returns an IPv4 node
for an AF_INET6 request, so the family was not a filter for literals.

Parse literals with inet_address::parse_numerical regardless of the
requested family. A literal of the other family fails with
ARES_EBADFAMILY, matching what the resolver does for names that only
have records of the other family.
…backs

The error category's hand-written copy of c-ares' error table stops at
ARES_ECANCELLED, so ARES_ESERVICE and ARES_ENOSERVER ("No DNS servers
were configured", the result of every configured server being unusable)
rendered as "Unknown error". Codes the table does not list now get
ares_strerror's text. The listed texts are unchanged, since
applications and their tests match on them; a new test pins them.

ares_socket_functions_ex had aif_nametoindex/aif_indextoname set to
null. c-ares uses them to resolve the %iface suffix of link-local
nameservers and silently drops such servers without them, so a
resolv.conf of "nameserver fe80::1%eth0" lost its only server.
The mock nameserver only ever answered A records, so make_hostent's
AF_INET6 branches, reverse lookups of 16-byte addresses, the TCP query
path over an IPv6 transport and numeric-literal handling with an
explicit family had no coverage. Drive the mock by the question type
(A, AAAA, PTR) and add:

- test_resolve_aaaa_from_ipv6_nameserver
- test_reverse_lookup_ipv6_from_ipv6_nameserver (ip6.arpa PTR)
- test_resolve_tcp_ipv6_nameserver
- test_resolve_numeric_with_family (literals never reach c-ares; a
  literal of the other family fails; a %scope suffix is preserved)

All IPv6 cases gate on ipv6_available_or_skip().
@david-yu
david-yu force-pushed the ipv6/03-dns-literals-errors branch from af52f44 to dbfe084 Compare September 25, 2026 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant