[v3] resolv.conf: implement ipv4+ipv6 flags (bug 30544)

Message ID 2781e81e-28b7-48c7-83db-08e6ab822fb0@redhat.com
State New
Headers
Series [v3] resolv.conf: implement ipv4+ipv6 flags (bug 30544) |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
redhat-pt-bot/TryBot-32bit fail Patch series failed to apply
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm fail Test failed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 fail Test failed

Commit Message

Petr Menšík Nov. 25, 2024, 6:08 p.m. UTC
  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>
  

Patch

From f85dc2aa9502b25f93b49d12334860c9d81475e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
Date: Mon, 3 Jul 2023 08:32:20 +0200
Subject: [PATCH] resolv.conf: implement ipv4+ipv6 flags (bug 30544)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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>
---
 resolv/nss_dns/dns-host.c             | 55 ++++++++++++++++++---------
 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, 45 insertions(+), 18 deletions(-)

diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index 95a7b3f0e5..901bf1c3b5 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 ();
@@ -413,25 +414,43 @@  _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
   int olderr = errno;
   int n;
 
-  if ((ctx->resp->options & RES_NOAAAA) == 0)
-    {
-      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
+  switch (ctx->resp->options & (RES_NOAAAA|RES_IPV4|RES_IPV6))
     {
-      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. */
+      case RES_IPV4|RES_IPV6|RES_NOAAAA: /*< this does not make sense. */
+	n = __res_context_search (ctx, name, C_IN, T_A,
+				  dns_packet_buffer, sizeof (dns_packet_buffer),
+				  NULL, NULL, NULL, NULL, NULL);
+	if (n >= 0)
+	  status = gaih_getanswer_noaaaa (alt_dns_packet_buffer, n,
+					  &abuf, pat, errnop, herrnop, ttlp);
+	break;
+
+      case RES_IPV6:
+	n = __res_context_search (ctx, name, C_IN, T_AAAA,
+				  dns_packet_buffer, sizeof (dns_packet_buffer),
+				  NULL, NULL, NULL, NULL, NULL);
+	if (n >= 0) /* oh we want AAAA, but not A here. code is the same. */
+	  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 1b1f5233f9..c271b60dec 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 b838dc7064..38af14bbb3 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. */
 
 #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 e41bcebd9d..97eb57e2a4 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);
-- 
2.47.0