[PATCH v5 1/2] resolv: implement ipv4+ipv6 flags in resolv.conf (bug 30544)
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Wed Oct 15 18:29:58 GMT 2025
On 09/10/25 11:34, Petr Menšík wrote:
> Add separate flags to mark support of address family. Its intention is
> to react dynamically to changing network conditions. If the host has
> IPv4 connectivity only, it would set ipv4 option. If it has also IPv6
> connectivity, it would set ipv6 option. If it connects IPv6-only
> network, then it set ipv6 only.
>
> For backward compatibility it will keep old behaviour when both ipv4 and
> ipv6 flags are missing. In this case it behaves the same way as if both
> were present.
>
> This is quite useful when default AF_UNSPEC is used in hints, but the
> host is on network with just single AF used.
>
> Unlike no-aaaa option it would change just AF_UNSPEC behaviour, ie.
> getent ahosts example.com. getent ahostsv4 and getent ahostsv6 would
> remain unmodified.
>
> Signed-off-by: Petr Menšík <pemensik@redhat.com>
>> The name of new options is for a discussion. I like ipv4 and ipv6 as more beginner friendly. It might make sense to rename ipv4 to "a" and ipv6 to "aaaa" instead. But it would be a bit confusing that aaaa is not just negative of no-aaaa option, but something slightly different.
>>
I don't have a strong preference, but I tend to prefer 'ipv4'/'ipv6'
instead of the historical 'a'/'aaaa'.
>> Also, it might help adding supporting AI_ALL flag to get all addresses, just like ipv4 and ipv6 options were specified, but per request. Reverting to current behaviour regarless of flags.
Would it be the same as the current behavior (without flags)? Not sure
if this is an improvement.
>>
>> Or adding similar new flag AI_ALLAF instead? Allowing to ignore those flags per-request only? What do you think about this change itself?
It is not clear to me how different it would be from AI_ALL.
The patch is sound, although I don't have much experience in this area.
> ---
> resolv/nss_dns/dns-host.c | 50 +++++++++++++++++----------
> resolv/res_debug.c | 2 ++
> resolv/res_init.c | 2 ++
> resolv/resolv.h | 2 ++
> resolv/tst-resolv-res_init-skeleton.c | 2 ++
> 5 files changed, 40 insertions(+), 18 deletions(-)
>
> diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
> index 14da73ee1d..99e8e5ce4c 100644
> --- a/resolv/nss_dns/dns-host.c
> +++ b/resolv/nss_dns/dns-host.c
> @@ -379,6 +379,7 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
> {
> enum nss_status status = check_name (name, herrnop);
> char tmp[NS_MAXDNAME];
> +
> if (status != NSS_STATUS_SUCCESS)
> return status;
> struct resolv_context *ctx = __resolv_context_get ();
> @@ -412,26 +413,39 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
>
> int olderr = errno;
> int n;
> + int res_options = ctx->resp->options & (RES_NOAAAA|RES_IPV4|RES_IPV6);
The 'struct __res_state' option is an unsigned long, using int might be
problem with flags that exceeds INT_MAX.
> + int qtype;
>
> - if ((ctx->resp->options & RES_NOAAAA) == 0)
> + switch (res_options)
> {
> - n = __res_context_search (ctx, name, C_IN, T_QUERY_A_AND_AAAA,
> - dns_packet_buffer, sizeof (dns_packet_buffer),
> - &alt_dns_packet_buffer, &ans2p, &nans2p,
> - &resplen2, &ans2p_malloced);
> - if (n >= 0)
> - status = gaih_getanswer (alt_dns_packet_buffer, n, ans2p, resplen2,
> - &abuf, pat, errnop, herrnop, ttlp);
> - }
> - else
> - {
> - n = __res_context_search (ctx, name, C_IN, T_A,
> - dns_packet_buffer, sizeof (dns_packet_buffer),
> - &alt_dns_packet_buffer, NULL, NULL, NULL, NULL);
> - if (n >= 0)
> - status = gaih_getanswer_noaaaa (alt_dns_packet_buffer, n,
> - &abuf, pat, errnop, herrnop, ttlp);
> - }
> + case RES_IPV4:
> + case RES_IPV4|RES_NOAAAA:
> + case RES_NOAAAA:
> + case RES_IPV6|RES_NOAAAA: /*< this combination should never be used. */
Double space after period.
> + case RES_IPV4|RES_IPV6|RES_NOAAAA: /*< this does not make sense. */
> + case RES_IPV6: /*< oh we want AAAA, but not A here. code is the same. */
> + qtype = (res_options == RES_IPV6 ? T_AAAA : T_A);
> + n = __res_context_search (ctx, name, C_IN, qtype,
> + dns_packet_buffer, sizeof (dns_packet_buffer),
> + &alt_dns_packet_buffer, NULL, NULL, NULL, NULL);
> + if (n >= 0)
> + status = gaih_getanswer_noaaaa (alt_dns_packet_buffer, n,
> + &abuf, pat, errnop, herrnop, ttlp);
> + break;
> +
> + case 0:
> + case RES_IPV4|RES_IPV6:
> + default:
> + n = __res_context_search (ctx, name, C_IN, T_QUERY_A_AND_AAAA,
> + dns_packet_buffer, sizeof (dns_packet_buffer),
> + &alt_dns_packet_buffer, &ans2p, &nans2p,
> + &resplen2, &ans2p_malloced);
> + if (n >= 0)
> + status = gaih_getanswer (alt_dns_packet_buffer, n, ans2p, resplen2,
> + &abuf, pat, errnop, herrnop, ttlp);
> + break;
> + }
> +
> if (n < 0)
> {
> switch (errno)
> diff --git a/resolv/res_debug.c b/resolv/res_debug.c
> index 73af0c72fe..f73bccd7bd 100644
> --- a/resolv/res_debug.c
> +++ b/resolv/res_debug.c
> @@ -613,6 +613,8 @@ p_option(u_long option) {
> case RES_NORELOAD: return "no-reload";
> case RES_TRUSTAD: return "trust-ad";
> case RES_NOAAAA: return "no-aaaa";
> + case RES_IPV4: return "ipv4";
> + case RES_IPV6: return "ipv6";
> /* XXX nonreentrant */
> default: sprintf(nbuf, "?0x%lx?", (u_long)option);
> return (nbuf);
> diff --git a/resolv/res_init.c b/resolv/res_init.c
> index aed1902ec5..2e898d3fb3 100644
> --- a/resolv/res_init.c
> +++ b/resolv/res_init.c
> @@ -696,6 +696,8 @@ res_setoptions (struct resolv_conf_parser *parser, const char *options)
> { STRnLEN ("trust-ad"), RES_TRUSTAD },
> { STRnLEN ("no-aaaa"), RES_NOAAAA },
> { STRnLEN ("strict-error"), RES_STRICTERR },
> + { STRnLEN ("ipv4"), RES_IPV4 },
> + { STRnLEN ("ipv6"), RES_IPV6 },
> };
> #define noptions (sizeof (options) / sizeof (options[0]))
> bool negate_option = *cp == '-';
> diff --git a/resolv/resolv.h b/resolv/resolv.h
> index b8a0f66a5f..647881243d 100644
> --- a/resolv/resolv.h
> +++ b/resolv/resolv.h
> @@ -134,6 +134,8 @@ struct res_sym {
> #define RES_TRUSTAD 0x04000000 /* Request AD bit, keep it in responses. */
> #define RES_NOAAAA 0x08000000 /* Suppress AAAA queries. */
> #define RES_STRICTERR 0x10000000 /* Report more DNS errors as errors. */
> +#define RES_IPV4 0x20000000 /* Query A records on PF_UNSPEC hints. */
> +#define RES_IPV6 0x40000000 /* Query AAAA records on PF_UNSPEC hints. */
Double space after period.
>
> #define RES_DEFAULT (RES_RECURSE|RES_DEFNAMES|RES_DNSRCH)
>
> diff --git a/resolv/tst-resolv-res_init-skeleton.c b/resolv/tst-resolv-res_init-skeleton.c
> index 3ccbe71db9..3d99f4b5e5 100644
> --- a/resolv/tst-resolv-res_init-skeleton.c
> +++ b/resolv/tst-resolv-res_init-skeleton.c
> @@ -130,6 +130,8 @@ print_resp (FILE *fp, res_state resp)
> print_option_flag (fp, &options, RES_TRUSTAD, "trust-ad");
> print_option_flag (fp, &options, RES_NOAAAA, "no-aaaa");
> print_option_flag (fp, &options, RES_STRICTERR, "strict-error");
> + print_option_flag (fp, &options, RES_IPV4, "ipv4");
> + print_option_flag (fp, &options, RES_IPV6, "ipv6");
> fputc ('\n', fp);
> if (options != 0)
> fprintf (fp, "; error: unresolved option bits: 0x%x\n", options);
More information about the Libc-alpha
mailing list