From: Florian Weimer Date: Thu, 12 Feb 2026 11:18:54 +0000 (+0100) Subject: nscd: Pass TRY_AGAIN errors in the hosts cache to clients X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=68fe46a2ca55938a6c8fb3be54ef8982c11a2e00;p=thirdparty%2Fglibc.git nscd: Pass TRY_AGAIN errors in the hosts cache to clients 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 --- diff --git a/nscd/aicache.c b/nscd/aicache.c index 91ec09af7b..ef15c373f5 100644 --- a/nscd/aicache.c +++ b/nscd/aicache.c @@ -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 : ¬found); if (fd != -1) - TEMP_FAILURE_RETRY (send (fd, ¬found, 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, ¬found, total); + memcpy (&dataset->resp, resp, total); /* Copy the key data. */ key_copy = memcpy (dataset->strdata, key, req->key_len); diff --git a/nscd/hstcache.c b/nscd/hstcache.c index e9272000a3..78485fbac3 100644 --- a/nscd/hstcache.c +++ b/nscd/hstcache.c @@ -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; } diff --git a/nss/getaddrinfo.c b/nss/getaddrinfo.c index c0f496f96c..4f6ac3358a 100644 --- a/nss/getaddrinfo.c +++ b/nss/getaddrinfo.c @@ -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)