]> git.ipfire.org Git - thirdparty/glibc.git/blame - nscd/hstcache.c
nscd: Use time_t for return type of addgetnetgrentX
[thirdparty/glibc.git] / nscd / hstcache.c
CommitLineData
67479a70 1/* Cache handling for host lookup.
dff8da6b 2 Copyright (C) 1998-2024 Free Software Foundation, Inc.
67479a70 3 This file is part of the GNU C Library.
67479a70 4
43bc8ac6 5 This program is free software; you can redistribute it and/or modify
2e2efe65
RM
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; version 2 of the License, or
8 (at your option) any later version.
67479a70 9
43bc8ac6 10 This program is distributed in the hope that it will be useful,
67479a70 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
43bc8ac6
UD
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
67479a70 14
43bc8ac6 15 You should have received a copy of the GNU General Public License
5a82c748 16 along with this program; if not, see <https://www.gnu.org/licenses/>. */
67479a70 17
9caf4f1c 18#include <alloca.h>
67479a70
UD
19#include <assert.h>
20#include <errno.h>
21#include <error.h>
a95a08b4 22#include <libintl.h>
67479a70 23#include <netdb.h>
c7a9b6e2 24#include <stdbool.h>
67479a70
UD
25#include <stddef.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <time.h>
30#include <unistd.h>
e054f494 31#include <stdint.h>
67479a70 32#include <arpa/inet.h>
9e56c380 33#include <arpa/nameser.h>
a95a08b4 34#include <sys/mman.h>
c7a9b6e2 35#include <stackinfo.h>
2f9f0d18 36#include <scratch_buffer.h>
67479a70
UD
37
38#include "nscd.h"
39#include "dbg_log.h"
40
67479a70
UD
41
42/* This is the standard reply in case the service is disabled. */
43static const hst_response_header disabled =
44{
c2e13112
RM
45 .version = NSCD_VERSION,
46 .found = -1,
47 .h_name_len = 0,
48 .h_aliases_cnt = 0,
49 .h_addrtype = -1,
50 .h_length = -1,
51 .h_addr_list_cnt = 0,
52 .error = NETDB_INTERNAL
67479a70
UD
53};
54
55/* This is the struct describing how to write this record. */
56const struct iovec hst_iov_disabled =
57{
c2e13112
RM
58 .iov_base = (void *) &disabled,
59 .iov_len = sizeof (disabled)
67479a70
UD
60};
61
62
63/* This is the standard reply in case we haven't found the dataset. */
64static const hst_response_header notfound =
65{
c2e13112
RM
66 .version = NSCD_VERSION,
67 .found = 0,
68 .h_name_len = 0,
69 .h_aliases_cnt = 0,
70 .h_addrtype = -1,
71 .h_length = -1,
72 .h_addr_list_cnt = 0,
73 .error = HOST_NOT_FOUND
67479a70
UD
74};
75
67479a70 76
bc425b33
UD
77/* This is the standard reply in case there are temporary problems. */
78static const hst_response_header tryagain =
79{
80 .version = NSCD_VERSION,
81 .found = 0,
82 .h_name_len = 0,
83 .h_aliases_cnt = 0,
84 .h_addrtype = -1,
85 .h_length = -1,
86 .h_addr_list_cnt = 0,
87 .error = TRY_AGAIN
88};
89
90
a4c7ea7b 91static time_t
a95a08b4 92cache_addhst (struct database_dyn *db, int fd, request_header *req,
c207f23b 93 const void *key, struct hostent *hst, uid_t owner,
20e498bd 94 struct hashentry *const he, struct datahead *dh, int errval,
384ca551 95 int32_t ttl)
67479a70 96{
9ad58cc3 97 bool all_written = true;
67479a70
UD
98 time_t t = time (NULL);
99
a95a08b4
UD
100 /* We allocate all data in one memory block: the iov vector,
101 the response header and the dataset itself. */
102 struct dataset
103 {
104 struct datahead head;
105 hst_response_header resp;
106 char strdata[0];
107 } *dataset;
108
109 assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
110
a4c7ea7b 111 time_t timeout = MAX_TIMEOUT_VALUE;
67479a70
UD
112 if (hst == NULL)
113 {
a95a08b4
UD
114 if (he != NULL && errval == EAGAIN)
115 {
116 /* If we have an old record available but cannot find one
117 now because the service is not available we keep the old
118 record and make sure it does not get removed. */
119 if (reload_count != UINT_MAX)
120 /* Do not reset the value if we never not reload the record. */
121 dh->nreloads = reload_count - 1;
a4c7ea7b
UD
122
123 /* Reload with the same time-to-live value. */
124 timeout = dh->timeout = t + dh->ttl;
a95a08b4
UD
125 }
126 else
127 {
128 /* We have no data. This means we send the standard reply for this
bc425b33 129 case. Possibly this is only temporary. */
9ad58cc3 130 ssize_t total = sizeof (notfound);
bc425b33
UD
131 assert (sizeof (notfound) == sizeof (tryagain));
132
133 const hst_response_header *resp = (errval == EAGAIN
134 ? &tryagain : &notfound);
67479a70 135
34a5a146
JM
136 if (fd != -1
137 && TEMP_FAILURE_RETRY (send (fd, resp, total,
138 MSG_NOSIGNAL)) != total)
9ad58cc3 139 all_written = false;
67479a70 140
3e1aa84e
UD
141 /* If we have a transient error or cannot permanently store
142 the result, so be it. */
143 if (errval == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
445b4a53
TK
144 {
145 /* Mark the old entry as obsolete. */
146 if (dh != NULL)
147 dh->usable = false;
148 }
99231d9a
UD
149 else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
150 + req->key_len), 1)) != NULL)
a95a08b4 151 {
1cdeb237
SP
152 timeout = datahead_init_neg (&dataset->head,
153 (sizeof (struct dataset)
154 + req->key_len), total,
155 (ttl == INT32_MAX
156 ? db->negtimeout : ttl));
67479a70 157
a95a08b4 158 /* This is the reply. */
bc425b33 159 memcpy (&dataset->resp, resp, total);
67479a70 160
a95a08b4
UD
161 /* Copy the key data. */
162 memcpy (dataset->strdata, key, req->key_len);
67479a70 163
8c89236f
UD
164 /* If necessary, we also propagate the data to disk. */
165 if (db->persistent)
166 {
167 // XXX async OK?
168 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
169 msync ((void *) pval,
170 ((uintptr_t) dataset & pagesize_m1)
171 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
172 }
173
7e71e55f 174 (void) cache_add (req->type, &dataset->strdata, req->key_len,
528741cb 175 &dataset->head, true, db, owner, he == NULL);
a95a08b4 176
00ebd7ed
UD
177 pthread_rwlock_unlock (&db->lock);
178
a95a08b4
UD
179 /* Mark the old entry as obsolete. */
180 if (dh != NULL)
181 dh->usable = false;
182 }
99bb9f42 183 }
67479a70
UD
184 }
185 else
186 {
187 /* Determine the I/O structure. */
67479a70
UD
188 size_t h_name_len = strlen (hst->h_name) + 1;
189 size_t h_aliases_cnt;
3107c0c5 190 uint32_t *h_aliases_len;
67479a70 191 size_t h_addr_list_cnt;
67479a70
UD
192 char *addresses;
193 char *aliases;
194 char *key_copy = NULL;
195 char *cp;
196 size_t cnt;
9ad58cc3 197 ssize_t total;
67479a70
UD
198
199 /* Determine the number of aliases. */
200 h_aliases_cnt = 0;
201 for (cnt = 0; hst->h_aliases[cnt] != NULL; ++cnt)
202 ++h_aliases_cnt;
203 /* Determine the length of all aliases. */
3107c0c5 204 h_aliases_len = (uint32_t *) alloca (h_aliases_cnt * sizeof (uint32_t));
67479a70
UD
205 total = 0;
206 for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
207 {
208 h_aliases_len[cnt] = strlen (hst->h_aliases[cnt]) + 1;
209 total += h_aliases_len[cnt];
210 }
211
212 /* Determine the number of addresses. */
213 h_addr_list_cnt = 0;
1ce7d80d 214 while (hst->h_addr_list[h_addr_list_cnt] != NULL)
67479a70
UD
215 ++h_addr_list_cnt;
216
a95a08b4
UD
217 if (h_addr_list_cnt == 0)
218 /* Invalid entry. */
a4c7ea7b 219 return MAX_TIMEOUT_VALUE;
a95a08b4
UD
220
221 total += (sizeof (struct dataset)
67479a70 222 + h_name_len
3107c0c5 223 + h_aliases_cnt * sizeof (uint32_t)
488fb3c7 224 + h_addr_list_cnt * hst->h_length);
67479a70 225
a95a08b4
UD
226 /* If we refill the cache, first assume the reconrd did not
227 change. Allocate memory on the cache since it is likely
228 discarded anyway. If it turns out to be necessary to have a
229 new record we can still allocate real memory. */
230 bool alloca_used = false;
231 dataset = NULL;
67479a70 232
a95a08b4
UD
233 /* If the record contains more than one IP address (used for
234 load balancing etc) don't cache the entry. This is something
235 the current cache handling cannot handle and it is more than
236 questionable whether it is worthwhile complicating the cache
237 handling just for handling such a special case. */
a30d41c1 238 if (he == NULL && h_addr_list_cnt == 1)
20e498bd
UD
239 dataset = (struct dataset *) mempool_alloc (db, total + req->key_len,
240 1);
67479a70 241
a95a08b4
UD
242 if (dataset == NULL)
243 {
244 /* We cannot permanently add the result in the moment. But
245 we can provide the result as is. Store the data in some
246 temporary memory. */
247 dataset = (struct dataset *) alloca (total + req->key_len);
248
249 /* We cannot add this record to the permanent database. */
250 alloca_used = true;
251 }
252
1cdeb237
SP
253 timeout = datahead_init_pos (&dataset->head, total + req->key_len,
254 total - offsetof (struct dataset, resp),
255 he == NULL ? 0 : dh->nreloads + 1,
256 ttl == INT32_MAX ? db->postimeout : ttl);
a95a08b4
UD
257
258 dataset->resp.version = NSCD_VERSION;
259 dataset->resp.found = 1;
260 dataset->resp.h_name_len = h_name_len;
261 dataset->resp.h_aliases_cnt = h_aliases_cnt;
262 dataset->resp.h_addrtype = hst->h_addrtype;
263 dataset->resp.h_length = hst->h_length;
264 dataset->resp.h_addr_list_cnt = h_addr_list_cnt;
265 dataset->resp.error = NETDB_SUCCESS;
266
9ad58cc3
UD
267 /* Make sure there is no gap. */
268 assert ((char *) (&dataset->resp.error + 1) == dataset->strdata);
269
a95a08b4 270 cp = dataset->strdata;
67479a70
UD
271
272 cp = mempcpy (cp, hst->h_name, h_name_len);
3107c0c5 273 cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (uint32_t));
67479a70
UD
274
275 /* The normal addresses first. */
276 addresses = cp;
277 for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
278 cp = mempcpy (cp, hst->h_addr_list[cnt], hst->h_length);
279
67479a70
UD
280 /* Then the aliases. */
281 aliases = cp;
282 for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
283 cp = mempcpy (cp, hst->h_aliases[cnt], h_aliases_len[cnt]);
284
a95a08b4
UD
285 assert (cp
286 == dataset->strdata + total - offsetof (struct dataset,
287 strdata));
67479a70
UD
288
289 /* If we are adding a GETHOSTBYNAME{,v6} entry we must be prepared
290 that the answer we get from the NSS does not contain the key
291 itself. This is the case if the resolver is used and the name
292 is extended by the domainnames from /etc/resolv.conf. Therefore
293 we explicitly add the name here. */
c207f23b 294 key_copy = memcpy (cp, key, req->key_len);
67479a70 295
9ad58cc3
UD
296 assert ((char *) &dataset->resp + dataset->head.recsize == cp);
297
a95a08b4
UD
298 /* Now we can determine whether on refill we have to create a new
299 record or not. */
300 if (he != NULL)
301 {
302 assert (fd == -1);
67479a70 303
a95a08b4
UD
304 if (total + req->key_len == dh->allocsize
305 && total - offsetof (struct dataset, resp) == dh->recsize
306 && memcmp (&dataset->resp, dh->data,
307 dh->allocsize - offsetof (struct dataset, resp)) == 0)
308 {
8c89236f 309 /* The data has not changed. We will just bump the
a95a08b4
UD
310 timeout value. Note that the new record has been
311 allocated on the stack and need not be freed. */
c44d3bdf 312 assert (h_addr_list_cnt == 1);
a4c7ea7b 313 dh->ttl = dataset->head.ttl;
a95a08b4
UD
314 dh->timeout = dataset->head.timeout;
315 ++dh->nreloads;
316 }
317 else
318 {
c44d3bdf 319 if (h_addr_list_cnt == 1)
a95a08b4 320 {
c44d3bdf
UD
321 /* We have to create a new record. Just allocate
322 appropriate memory and copy it. */
323 struct dataset *newp
324 = (struct dataset *) mempool_alloc (db,
c52137d3 325 total + req->key_len,
20e498bd 326 1);
c44d3bdf
UD
327 if (newp != NULL)
328 {
329 /* Adjust pointers into the memory block. */
330 addresses = (char *) newp + (addresses
331 - (char *) dataset);
332 aliases = (char *) newp + (aliases - (char *) dataset);
333 assert (key_copy != NULL);
334 key_copy = (char *) newp + (key_copy - (char *) dataset);
335
336 dataset = memcpy (newp, dataset, total + req->key_len);
337 alloca_used = false;
338 }
a95a08b4
UD
339 }
340
341 /* Mark the old record as obsolete. */
342 dh->usable = false;
343 }
344 }
345 else
490998a5 346 {
a95a08b4
UD
347 /* We write the dataset before inserting it to the database
348 since while inserting this thread might block and so would
349 unnecessarily keep the receiver waiting. */
350 assert (fd != -1);
351
8c78faa9
AZ
352 if (writeall (fd, &dataset->resp, dataset->head.recsize)
353 != dataset->head.recsize)
354 all_written = false;
490998a5
UD
355 }
356
a95a08b4
UD
357 /* Add the record to the database. But only if it has not been
358 stored on the stack.
67479a70 359
a95a08b4
UD
360 If the record contains more than one IP address (used for
361 load balancing etc) don't cache the entry. This is something
362 the current cache handling cannot handle and it is more than
363 questionable whether it is worthwhile complicating the cache
364 handling just for handling such a special case. */
365 if (! alloca_used)
67479a70 366 {
a95a08b4
UD
367 /* If necessary, we also propagate the data to disk. */
368 if (db->persistent)
3418007e
UD
369 {
370 // XXX async OK?
371 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
372 msync ((void *) pval,
373 ((uintptr_t) dataset & pagesize_m1)
374 + total + req->key_len, MS_ASYNC);
375 }
a95a08b4 376
a95a08b4
UD
377 /* NB: the following code is really complicated. It has
378 seemlingly duplicated code paths which do the same. The
379 problem is that we always must add the hash table entry
380 with the FIRST flag set first. Otherwise we get dangling
381 pointers in case memory allocation fails. */
c207f23b 382 assert (hst->h_addr_list[1] == NULL);
a95a08b4 383
a95a08b4
UD
384 /* Avoid adding names if more than one address is available. See
385 above for more info. */
c207f23b
UD
386 assert (req->type == GETHOSTBYNAME
387 || req->type == GETHOSTBYNAMEv6
388 || req->type == GETHOSTBYADDR
389 || req->type == GETHOSTBYADDRv6);
a95a08b4 390
7e71e55f 391 (void) cache_add (req->type, key_copy, req->key_len,
528741cb 392 &dataset->head, true, db, owner, he == NULL);
00ebd7ed
UD
393
394 pthread_rwlock_unlock (&db->lock);
67479a70 395 }
67479a70
UD
396 }
397
9ad58cc3 398 if (__builtin_expect (!all_written, 0) && debug_level > 0)
67479a70
UD
399 {
400 char buf[256];
401 dbg_log (_("short write in %s: %s"), __FUNCTION__,
402 strerror_r (errno, buf, sizeof (buf)));
403 }
a4c7ea7b
UD
404
405 return timeout;
67479a70
UD
406}
407
408
a95a08b4
UD
409static int
410lookup (int type, void *key, struct hostent *resultbufp, char *buffer,
384ca551 411 size_t buflen, struct hostent **hst, int32_t *ttlp)
a95a08b4
UD
412{
413 if (type == GETHOSTBYNAME)
384ca551
UD
414 return __gethostbyname3_r (key, AF_INET, resultbufp, buffer, buflen, hst,
415 &h_errno, ttlp, NULL);
8c89236f 416 if (type == GETHOSTBYNAMEv6)
384ca551
UD
417 return __gethostbyname3_r (key, AF_INET6, resultbufp, buffer, buflen, hst,
418 &h_errno, ttlp, NULL);
8c89236f 419 if (type == GETHOSTBYADDR)
384ca551
UD
420 return __gethostbyaddr2_r (key, NS_INADDRSZ, AF_INET, resultbufp, buffer,
421 buflen, hst, &h_errno, ttlp);
422 return __gethostbyaddr2_r (key, NS_IN6ADDRSZ, AF_INET6, resultbufp, buffer,
423 buflen, hst, &h_errno, ttlp);
a95a08b4
UD
424}
425
426
a4c7ea7b 427static time_t
a95a08b4
UD
428addhstbyX (struct database_dyn *db, int fd, request_header *req,
429 void *key, uid_t uid, struct hashentry *he, struct datahead *dh)
67479a70
UD
430{
431 /* Search for the entry matching the key. Please note that we don't
432 look again in the table whether the dataset is now available. We
433 simply insert it. It does not matter if it is in there twice. The
434 pruning function only will look at the timestamp. */
67479a70
UD
435 struct hostent resultbuf;
436 struct hostent *hst;
a95a08b4 437 int errval = 0;
384ca551 438 int32_t ttl = INT32_MAX;
67479a70 439
a1ffb40e 440 if (__glibc_unlikely (debug_level > 0))
a95a08b4 441 {
c207f23b
UD
442 const char *str;
443 char buf[INET6_ADDRSTRLEN + 1];
444 if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
445 str = key;
446 else
447 str = inet_ntop (req->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
448 key, buf, sizeof (buf));
449
a95a08b4 450 if (he == NULL)
c207f23b 451 dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) str);
a95a08b4 452 else
c207f23b 453 dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) str);
a95a08b4 454 }
67479a70 455
2f9f0d18
FW
456 struct scratch_buffer tmpbuf;
457 scratch_buffer_init (&tmpbuf);
458
459 while (lookup (req->type, key, &resultbuf,
460 tmpbuf.data, tmpbuf.length, &hst, &ttl) != 0
67479a70 461 && h_errno == NETDB_INTERNAL
a95a08b4 462 && (errval = errno) == ERANGE)
2f9f0d18
FW
463 if (!scratch_buffer_grow (&tmpbuf))
464 {
465 /* We ran out of memory. We cannot do anything but sending a
466 negative response. In reality this should never
467 happen. */
468 hst = NULL;
469 /* We set the error to indicate this is (possibly) a temporary
470 error and that it does not mean the entry is not
471 available at all. */
472 h_errno = TRY_AGAIN;
473 errval = EAGAIN;
474 break;
475 }
67479a70 476
a4c7ea7b
UD
477 time_t timeout = cache_addhst (db, fd, req, key, hst, uid, he, dh,
478 h_errno == TRY_AGAIN ? errval : 0, ttl);
2f9f0d18 479 scratch_buffer_free (&tmpbuf);
a4c7ea7b 480 return timeout;
67479a70
UD
481}
482
483
484void
a95a08b4 485addhstbyname (struct database_dyn *db, int fd, request_header *req,
a1c542bf 486 void *key, uid_t uid)
67479a70 487{
a95a08b4
UD
488 addhstbyX (db, fd, req, key, uid, NULL, NULL);
489}
67479a70 490
67479a70 491
a4c7ea7b 492time_t
a95a08b4
UD
493readdhstbyname (struct database_dyn *db, struct hashentry *he,
494 struct datahead *dh)
495{
496 request_header req =
a1c542bf 497 {
a95a08b4
UD
498 .type = GETHOSTBYNAME,
499 .key_len = he->len
500 };
a1c542bf 501
a4c7ea7b 502 return addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
a95a08b4 503}
c7a9b6e2 504
67479a70 505
a95a08b4
UD
506void
507addhstbyaddr (struct database_dyn *db, int fd, request_header *req,
508 void *key, uid_t uid)
509{
510 addhstbyX (db, fd, req, key, uid, NULL, NULL);
511}
a1c542bf 512
c7a9b6e2 513
a4c7ea7b 514time_t
a95a08b4
UD
515readdhstbyaddr (struct database_dyn *db, struct hashentry *he,
516 struct datahead *dh)
517{
518 request_header req =
519 {
520 .type = GETHOSTBYADDR,
521 .key_len = he->len
522 };
523
a4c7ea7b 524 return addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
67479a70
UD
525}
526
527
528void
a95a08b4 529addhstbynamev6 (struct database_dyn *db, int fd, request_header *req,
a1c542bf 530 void *key, uid_t uid)
67479a70 531{
a95a08b4
UD
532 addhstbyX (db, fd, req, key, uid, NULL, NULL);
533}
67479a70 534
a1c542bf 535
a4c7ea7b 536time_t
a95a08b4
UD
537readdhstbynamev6 (struct database_dyn *db, struct hashentry *he,
538 struct datahead *dh)
539{
540 request_header req =
67479a70 541 {
a95a08b4
UD
542 .type = GETHOSTBYNAMEv6,
543 .key_len = he->len
544 };
c7a9b6e2 545
a4c7ea7b 546 return addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
67479a70
UD
547}
548
549
550void
a95a08b4 551addhstbyaddrv6 (struct database_dyn *db, int fd, request_header *req,
a1c542bf 552 void *key, uid_t uid)
67479a70 553{
a95a08b4
UD
554 addhstbyX (db, fd, req, key, uid, NULL, NULL);
555}
67479a70 556
67479a70 557
a4c7ea7b 558time_t
a95a08b4
UD
559readdhstbyaddrv6 (struct database_dyn *db, struct hashentry *he,
560 struct datahead *dh)
561{
562 request_header req =
67479a70 563 {
a95a08b4
UD
564 .type = GETHOSTBYADDRv6,
565 .key_len = he->len
566 };
c7a9b6e2 567
a4c7ea7b 568 return addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
67479a70 569}