[PATCH v2] resolv: Avoid duplicate query if search list contains '.' (bug 33804)

Florian Weimer fweimer@redhat.com
Tue Mar 3 17:50:08 GMT 2026


From: Carlos Peón Costa <carlospeon@gmail.com>

Co-authored-by: Florian Weimer <fweimer@redhat.com>
Signed-off-by: Florian Weimer <fweimer@redhat.com>

---
v2: Use _Atomic unsigned int for the query counter.  Add a comment.

 resolv/res_query.c            | 11 +++++++----
 resolv/tst-resolv-no-search.c | 12 ++++++++++++
 2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/resolv/res_query.c b/resolv/res_query.c
index 039c25a3c3..30ace9d06d 100644
--- a/resolv/res_query.c
+++ b/resolv/res_query.c
@@ -354,7 +354,7 @@ __res_context_search (struct resolv_context *ctx,
 	char tmp[NS_MAXDNAME];
 	u_int dots;
 	int trailing_dot, ret, saved_herrno;
-	int got_nodata = 0, got_servfail = 0, root_on_list = 0;
+	int got_nodata = 0, got_servfail = 0;
 	int tried_as_is = 0;
 	int searched = 0;
 
@@ -433,8 +433,11 @@ __res_context_search (struct resolv_context *ctx,
 			   domain.  */
 			if (dname[0] == '.')
 				dname++;
-			if (dname[0] == '\0')
-				root_on_list++;
+			if (dname[0] == '\0') {
+				if (tried_as_is)
+					continue;
+				tried_as_is++;
+			}
 
 			ret = __res_context_querydomain
 			  (ctx, name, dname, class, type,
@@ -506,7 +509,7 @@ __res_context_search (struct resolv_context *ctx,
 	 * unless RES_NOTLDQUERY is set and there were no dots.
 	 */
 	if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0)
-	    && !(tried_as_is || root_on_list)) {
+	    && !tried_as_is) {
 		ret = __res_context_querydomain
 		  (ctx, name, NULL, class, type,
 		   answer, anslen, answerp, answerp2, nanswerp2,
diff --git a/resolv/tst-resolv-no-search.c b/resolv/tst-resolv-no-search.c
index 29701d4772..7d78d4044c 100644
--- a/resolv/tst-resolv-no-search.c
+++ b/resolv/tst-resolv-no-search.c
@@ -27,6 +27,11 @@
 #include <support/resolv_test.h>
 #include <support/support.h>
 
+/* Used to check for duplicated queries (bug 33804).  POSIX does not
+   explicitly say that socket calls (as used in the resolver tests)
+   provide synchronization.  */
+static _Atomic unsigned int query_count;
+
 /* Check that plain res_init loads the configuration as expected.  */
 static void
 test_res_init (void *ignored)
@@ -43,6 +48,7 @@ response (const struct resolv_response_context *ctx,
 {
   TEST_VERIFY_EXIT (qclass == C_IN);
   TEST_COMPARE (ctx->server_index, 0);
+  ++query_count;
 
   if (strncmp (qname, "does-not-exist", strlen ("does-not-exist")) == 0)
     {
@@ -82,12 +88,16 @@ check_h (const char *name, int family, const char *expected)
   if (family == AF_INET)
     {
       char *query = xasprintf ("gethostbyname (\"%s\")", name);
+      query_count = 0;
       check_hostent (query, gethostbyname (name), expected);
+      TEST_COMPARE (query_count, 1);
       free (query);
     }
   {
     char *query = xasprintf ("gethostbyname2 (\"%s\", %d)", name, family);
+    query_count = 0;
     check_hostent (query, gethostbyname2 (name, family), expected);
+    TEST_COMPARE (query_count, 1);
     free (query);
   }
 }
@@ -98,8 +108,10 @@ check_ai (const char *name, int family, const char *expected)
   struct addrinfo hints = { .ai_family = family, .ai_socktype = SOCK_STREAM, };
   struct addrinfo *ai;
   char *query = xasprintf ("%s:80 [%d]", name, hints.ai_family);
+  query_count = 0;
   int ret = getaddrinfo (name, "80", &hints, &ai);
   check_addrinfo (query, ai, ret, expected);
+  TEST_COMPARE (query_count, family == AF_UNSPEC ? 2 : 1);
   if (ret == 0)
     freeaddrinfo (ai);
   free (query);

base-commit: c995686e2cbe2a3ab2a11877a61c14a2e1fc35cb



More information about the Libc-alpha mailing list