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