Remove Linux kernel version ambiguity in comment added by previous commit.
[platform/upstream/glibc.git] / nscd / aicache.c
1 /* Cache handling for host lookup.
2    Copyright (C) 2004-2013 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
29 #include "dbg_log.h"
30 #include "nscd.h"
31 #ifdef HAVE_SENDFILE
32 # include <kernel-features.h>
33 #endif
34
35
36 typedef 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);
40 typedef 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 **);
44 typedef 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
49 static 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
60 static time_t
61 addhstaiX (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 (__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
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)
104             no_more = 1;
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
113   size_t tmpbuf6len = 1024;
114   char *tmpbuf6 = alloca (tmpbuf6len);
115   size_t tmpbuf4len = 0;
116   char *tmpbuf4 = NULL;
117   int32_t ttl = INT32_MAX;
118   ssize_t total = 0;
119   char *key_copy = NULL;
120   bool alloca_used = false;
121   time_t timeout = MAX_TIMEOUT_VALUE;
122
123   while (!no_more)
124     {
125       void *cp;
126       int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL };
127       int naddrs = 0;
128       size_t addrslen = 0;
129       char *canon = NULL;
130       size_t canonlen;
131
132       nss_gethostbyname4_r fct4 = __nss_lookup_function (nip,
133                                                          "gethostbyname4_r");
134       if (fct4 != NULL)
135         {
136           struct gaih_addrtuple atmem;
137           struct gaih_addrtuple *at;
138           while (1)
139             {
140               at = &atmem;
141               rc6 = 0;
142               herrno = 0;
143               status[1] = DL_CALL_FCT (fct4, (key, &at, tmpbuf6, tmpbuf6len,
144                                               &rc6, &herrno, &ttl));
145               if (rc6 != ERANGE || (herrno != NETDB_INTERNAL
146                                     && herrno != TRY_AGAIN))
147                 break;
148               tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len);
149             }
150
151           if (rc6 != 0 && herrno == NETDB_INTERNAL)
152             goto out;
153
154           if (status[1] != NSS_STATUS_SUCCESS)
155             goto next_nip;
156
157           /* We found the data.  Count the addresses and the size.  */
158           for (const struct gaih_addrtuple *at2 = at = &atmem; at2 != NULL;
159                at2 = at2->next)
160             {
161               ++naddrs;
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.  */
165               if (at2->family == AF_INET)
166                 addrslen += INADDRSZ;
167               else if (at2->family == AF_INET6)
168                 addrslen += IN6ADDRSZ;
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)
178             dataset = (struct dataset *) mempool_alloc (db, total
179                                                         + req->key_len, 1);
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             }
191
192           /* Fill in the address and address families.  */
193           char *addrs = dataset->strdata;
194           uint8_t *family = (uint8_t *) (addrs + addrslen);
195
196           for (const struct gaih_addrtuple *at2 = at; at2 != NULL;
197                at2 = at2->next)
198             {
199               *family++ = at2->family;
200               if (at2->family == AF_INET)
201                 addrs = mempcpy (addrs, at2->addr, INADDRSZ);
202               else if (at2->family == AF_INET6)
203                 addrs = mempcpy (addrs, at2->addr, IN6ADDRSZ);
204             }
205
206           cp = family;
207         }
208       else
209         {
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
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,
227                                              tmpbuf6len, &rc6, &herrno, &ttl,
228                                              &canon));
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
250           /* Next collect IPv4 information.  */
251           while (1)
252             {
253               rc4 = 0;
254               status[1] = DL_CALL_FCT (fct, (key, AF_INET, &th[1], tmpbuf4,
255                                              tmpbuf4len, &rc4, &herrno,
256                                              ttl == INT32_MAX ? &ttl : NULL,
257                                              canon == NULL ? &canon : NULL));
258               if (rc4 != ERANGE || herrno != NETDB_INTERNAL)
259                 break;
260               tmpbuf4 = extend_alloca (tmpbuf4, tmpbuf4len, 2 * tmpbuf4len);
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                   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                 {
301                   struct hostent *hstent = NULL;
302                   int herrno;
303                   struct hostent hstent_mem;
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
315                     {
316                       addr = th[0].h_addr_list[0];
317                       addrlen = sizeof (struct in6_addr);
318                       addrfamily = AF_INET6;
319                     }
320
321                   size_t tmpbuflen = 512;
322                   char *tmpbuf = alloca (tmpbuflen);
323                   int rc;
324                   while (1)
325                     {
326                       rc = __gethostbyaddr2_r (addr, addrlen, addrfamily,
327                                                &hstent_mem, tmpbuf, tmpbuflen,
328                                                &hstent, &herrno, NULL);
329                       if (rc != ERANGE || herrno != NETDB_INTERNAL)
330                         break;
331                       tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
332                                               tmpbuflen * 2);
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       /* 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;
389
390       /* Compute the timeout time.  */
391       dataset->head.ttl = ttl == INT32_MAX ? db->postimeout : ttl;
392       timeout = dataset->head.timeout = time (NULL) + dataset->head.ttl;
393
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;
400
401       if (canon != NULL)
402         cp = mempcpy (cp, canon, canonlen);
403
404       key_copy = memcpy (cp, key, req->key_len);
405
406       assert (cp == (char *) dataset + total);
407
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;
424               dh->ttl = dataset->head.ttl;
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,
433                                                     1);
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;
441                 }
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);
453
454 #ifdef HAVE_SENDFILE
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);
459               assert ((char *) dataset - (char *) db->head + total
460                       <= (sizeof (struct database_pers_head)
461                           + db->head->module * sizeof (ref_t)
462                           + db->head->data_size));
463 # ifndef __ASSUME_SENDFILE
464               ssize_t written;
465               written =
466 # endif
467                 sendfileall (fd, db->wr_fd, (char *) &dataset->resp
468                              - (char *) db->head, dataset->head.recsize);
469 # ifndef __ASSUME_SENDFILE
470               if (written == -1 && errno == ENOSYS)
471                 goto use_write;
472 # endif
473             }
474           else
475 # ifndef __ASSUME_SENDFILE
476           use_write:
477 # endif
478 #endif
479             writeall (fd, &dataset->resp, dataset->head.recsize);
480         }
481
482       goto out;
483
484 next_nip:
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;
503
504       /* Reload with the same time-to-live value.  */
505       timeout = dh->timeout = time (NULL) + dh->ttl;
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)
514         TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
515
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))
519         {
520           /* Mark the old entry as obsolete.  */
521           if (dh != NULL)
522             dh->usable = false;
523           dataset = NULL;
524         }
525       else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
526                                               + req->key_len), 1)) != NULL)
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.  */
535           timeout = dataset->head.timeout = time (NULL) + db->negtimeout;
536           dataset->head.ttl = db->negtimeout;
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         }
544    }
545
546  out:
547   _res.options |= old_res_options & RES_USE_INET6;
548
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
561       (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
562                         true, db, uid, he == NULL);
563
564       pthread_rwlock_unlock (&db->lock);
565
566       /* Mark the old entry as obsolete.  */
567       if (dh != NULL)
568         dh->usable = false;
569     }
570
571   return timeout;
572 }
573
574
575 void
576 addhstai (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
583 time_t
584 readdhstai (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
592   return addhstaiX (db, -1, &req, db->data + he->key, he->owner, he, dh);
593 }