1/* Cache handling for host lookup.
2 Copyright (C) 2004-2019 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
5
6 This program is free software; you can redistribute it and/or modify
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.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>. */
18
19#include <assert.h>
20#include <errno.h>
21#include <libintl.h>
22#include <netdb.h>
23#include <nss.h>
24#include <string.h>
25#include <time.h>
26#include <unistd.h>
27#include <sys/mman.h>
28#include <resolv/resolv-internal.h>
29#include <resolv/resolv_context.h>
30#include <scratch_buffer.h>
31
32#include "dbg_log.h"
33#include "nscd.h"
34
35
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);
40typedef enum nss_status (*nss_gethostbyname3_r)
41 (const char *name, int af, struct hostent *host,
42 char *buffer, size_t buflen, int *errnop,
43 int *h_errnop, int32_t *, char **);
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
60static time_t
61addhstaiX (struct database_dyn *db, int fd, request_header *req,
62 void *key, uid_t uid, struct hashentry *const he,
63 struct datahead *dh)
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. */
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 (__glibc_unlikely (debug_level > 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
87 static service_user *hosts_database;
88 service_user *nip;
89 int no_more;
90 int rc6 = 0;
91 int rc4 = 0;
92 int herrno = 0;
93
94 if (hosts_database == NULL)
95 no_more = __nss_database_lookup2 ("hosts", NULL,
96 "dns [!UNAVAIL=return] files",
97 &hosts_database);
98 else
99 no_more = 0;
100 nip = hosts_database;
101
102 /* Initialize configurations. */
103 struct resolv_context *ctx = __resolv_context_get ();
104 if (ctx == NULL)
105 no_more = 1;
106
107 struct scratch_buffer tmpbuf6;
108 scratch_buffer_init (&tmpbuf6);
109 struct scratch_buffer tmpbuf4;
110 scratch_buffer_init (&tmpbuf4);
111 struct scratch_buffer canonbuf;
112 scratch_buffer_init (&canonbuf);
113
114 int32_t ttl = INT32_MAX;
115 ssize_t total = 0;
116 char *key_copy = NULL;
117 bool alloca_used = false;
118 time_t timeout = MAX_TIMEOUT_VALUE;
119
120 while (!no_more)
121 {
122 void *cp;
123 int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL };
124 int naddrs = 0;
125 size_t addrslen = 0;
126
127 char *canon = NULL;
128 size_t canonlen;
129
130 nss_gethostbyname4_r fct4 = __nss_lookup_function (nip,
131 "gethostbyname4_r");
132 if (fct4 != NULL)
133 {
134 struct gaih_addrtuple atmem;
135 struct gaih_addrtuple *at;
136 while (1)
137 {
138 at = &atmem;
139 rc6 = 0;
140 herrno = 0;
141 status[1] = DL_CALL_FCT (fct4, (key, &at,
142 tmpbuf6.data, tmpbuf6.length,
143 &rc6, &herrno, &ttl));
144 if (rc6 != ERANGE || (herrno != NETDB_INTERNAL
145 && herrno != TRY_AGAIN))
146 break;
147 if (!scratch_buffer_grow (&tmpbuf6))
148 {
149 rc6 = ENOMEM;
150 break;
151 }
152 }
153
154 if (rc6 != 0 && herrno == NETDB_INTERNAL)
155 goto out;
156
157 if (status[1] != NSS_STATUS_SUCCESS)
158 goto next_nip;
159
160 /* We found the data. Count the addresses and the size. */
161 for (const struct gaih_addrtuple *at2 = at = &atmem; at2 != NULL;
162 at2 = at2->next)
163 {
164 ++naddrs;
165 /* We do not handle anything other than IPv4 and IPv6
166 addresses. The getaddrinfo implementation does not
167 either so it is not worth trying to do more. */
168 if (at2->family == AF_INET)
169 addrslen += INADDRSZ;
170 else if (at2->family == AF_INET6)
171 addrslen += IN6ADDRSZ;
172 }
173 canon = at->name;
174 canonlen = strlen (canon) + 1;
175
176 total = sizeof (*dataset) + naddrs + addrslen + canonlen;
177
178 /* Now we can allocate the data structure. If the TTL of the
179 entry is reported as zero do not cache the entry at all. */
180 if (ttl != 0 && he == NULL)
181 dataset = (struct dataset *) mempool_alloc (db, total
182 + req->key_len, 1);
183
184 if (dataset == NULL)
185 {
186 /* We cannot permanently add the result in the moment. But
187 we can provide the result as is. Store the data in some
188 temporary memory. */
189 dataset = (struct dataset *) alloca (total + req->key_len);
190
191 /* We cannot add this record to the permanent database. */
192 alloca_used = true;
193 }
194
195 /* Fill in the address and address families. */
196 char *addrs = dataset->strdata;
197 uint8_t *family = (uint8_t *) (addrs + addrslen);
198
199 for (const struct gaih_addrtuple *at2 = at; at2 != NULL;
200 at2 = at2->next)
201 {
202 *family++ = at2->family;
203 if (at2->family == AF_INET)
204 addrs = mempcpy (addrs, at2->addr, INADDRSZ);
205 else if (at2->family == AF_INET6)
206 addrs = mempcpy (addrs, at2->addr, IN6ADDRSZ);
207 }
208
209 cp = family;
210 }
211 else
212 {
213 /* Prefer the function which also returns the TTL and
214 canonical name. */
215 nss_gethostbyname3_r fct = __nss_lookup_function (nip,
216 "gethostbyname3_r");
217 if (fct == NULL)
218 fct = __nss_lookup_function (nip, "gethostbyname2_r");
219
220 if (fct == NULL)
221 goto next_nip;
222
223 struct hostent th[2];
224
225 /* Collect IPv6 information first. */
226 while (1)
227 {
228 rc6 = 0;
229 status[0] = DL_CALL_FCT (fct, (key, AF_INET6, &th[0],
230 tmpbuf6.data, tmpbuf6.length,
231 &rc6, &herrno, &ttl,
232 &canon));
233 if (rc6 != ERANGE || herrno != NETDB_INTERNAL)
234 break;
235 if (!scratch_buffer_grow (&tmpbuf6))
236 {
237 rc6 = ENOMEM;
238 break;
239 }
240 }
241
242 if (rc6 != 0 && herrno == NETDB_INTERNAL)
243 goto out;
244
245 /* Next collect IPv4 information. */
246 while (1)
247 {
248 rc4 = 0;
249 status[1] = DL_CALL_FCT (fct, (key, AF_INET, &th[1],
250 tmpbuf4.data, tmpbuf4.length,
251 &rc4, &herrno,
252 ttl == INT32_MAX ? &ttl : NULL,
253 canon == NULL ? &canon : NULL));
254 if (rc4 != ERANGE || herrno != NETDB_INTERNAL)
255 break;
256 if (!scratch_buffer_grow (&tmpbuf4))
257 {
258 rc4 = ENOMEM;
259 break;
260 }
261 }
262
263 if (rc4 != 0 && herrno == NETDB_INTERNAL)
264 goto out;
265
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)
280 {
281 /* Determine the canonical name. */
282 nss_getcanonname_r cfct;
283 cfct = __nss_lookup_function (nip, "getcanonname_r");
284 if (cfct != NULL)
285 {
286 char *s;
287 int rc;
288
289 if (DL_CALL_FCT (cfct, (key, canonbuf.data, canonbuf.length,
290 &s, &rc, &herrno))
291 == NSS_STATUS_SUCCESS)
292 canon = s;
293 else
294 /* Set to name now to avoid using gethostbyaddr. */
295 canon = key;
296 }
297 else
298 {
299 struct hostent *hstent = NULL;
300 int herrno;
301 struct hostent hstent_mem;
302 void *addr;
303 size_t addrlen;
304 int addrfamily;
305
306 if (status[1] == NSS_STATUS_SUCCESS)
307 {
308 addr = th[1].h_addr_list[0];
309 addrlen = sizeof (struct in_addr);
310 addrfamily = AF_INET;
311 }
312 else
313 {
314 addr = th[0].h_addr_list[0];
315 addrlen = sizeof (struct in6_addr);
316 addrfamily = AF_INET6;
317 }
318
319 int rc;
320 while (1)
321 {
322 rc = __gethostbyaddr2_r (addr, addrlen, addrfamily,
323 &hstent_mem,
324 canonbuf.data, canonbuf.length,
325 &hstent, &herrno, NULL);
326 if (rc != ERANGE || herrno != NETDB_INTERNAL)
327 break;
328 if (!scratch_buffer_grow (&canonbuf))
329 {
330 rc = ENOMEM;
331 break;
332 }
333 }
334
335 if (rc == 0)
336 {
337 if (hstent != NULL)
338 canon = hstent->h_name;
339 else
340 canon = key;
341 }
342 }
343 }
344
345 canonlen = canon == NULL ? 0 : (strlen (canon) + 1);
346
347 total = sizeof (*dataset) + naddrs + addrslen + canonlen;
348
349
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)
353 dataset = (struct dataset *) mempool_alloc (db, total
354 + req->key_len, 1);
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 }
366
367 /* Fill in the address and address families. */
368 char *addrs = dataset->strdata;
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;
378 }
379
380 cp = family;
381 }
382
383 timeout = datahead_init_pos (&dataset->head, total + req->key_len,
384 total - offsetof (struct dataset, resp),
385 he == NULL ? 0 : dh->nreloads + 1,
386 ttl == INT32_MAX ? db->postimeout : ttl);
387
388 /* Fill in the rest of the dataset. */
389 dataset->resp.version = NSCD_VERSION;
390 dataset->resp.found = 1;
391 dataset->resp.naddrs = naddrs;
392 dataset->resp.addrslen = addrslen;
393 dataset->resp.canonlen = canonlen;
394 dataset->resp.error = NETDB_SUCCESS;
395
396 if (canon != NULL)
397 cp = mempcpy (cp, canon, canonlen);
398
399 key_copy = memcpy (cp, key, req->key_len);
400
401 assert (cp == (char *) dataset + total);
402
403 /* Now we can determine whether on refill we have to create a
404 new record or not. */
405 if (he != NULL)
406 {
407 assert (fd == -1);
408
409 if (total + req->key_len == dh->allocsize
410 && total - offsetof (struct dataset, resp) == dh->recsize
411 && memcmp (&dataset->resp, dh->data,
412 dh->allocsize - offsetof (struct dataset,
413 resp)) == 0)
414 {
415 /* The data has not changed. We will just bump the
416 timeout value. Note that the new record has been
417 allocated on the stack and need not be freed. */
418 dh->timeout = dataset->head.timeout;
419 dh->ttl = dataset->head.ttl;
420 ++dh->nreloads;
421 }
422 else
423 {
424 /* We have to create a new record. Just allocate
425 appropriate memory and copy it. */
426 struct dataset *newp
427 = (struct dataset *) mempool_alloc (db, total + req->key_len,
428 1);
429 if (__glibc_likely (newp != NULL))
430 {
431 /* Adjust pointer into the memory block. */
432 key_copy = (char *) newp + (key_copy - (char *) dataset);
433
434 dataset = memcpy (newp, dataset, total + req->key_len);
435 alloca_used = false;
436 }
437
438 /* Mark the old record as obsolete. */
439 dh->usable = false;
440 }
441 }
442 else
443 {
444 /* We write the dataset before inserting it to the database
445 since while inserting this thread might block and so
446 would unnecessarily let the receiver wait. */
447 assert (fd != -1);
448
449 writeall (fd, &dataset->resp, dataset->head.recsize);
450 }
451
452 goto out;
453
454next_nip:
455 if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN)
456 break;
457
458 if (nip->next == NULL)
459 no_more = -1;
460 else
461 nip = nip->next;
462 }
463
464 /* No result found. Create a negative result record. */
465 if (he != NULL && rc4 == EAGAIN)
466 {
467 /* If we have an old record available but cannot find one now
468 because the service is not available we keep the old record
469 and make sure it does not get removed. */
470 if (reload_count != UINT_MAX && dh->nreloads == reload_count)
471 /* Do not reset the value if we never not reload the record. */
472 dh->nreloads = reload_count - 1;
473
474 /* Reload with the same time-to-live value. */
475 timeout = dh->timeout = time (NULL) + dh->ttl;
476 }
477 else
478 {
479 /* We have no data. This means we send the standard reply for
480 this case. */
481 total = sizeof (notfound);
482
483 if (fd != -1)
484 TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
485
486 /* If we have a transient error or cannot permanently store the
487 result, so be it. */
488 if (rc4 == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
489 {
490 /* Mark the old entry as obsolete. */
491 if (dh != NULL)
492 dh->usable = false;
493 dataset = NULL;
494 }
495 else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
496 + req->key_len), 1)) != NULL)
497 {
498 timeout = datahead_init_neg (&dataset->head,
499 sizeof (struct dataset) + req->key_len,
500 total, db->negtimeout);
501
502 /* This is the reply. */
503 memcpy (&dataset->resp, &notfound, total);
504
505 /* Copy the key data. */
506 key_copy = memcpy (dataset->strdata, key, req->key_len);
507 }
508 }
509
510 out:
511 __resolv_context_put (ctx);
512
513 if (dataset != NULL && !alloca_used)
514 {
515 /* If necessary, we also propagate the data to disk. */
516 if (db->persistent)
517 {
518 // XXX async OK?
519 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
520 msync ((void *) pval,
521 ((uintptr_t) dataset & pagesize_m1) + total + req->key_len,
522 MS_ASYNC);
523 }
524
525 (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
526 true, db, uid, he == NULL);
527
528 pthread_rwlock_unlock (&db->lock);
529
530 /* Mark the old entry as obsolete. */
531 if (dh != NULL)
532 dh->usable = false;
533 }
534
535 scratch_buffer_free (&tmpbuf6);
536 scratch_buffer_free (&tmpbuf4);
537 scratch_buffer_free (&canonbuf);
538
539 return timeout;
540}
541
542
543void
544addhstai (struct database_dyn *db, int fd, request_header *req, void *key,
545 uid_t uid)
546{
547 addhstaiX (db, fd, req, key, uid, NULL, NULL);
548}
549
550
551time_t
552readdhstai (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
553{
554 request_header req =
555 {
556 .type = GETAI,
557 .key_len = he->len
558 };
559
560 return addhstaiX (db, -1, &req, db->data + he->key, he->owner, he, dh);
561}
562