]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
nscd: Pass TRY_AGAIN errors in the hosts cache to clients
authorFlorian Weimer <fweimer@redhat.com>
Thu, 12 Feb 2026 11:18:54 +0000 (12:18 +0100)
committerFlorian Weimer <fweimer@redhat.com>
Thu, 12 Feb 2026 11:18:54 +0000 (12:18 +0100)
This is the minimal set of changes to get the upcoming test to pass.
The TTL extension logic is somewhat iffy.  There is a trade-off here:
correct operation under the DNS specification (no TTL extensions), or
reducing loading the infrastructure if TRY_AGAIN is the result of
overload from this client.

Reviewed-by: DJ Delorie <dj@redhat.com>
nscd/aicache.c
nscd/hstcache.c
nss/getaddrinfo.c

index 91ec09af7bca1190d3c34f92f0d7d58d1d75c675..ef15c373f589b8b29818c3973d4a8d0ea11e7323 100644 (file)
@@ -42,6 +42,15 @@ static const ai_response_header notfound =
   .error = 0
 };
 
+static const ai_response_header tryagain =
+{
+  .version = NSCD_VERSION,
+  .found = 0,
+  .naddrs = 0,
+  .addrslen = 0,
+  .canonlen = 0,
+  .error = TRY_AGAIN
+};
 
 static time_t
 addhstaiX (struct database_dyn *db, int fd, request_header *req,
@@ -440,7 +449,7 @@ next_nip:
     }
 
   /* No result found.  Create a negative result record.  */
-  if (he != NULL && rc4 == EAGAIN)
+  if (he != NULL && herrno == TRY_AGAIN)
     {
       /* If we have an old record available but cannot find one now
         because the service is not available we keep the old record
@@ -454,16 +463,21 @@ next_nip:
     }
   else
     {
-      /* We have no data.  This means we send the standard reply for
-        this case.  */
+      /* We have no data.  This means we send the standard reply
+        for this case.  Possibly this is only temporary due to
+        networking errors or memory allocation failures.  */
       total = sizeof (notfound);
+      assert (sizeof (notfound) == sizeof (tryagain));
+
+      const ai_response_header *resp = (herrno == TRY_AGAIN
+                                       ? &tryagain : &notfound);
 
       if (fd != -1)
-       TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
+       TEMP_FAILURE_RETRY (send (fd, resp, total, MSG_NOSIGNAL));
 
       /* If we have a transient error or cannot permanently store the
         result, so be it.  */
-      if (rc4 == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
+      if (herrno == TRY_AGAIN || __builtin_expect (db->negtimeout == 0, 0))
        {
          /* Mark the old entry as obsolete.  */
          if (dh != NULL)
@@ -478,7 +492,7 @@ next_nip:
                                       total, db->negtimeout);
 
          /* This is the reply.  */
-         memcpy (&dataset->resp, &notfound, total);
+         memcpy (&dataset->resp, resp, total);
 
          /* Copy the key data.  */
          key_copy = memcpy (dataset->strdata, key, req->key_len);
index e9272000a3266280edefaf51e641f044ed112195..78485fbac3556d5e55c351ea365d8a2b009d4452 100644 (file)
@@ -125,8 +125,9 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
        }
       else
        {
-         /* We have no data.  This means we send the standard reply for this
-            case.  Possibly this is only temporary.  */
+         /* We have no data.  This means we send the standard reply
+            for this case.  Possibly this is only temporary due to
+            networking errors or memory allocation failures.  */
          ssize_t total = sizeof (notfound);
          assert (sizeof (notfound) == sizeof (tryagain));
 
@@ -434,7 +435,6 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
      pruning function only will look at the timestamp.  */
   struct hostent resultbuf;
   struct hostent *hst;
-  int errval = 0;
   int32_t ttl = INT32_MAX;
 
   if (__glibc_unlikely (debug_level > 0))
@@ -459,7 +459,7 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
   while (lookup (req->type, key, &resultbuf,
                 tmpbuf.data, tmpbuf.length, &hst, &ttl) != 0
         && h_errno == NETDB_INTERNAL
-        && (errval = errno) == ERANGE)
+        && errno == ERANGE)
     if (!scratch_buffer_grow (&tmpbuf))
       {
        /* We ran out of memory.  We cannot do anything but sending a
@@ -470,12 +470,15 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
           error and that it does not mean the entry is not
           available at all.  */
        h_errno = TRY_AGAIN;
-       errval = EAGAIN;
        break;
       }
 
+  /* On out-of-memory or without a network connection, pass EAGAIN as
+     an error code.  This will be translated back to TRY_AGAIN in the
+     client.  Other errors with hst == NULL and error code 0 are
+     treated as HOST_NOT_FOUND.  */
   time_t timeout = cache_addhst (db, fd, req, key, hst, uid, he, dh,
-                                h_errno == TRY_AGAIN ? errval : 0, ttl);
+                                h_errno == TRY_AGAIN ? EAGAIN : 0, ttl);
   scratch_buffer_free (&tmpbuf);
   return timeout;
 }
index c0f496f96c752220e68bf0257d9ff1ffc624ebe6..4f6ac3358a90df35be8078fef7e1940967dcdd2b 100644 (file)
@@ -502,7 +502,12 @@ get_nscd_addresses (const char *name, const struct addrinfo *req,
     {
       /* The database contains a negative entry.  */
       if (err == 0)
-       return -EAI_NONAME;
+       {
+         /* h_errno contains the error from nscd.  */
+         if (h_errno == TRY_AGAIN)
+           return -EAI_AGAIN;
+         return -EAI_NONAME;
+       }
       if (__nss_not_use_nscd_hosts == 0)
        {
          if (h_errno == NETDB_INTERNAL && errno == ENOMEM)