]> git.ipfire.org Git - thirdparty/glibc.git/blame - nscd/aicache.c
Fix dependencies for stdlib/tst-tls-atexit
[thirdparty/glibc.git] / nscd / aicache.c
CommitLineData
d19687d6 1/* Cache handling for host lookup.
568035b7 2 Copyright (C) 2004-2013 Free Software Foundation, Inc.
d19687d6
UD
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
5
43bc8ac6 6 This program is free software; you can redistribute it and/or modify
2e2efe65
RM
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; version 2 of the License, or
9 (at your option) any later version.
d19687d6 10
43bc8ac6 11 This program is distributed in the hope that it will be useful,
d19687d6 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
43bc8ac6
UD
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
d19687d6 15
43bc8ac6 16 You should have received a copy of the GNU General Public License
59ba27a6 17 along with this program; if not, see <http://www.gnu.org/licenses/>. */
d19687d6
UD
18
19#include <assert.h>
20#include <errno.h>
21#include <libintl.h>
22#include <netdb.h>
1eb946b9 23#include <nss.h>
d19687d6
UD
24#include <string.h>
25#include <time.h>
26#include <unistd.h>
27#include <sys/mman.h>
eac10791
UD
28
29#include "dbg_log.h"
30#include "nscd.h"
31#ifdef HAVE_SENDFILE
32# include <kernel-features.h>
33#endif
d19687d6
UD
34
35
1eb946b9
UD
36typedef enum nss_status (*nss_gethostbyname4_r)
37 (const char *name, struct gaih_addrtuple **pat,
38 char *buffer, size_t buflen, int *errnop,
39 int *h_errnop, int32_t *ttlp);
d1fe1f22 40typedef enum nss_status (*nss_gethostbyname3_r)
d19687d6
UD
41 (const char *name, int af, struct hostent *host,
42 char *buffer, size_t buflen, int *errnop,
d1fe1f22 43 int *h_errnop, int32_t *, char **);
d19687d6
UD
44typedef enum nss_status (*nss_getcanonname_r)
45 (const char *name, char *buffer, size_t buflen, char **result,
46 int *errnop, int *h_errnop);
47
48
49static const ai_response_header notfound =
50{
51 .version = NSCD_VERSION,
52 .found = 0,
53 .naddrs = 0,
54 .addrslen = 0,
55 .canonlen = 0,
56 .error = 0
57};
58
59
a4c7ea7b 60static time_t
d19687d6 61addhstaiX (struct database_dyn *db, int fd, request_header *req,
20e498bd
UD
62 void *key, uid_t uid, struct hashentry *const he,
63 struct datahead *dh)
d19687d6
UD
64{
65 /* Search for the entry matching the key. Please note that we don't
66 look again in the table whether the dataset is now available. We
67 simply insert it. It does not matter if it is in there twice. The
68 pruning function only will look at the timestamp. */
d19687d6
UD
69
70 /* We allocate all data in one memory block: the iov vector,
71 the response header and the dataset itself. */
72 struct dataset
73 {
74 struct datahead head;
75 ai_response_header resp;
76 char strdata[0];
77 } *dataset = NULL;
78
79 if (__builtin_expect (debug_level > 0, 0))
80 {
81 if (he == NULL)
82 dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key);
83 else
84 dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key);
85 }
86
d19687d6
UD
87 static service_user *hosts_database;
88 service_user *nip = NULL;
89 int no_more;
90 int rc6 = 0;
91 int rc4 = 0;
92 int herrno = 0;
93
94 if (hosts_database != NULL)
95 {
96 nip = hosts_database;
97 no_more = 0;
98 }
99 else
100 no_more = __nss_database_lookup ("hosts", NULL,
101 "dns [!UNAVAIL=return] files", &nip);
102
103 if (__res_maybe_init (&_res, 0) == -1)
e0a69f5c 104 no_more = 1;
d19687d6
UD
105
106 /* If we are looking for both IPv4 and IPv6 address we don't want
107 the lookup functions to automatically promote IPv4 addresses to
108 IPv6 addresses. Currently this is decided by setting the
109 RES_USE_INET6 bit in _res.options. */
110 int old_res_options = _res.options;
111 _res.options &= ~RES_USE_INET6;
112
ea42a20c 113 size_t tmpbuf6len = 1024;
d19687d6
UD
114 char *tmpbuf6 = alloca (tmpbuf6len);
115 size_t tmpbuf4len = 0;
116 char *tmpbuf4 = NULL;
384ca551 117 int32_t ttl = INT32_MAX;
d19687d6
UD
118 ssize_t total = 0;
119 char *key_copy = NULL;
120 bool alloca_used = false;
a4c7ea7b 121 time_t timeout = MAX_TIMEOUT_VALUE;
d19687d6
UD
122
123 while (!no_more)
124 {
1eb946b9 125 void *cp;
d19687d6 126 int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL };
1eb946b9
UD
127 int naddrs = 0;
128 size_t addrslen = 0;
5c9629d2 129 char *canon = NULL;
1eb946b9
UD
130 size_t canonlen;
131
132 nss_gethostbyname4_r fct4 = __nss_lookup_function (nip,
133 "gethostbyname4_r");
134 if (fct4 != NULL)
135 {
ea42a20c
UD
136 struct gaih_addrtuple atmem;
137 struct gaih_addrtuple *at;
1eb946b9
UD
138 while (1)
139 {
ea42a20c 140 at = &atmem;
1eb946b9 141 rc6 = 0;
5c9629d2
UD
142 herrno = 0;
143 status[1] = DL_CALL_FCT (fct4, (key, &at, tmpbuf6, tmpbuf6len,
1eb946b9 144 &rc6, &herrno, &ttl));
5c9629d2
UD
145 if (rc6 != ERANGE || (herrno != NETDB_INTERNAL
146 && herrno != TRY_AGAIN))
1eb946b9
UD
147 break;
148 tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len);
149 }
150
151 if (rc6 != 0 && herrno == NETDB_INTERNAL)
152 goto out;
d19687d6 153
5c9629d2 154 if (status[1] != NSS_STATUS_SUCCESS)
1eb946b9
UD
155 goto next_nip;
156
157 /* We found the data. Count the addresses and the size. */
ea42a20c 158 for (const struct gaih_addrtuple *at2 = at = &atmem; at2 != NULL;
5c9629d2 159 at2 = at2->next)
1eb946b9
UD
160 {
161 ++naddrs;
5c9629d2
UD
162 /* We do not handle anything other than IPv4 and IPv6
163 addresses. The getaddrinfo implementation does not
164 either so it is not worth trying to do more. */
1eb946b9
UD
165 if (at2->family == AF_INET)
166 addrslen += INADDRSZ;
5c9629d2 167 else if (at2->family == AF_INET6)
1eb946b9 168 addrslen += IN6ADDRSZ;
1eb946b9
UD
169 }
170 canon = at->name;
171 canonlen = strlen (canon) + 1;
172
173 total = sizeof (*dataset) + naddrs + addrslen + canonlen;
174
175 /* Now we can allocate the data structure. If the TTL of the
176 entry is reported as zero do not cache the entry at all. */
177 if (ttl != 0 && he == NULL)
20e498bd
UD
178 dataset = (struct dataset *) mempool_alloc (db, total
179 + req->key_len, 1);
1eb946b9
UD
180
181 if (dataset == NULL)
182 {
183 /* We cannot permanently add the result in the moment. But
184 we can provide the result as is. Store the data in some
185 temporary memory. */
186 dataset = (struct dataset *) alloca (total + req->key_len);
187
188 /* We cannot add this record to the permanent database. */
189 alloca_used = true;
190 }
d1fe1f22 191
1eb946b9 192 /* Fill in the address and address families. */
5c9629d2 193 char *addrs = dataset->strdata;
1eb946b9
UD
194 uint8_t *family = (uint8_t *) (addrs + addrslen);
195
5c9629d2
UD
196 for (const struct gaih_addrtuple *at2 = at; at2 != NULL;
197 at2 = at2->next)
1eb946b9
UD
198 {
199 *family++ = at2->family;
200 if (at2->family == AF_INET)
201 addrs = mempcpy (addrs, at2->addr, INADDRSZ);
5c9629d2 202 else if (at2->family == AF_INET6)
1eb946b9 203 addrs = mempcpy (addrs, at2->addr, IN6ADDRSZ);
1eb946b9
UD
204 }
205
206 cp = family;
207 }
208 else
d19687d6 209 {
1eb946b9
UD
210 /* Prefer the function which also returns the TTL and
211 canonical name. */
212 nss_gethostbyname3_r fct = __nss_lookup_function (nip,
213 "gethostbyname3_r");
214 if (fct == NULL)
215 fct = __nss_lookup_function (nip, "gethostbyname2_r");
216
217 if (fct == NULL)
218 goto next_nip;
219
d19687d6
UD
220 struct hostent th[2];
221
222 /* Collect IPv6 information first. */
223 while (1)
224 {
225 rc6 = 0;
226 status[0] = DL_CALL_FCT (fct, (key, AF_INET6, &th[0], tmpbuf6,
1eb946b9
UD
227 tmpbuf6len, &rc6, &herrno, &ttl,
228 &canon));
d19687d6
UD
229 if (rc6 != ERANGE || herrno != NETDB_INTERNAL)
230 break;
231 tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len);
232 }
233
234 if (rc6 != 0 && herrno == NETDB_INTERNAL)
235 goto out;
236
237 /* If the IPv6 lookup has been successful do not use the
238 buffer used in that lookup, use a new one. */
239 if (status[0] == NSS_STATUS_SUCCESS && rc6 == 0)
240 {
241 tmpbuf4len = 512;
242 tmpbuf4 = alloca (tmpbuf4len);
243 }
244 else
245 {
246 tmpbuf4len = tmpbuf6len;
247 tmpbuf4 = tmpbuf6;
248 }
249
ffb1b882 250 /* Next collect IPv4 information. */
d19687d6
UD
251 while (1)
252 {
253 rc4 = 0;
254 status[1] = DL_CALL_FCT (fct, (key, AF_INET, &th[1], tmpbuf4,
d1fe1f22 255 tmpbuf4len, &rc4, &herrno,
384ca551 256 ttl == INT32_MAX ? &ttl : NULL,
d1fe1f22 257 canon == NULL ? &canon : NULL));
d19687d6
UD
258 if (rc4 != ERANGE || herrno != NETDB_INTERNAL)
259 break;
d1fe1f22 260 tmpbuf4 = extend_alloca (tmpbuf4, tmpbuf4len, 2 * tmpbuf4len);
d19687d6
UD
261 }
262
ffb1b882 263 if (rc4 != 0 && herrno == NETDB_INTERNAL)
d19687d6
UD
264 goto out;
265
1eb946b9
UD
266 if (status[0] != NSS_STATUS_SUCCESS
267 && status[1] != NSS_STATUS_SUCCESS)
268 goto next_nip;
269
270 /* We found the data. Count the addresses and the size. */
271 for (int j = 0; j < 2; ++j)
272 if (status[j] == NSS_STATUS_SUCCESS)
273 for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
274 {
275 ++naddrs;
276 addrslen += th[j].h_length;
277 }
278
279 if (canon == NULL)
d19687d6 280 {
1eb946b9
UD
281 /* Determine the canonical name. */
282 nss_getcanonname_r cfct;
283 cfct = __nss_lookup_function (nip, "getcanonname_r");
284 if (cfct != NULL)
285 {
286 const size_t max_fqdn_len = 256;
287 char *buf = alloca (max_fqdn_len);
288 char *s;
289 int rc;
290
291 if (DL_CALL_FCT (cfct, (key, buf, max_fqdn_len, &s,
292 &rc, &herrno))
293 == NSS_STATUS_SUCCESS)
294 canon = s;
295 else
296 /* Set to name now to avoid using gethostbyaddr. */
297 canon = key;
298 }
299 else
300 {
20e498bd 301 struct hostent *hstent = NULL;
1eb946b9 302 int herrno;
20e498bd 303 struct hostent hstent_mem;
1eb946b9
UD
304 void *addr;
305 size_t addrlen;
306 int addrfamily;
307
308 if (status[1] == NSS_STATUS_SUCCESS)
309 {
310 addr = th[1].h_addr_list[0];
311 addrlen = sizeof (struct in_addr);
312 addrfamily = AF_INET;
313 }
314 else
d19687d6 315 {
1eb946b9
UD
316 addr = th[0].h_addr_list[0];
317 addrlen = sizeof (struct in6_addr);
318 addrfamily = AF_INET6;
d19687d6
UD
319 }
320
1eb946b9
UD
321 size_t tmpbuflen = 512;
322 char *tmpbuf = alloca (tmpbuflen);
323 int rc;
324 while (1)
d1fe1f22 325 {
1eb946b9 326 rc = __gethostbyaddr2_r (addr, addrlen, addrfamily,
20e498bd
UD
327 &hstent_mem, tmpbuf, tmpbuflen,
328 &hstent, &herrno, NULL);
1eb946b9
UD
329 if (rc != ERANGE || herrno != NETDB_INTERNAL)
330 break;
331 tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
332 tmpbuflen * 2);
d1fe1f22 333 }
1eb946b9
UD
334
335 if (rc == 0)
d1fe1f22 336 {
20e498bd
UD
337 if (hstent != NULL)
338 canon = hstent->h_name;
d1fe1f22 339 else
1eb946b9 340 canon = key;
d1fe1f22 341 }
d19687d6 342 }
1eb946b9 343 }
d19687d6 344
1eb946b9
UD
345 canonlen = canon == NULL ? 0 : (strlen (canon) + 1);
346
347 total = sizeof (*dataset) + naddrs + addrslen + canonlen;
d19687d6 348
d19687d6 349
1eb946b9
UD
350 /* Now we can allocate the data structure. If the TTL of the
351 entry is reported as zero do not cache the entry at all. */
352 if (ttl != 0 && he == NULL)
20e498bd
UD
353 dataset = (struct dataset *) mempool_alloc (db, total
354 + req->key_len, 1);
1eb946b9
UD
355
356 if (dataset == NULL)
357 {
358 /* We cannot permanently add the result in the moment. But
359 we can provide the result as is. Store the data in some
360 temporary memory. */
361 dataset = (struct dataset *) alloca (total + req->key_len);
362
363 /* We cannot add this record to the permanent database. */
364 alloca_used = true;
365 }
d19687d6 366
1eb946b9 367 /* Fill in the address and address families. */
5c9629d2 368 char *addrs = dataset->strdata;
1eb946b9
UD
369 uint8_t *family = (uint8_t *) (addrs + addrslen);
370
371 for (int j = 0; j < 2; ++j)
372 if (status[j] == NSS_STATUS_SUCCESS)
373 for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
374 {
375 addrs = mempcpy (addrs, th[j].h_addr_list[i],
376 th[j].h_length);
377 *family++ = th[j].h_addrtype;
d19687d6
UD
378 }
379
1eb946b9
UD
380 cp = family;
381 }
d19687d6 382
1eb946b9
UD
383 /* Fill in the rest of the dataset. */
384 dataset->head.allocsize = total + req->key_len;
385 dataset->head.recsize = total - offsetof (struct dataset, resp);
386 dataset->head.notfound = false;
387 dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
388 dataset->head.usable = true;
d19687d6 389
1eb946b9 390 /* Compute the timeout time. */
a4c7ea7b
UD
391 dataset->head.ttl = ttl == INT32_MAX ? db->postimeout : ttl;
392 timeout = dataset->head.timeout = time (NULL) + dataset->head.ttl;
d19687d6 393
1eb946b9
UD
394 dataset->resp.version = NSCD_VERSION;
395 dataset->resp.found = 1;
396 dataset->resp.naddrs = naddrs;
397 dataset->resp.addrslen = addrslen;
398 dataset->resp.canonlen = canonlen;
399 dataset->resp.error = NETDB_SUCCESS;
d19687d6 400
1eb946b9
UD
401 if (canon != NULL)
402 cp = mempcpy (cp, canon, canonlen);
d19687d6 403
1eb946b9
UD
404 key_copy = memcpy (cp, key, req->key_len);
405
5c9629d2
UD
406 assert (cp == (char *) dataset + total);
407
1eb946b9
UD
408 /* Now we can determine whether on refill we have to create a
409 new record or not. */
410 if (he != NULL)
411 {
412 assert (fd == -1);
413
414 if (total + req->key_len == dh->allocsize
415 && total - offsetof (struct dataset, resp) == dh->recsize
416 && memcmp (&dataset->resp, dh->data,
417 dh->allocsize - offsetof (struct dataset,
418 resp)) == 0)
419 {
420 /* The data has not changed. We will just bump the
421 timeout value. Note that the new record has been
422 allocated on the stack and need not be freed. */
423 dh->timeout = dataset->head.timeout;
a4c7ea7b 424 dh->ttl = dataset->head.ttl;
1eb946b9
UD
425 ++dh->nreloads;
426 }
427 else
428 {
429 /* We have to create a new record. Just allocate
430 appropriate memory and copy it. */
431 struct dataset *newp
432 = (struct dataset *) mempool_alloc (db, total + req->key_len,
20e498bd 433 1);
1eb946b9
UD
434 if (__builtin_expect (newp != NULL, 1))
435 {
436 /* Adjust pointer into the memory block. */
437 key_copy = (char *) newp + (key_copy - (char *) dataset);
438
439 dataset = memcpy (newp, dataset, total + req->key_len);
440 alloca_used = false;
d19687d6 441 }
1eb946b9
UD
442
443 /* Mark the old record as obsolete. */
444 dh->usable = false;
445 }
446 }
447 else
448 {
449 /* We write the dataset before inserting it to the database
450 since while inserting this thread might block and so
451 would unnecessarily let the receiver wait. */
452 assert (fd != -1);
d19687d6 453
eac10791 454#ifdef HAVE_SENDFILE
1eb946b9
UD
455 if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
456 {
457 assert (db->wr_fd != -1);
458 assert ((char *) &dataset->resp > (char *) db->data);
ea547a1a 459 assert ((char *) dataset - (char *) db->head + total
1eb946b9
UD
460 <= (sizeof (struct database_pers_head)
461 + db->head->module * sizeof (ref_t)
462 + db->head->data_size));
9bea3473 463# ifndef __ASSUME_SENDFILE
1eb946b9 464 ssize_t written;
9bea3473
UD
465 written =
466# endif
467 sendfileall (fd, db->wr_fd, (char *) &dataset->resp
468 - (char *) db->head, dataset->head.recsize);
eac10791 469# ifndef __ASSUME_SENDFILE
1eb946b9
UD
470 if (written == -1 && errno == ENOSYS)
471 goto use_write;
eac10791 472# endif
1eb946b9
UD
473 }
474 else
eac10791 475# ifndef __ASSUME_SENDFILE
1eb946b9 476 use_write:
eac10791
UD
477# endif
478#endif
ea547a1a 479 writeall (fd, &dataset->resp, dataset->head.recsize);
d19687d6
UD
480 }
481
1eb946b9
UD
482 goto out;
483
484next_nip:
d19687d6
UD
485 if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN)
486 break;
487
488 if (nip->next == NULL)
489 no_more = -1;
490 else
491 nip = nip->next;
492 }
493
494 /* No result found. Create a negative result record. */
495 if (he != NULL && rc4 == EAGAIN)
496 {
497 /* If we have an old record available but cannot find one now
498 because the service is not available we keep the old record
499 and make sure it does not get removed. */
500 if (reload_count != UINT_MAX && dh->nreloads == reload_count)
501 /* Do not reset the value if we never not reload the record. */
502 dh->nreloads = reload_count - 1;
a4c7ea7b
UD
503
504 /* Reload with the same time-to-live value. */
505 timeout = dh->timeout = time (NULL) + dh->ttl;
d19687d6
UD
506 }
507 else
508 {
509 /* We have no data. This means we send the standard reply for
510 this case. */
511 total = sizeof (notfound);
512
513 if (fd != -1)
2c210d1e 514 TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
d19687d6 515
3e1aa84e
UD
516 /* If we have a transient error or cannot permanently store the
517 result, so be it. */
518 if (rc4 == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
445b4a53
TK
519 {
520 /* Mark the old entry as obsolete. */
521 if (dh != NULL)
522 dh->usable = false;
523 dataset = NULL;
524 }
99231d9a
UD
525 else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
526 + req->key_len), 1)) != NULL)
d19687d6
UD
527 {
528 dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
529 dataset->head.recsize = total;
530 dataset->head.notfound = true;
531 dataset->head.nreloads = 0;
532 dataset->head.usable = true;
533
534 /* Compute the timeout time. */
a4c7ea7b
UD
535 timeout = dataset->head.timeout = time (NULL) + db->negtimeout;
536 dataset->head.ttl = db->negtimeout;
d19687d6
UD
537
538 /* This is the reply. */
539 memcpy (&dataset->resp, &notfound, total);
540
541 /* Copy the key data. */
542 key_copy = memcpy (dataset->strdata, key, req->key_len);
543 }
d19687d6
UD
544 }
545
546 out:
034807a9 547 _res.options |= old_res_options & RES_USE_INET6;
d19687d6 548
d19687d6
UD
549 if (dataset != NULL && !alloca_used)
550 {
551 /* If necessary, we also propagate the data to disk. */
552 if (db->persistent)
553 {
554 // XXX async OK?
555 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
556 msync ((void *) pval,
557 ((uintptr_t) dataset & pagesize_m1) + total + req->key_len,
558 MS_ASYNC);
559 }
560
7e71e55f 561 (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
528741cb 562 true, db, uid, he == NULL);
d19687d6 563
00ebd7ed
UD
564 pthread_rwlock_unlock (&db->lock);
565
d19687d6
UD
566 /* Mark the old entry as obsolete. */
567 if (dh != NULL)
568 dh->usable = false;
569 }
a4c7ea7b
UD
570
571 return timeout;
d19687d6
UD
572}
573
574
575void
576addhstai (struct database_dyn *db, int fd, request_header *req, void *key,
577 uid_t uid)
578{
579 addhstaiX (db, fd, req, key, uid, NULL, NULL);
580}
581
582
a4c7ea7b 583time_t
d19687d6
UD
584readdhstai (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
585{
586 request_header req =
587 {
588 .type = GETAI,
589 .key_len = he->len
590 };
591
a4c7ea7b 592 return addhstaiX (db, -1, &req, db->data + he->key, he->owner, he, dh);
d19687d6 593}