Avoid warnings for unused results in nscd/connections.c.
[platform/upstream/glibc.git] / nscd / aicache.c
1 /* Cache handling for host lookup.
2    Copyright (C) 2004-2014 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/res_hconf.h>
29
30 #include "dbg_log.h"
31 #include "nscd.h"
32 #ifdef HAVE_SENDFILE
33 # include <kernel-features.h>
34 #endif
35
36
37 typedef 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);
41 typedef enum nss_status (*nss_gethostbyname3_r)
42   (const char *name, int af, struct hostent *host,
43    char *buffer, size_t buflen, int *errnop,
44    int *h_errnop, int32_t *, char **);
45 typedef 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
50 static 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
61 static time_t
62 addhstaiX (struct database_dyn *db, int fd, request_header *req,
63            void *key, uid_t uid, struct hashentry *const he,
64            struct datahead *dh)
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.  */
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
80   if (__glibc_unlikely (debug_level > 0))
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
88   static service_user *hosts_database;
89   service_user *nip;
90   int no_more;
91   int rc6 = 0;
92   int rc4 = 0;
93   int herrno = 0;
94
95   if (hosts_database == NULL)
96     no_more = __nss_database_lookup ("hosts", NULL,
97                                      "dns [!UNAVAIL=return] files",
98                                      &hosts_database);
99   else
100     no_more = 0;
101   nip = hosts_database;
102
103   /* Initialize configurations.  */
104   if (__glibc_unlikely (!_res_hconf.initialized))
105     _res_hconf_init ();
106   if (__res_maybe_init (&_res, 0) == -1)
107     no_more = 1;
108
109   /* If we are looking for both IPv4 and IPv6 address we don't want
110      the lookup functions to automatically promote IPv4 addresses to
111      IPv6 addresses.  Currently this is decided by setting the
112      RES_USE_INET6 bit in _res.options.  */
113   int old_res_options = _res.options;
114   _res.options &= ~RES_USE_INET6;
115
116   size_t tmpbuf6len = 1024;
117   char *tmpbuf6 = alloca (tmpbuf6len);
118   size_t tmpbuf4len = 0;
119   char *tmpbuf4 = NULL;
120   int32_t ttl = INT32_MAX;
121   ssize_t total = 0;
122   char *key_copy = NULL;
123   bool alloca_used = false;
124   time_t timeout = MAX_TIMEOUT_VALUE;
125
126   while (!no_more)
127     {
128       void *cp;
129       int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL };
130       int naddrs = 0;
131       size_t addrslen = 0;
132       char *canon = NULL;
133       size_t canonlen;
134
135       nss_gethostbyname4_r fct4 = __nss_lookup_function (nip,
136                                                          "gethostbyname4_r");
137       if (fct4 != NULL)
138         {
139           struct gaih_addrtuple atmem;
140           struct gaih_addrtuple *at;
141           while (1)
142             {
143               at = &atmem;
144               rc6 = 0;
145               herrno = 0;
146               status[1] = DL_CALL_FCT (fct4, (key, &at, tmpbuf6, tmpbuf6len,
147                                               &rc6, &herrno, &ttl));
148               if (rc6 != ERANGE || (herrno != NETDB_INTERNAL
149                                     && herrno != TRY_AGAIN))
150                 break;
151               tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len);
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], tmpbuf6,
230                                              tmpbuf6len, &rc6, &herrno, &ttl,
231                                              &canon));
232               if (rc6 != ERANGE || herrno != NETDB_INTERNAL)
233                 break;
234               tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len);
235             }
236
237           if (rc6 != 0 && herrno == NETDB_INTERNAL)
238             goto out;
239
240           /* If the IPv6 lookup has been successful do not use the
241              buffer used in that lookup, use a new one.  */
242           if (status[0] == NSS_STATUS_SUCCESS && rc6 == 0)
243             {
244               tmpbuf4len = 512;
245               tmpbuf4 = alloca (tmpbuf4len);
246             }
247           else
248             {
249               tmpbuf4len = tmpbuf6len;
250               tmpbuf4 = tmpbuf6;
251             }
252
253           /* Next collect IPv4 information.  */
254           while (1)
255             {
256               rc4 = 0;
257               status[1] = DL_CALL_FCT (fct, (key, AF_INET, &th[1], tmpbuf4,
258                                              tmpbuf4len, &rc4, &herrno,
259                                              ttl == INT32_MAX ? &ttl : NULL,
260                                              canon == NULL ? &canon : NULL));
261               if (rc4 != ERANGE || herrno != NETDB_INTERNAL)
262                 break;
263               tmpbuf4 = extend_alloca (tmpbuf4, tmpbuf4len, 2 * tmpbuf4len);
264             }
265
266           if (rc4 != 0 && herrno == NETDB_INTERNAL)
267             goto out;
268
269           if (status[0] != NSS_STATUS_SUCCESS
270               && status[1] != NSS_STATUS_SUCCESS)
271             goto next_nip;
272
273           /* We found the data.  Count the addresses and the size.  */
274           for (int j = 0; j < 2; ++j)
275             if (status[j] == NSS_STATUS_SUCCESS)
276               for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
277                 {
278                   ++naddrs;
279                   addrslen += th[j].h_length;
280                 }
281
282           if (canon == NULL)
283             {
284               /* Determine the canonical name.  */
285               nss_getcanonname_r cfct;
286               cfct = __nss_lookup_function (nip, "getcanonname_r");
287               if (cfct != NULL)
288                 {
289                   const size_t max_fqdn_len = 256;
290                   char *buf = alloca (max_fqdn_len);
291                   char *s;
292                   int rc;
293
294                   if (DL_CALL_FCT (cfct, (key, buf, max_fqdn_len, &s,
295                                           &rc, &herrno))
296                       == NSS_STATUS_SUCCESS)
297                     canon = s;
298                   else
299                     /* Set to name now to avoid using gethostbyaddr.  */
300                     canon = key;
301                 }
302               else
303                 {
304                   struct hostent *hstent = NULL;
305                   int herrno;
306                   struct hostent hstent_mem;
307                   void *addr;
308                   size_t addrlen;
309                   int addrfamily;
310
311                   if (status[1] == NSS_STATUS_SUCCESS)
312                     {
313                       addr = th[1].h_addr_list[0];
314                       addrlen = sizeof (struct in_addr);
315                       addrfamily = AF_INET;
316                     }
317                   else
318                     {
319                       addr = th[0].h_addr_list[0];
320                       addrlen = sizeof (struct in6_addr);
321                       addrfamily = AF_INET6;
322                     }
323
324                   size_t tmpbuflen = 512;
325                   char *tmpbuf = alloca (tmpbuflen);
326                   int rc;
327                   while (1)
328                     {
329                       rc = __gethostbyaddr2_r (addr, addrlen, addrfamily,
330                                                &hstent_mem, tmpbuf, tmpbuflen,
331                                                &hstent, &herrno, NULL);
332                       if (rc != ERANGE || herrno != NETDB_INTERNAL)
333                         break;
334                       tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
335                                               tmpbuflen * 2);
336                     }
337
338                   if (rc == 0)
339                     {
340                       if (hstent != NULL)
341                         canon = hstent->h_name;
342                       else
343                         canon = key;
344                     }
345                 }
346             }
347
348           canonlen = canon == NULL ? 0 : (strlen (canon) + 1);
349
350           total = sizeof (*dataset) + naddrs + addrslen + canonlen;
351
352
353           /* Now we can allocate the data structure.  If the TTL of the
354              entry is reported as zero do not cache the entry at all.  */
355           if (ttl != 0 && he == NULL)
356             dataset = (struct dataset *) mempool_alloc (db, total
357                                                         + req->key_len, 1);
358
359           if (dataset == NULL)
360             {
361               /* We cannot permanently add the result in the moment.  But
362                  we can provide the result as is.  Store the data in some
363                  temporary memory.  */
364               dataset = (struct dataset *) alloca (total + req->key_len);
365
366               /* We cannot add this record to the permanent database.  */
367               alloca_used = true;
368             }
369
370           /* Fill in the address and address families.  */
371           char *addrs = dataset->strdata;
372           uint8_t *family = (uint8_t *) (addrs + addrslen);
373
374           for (int j = 0; j < 2; ++j)
375             if (status[j] == NSS_STATUS_SUCCESS)
376               for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
377                 {
378                   addrs = mempcpy (addrs, th[j].h_addr_list[i],
379                                    th[j].h_length);
380                   *family++ = th[j].h_addrtype;
381                 }
382
383           cp = family;
384         }
385
386       timeout = datahead_init_pos (&dataset->head, total + req->key_len,
387                                    total - offsetof (struct dataset, resp),
388                                    he == NULL ? 0 : dh->nreloads + 1,
389                                    ttl == INT32_MAX ? db->postimeout : ttl);
390
391       /* Fill in the rest of the dataset.  */
392       dataset->resp.version = NSCD_VERSION;
393       dataset->resp.found = 1;
394       dataset->resp.naddrs = naddrs;
395       dataset->resp.addrslen = addrslen;
396       dataset->resp.canonlen = canonlen;
397       dataset->resp.error = NETDB_SUCCESS;
398
399       if (canon != NULL)
400         cp = mempcpy (cp, canon, canonlen);
401
402       key_copy = memcpy (cp, key, req->key_len);
403
404       assert (cp == (char *) dataset + total);
405
406       /* Now we can determine whether on refill we have to create a
407          new record or not.  */
408       if (he != NULL)
409         {
410           assert (fd == -1);
411
412           if (total + req->key_len == dh->allocsize
413               && total - offsetof (struct dataset, resp) == dh->recsize
414               && memcmp (&dataset->resp, dh->data,
415                          dh->allocsize - offsetof (struct dataset,
416                                                    resp)) == 0)
417             {
418               /* The data has not changed.  We will just bump the
419                  timeout value.  Note that the new record has been
420                  allocated on the stack and need not be freed.  */
421               dh->timeout = dataset->head.timeout;
422               dh->ttl = dataset->head.ttl;
423               ++dh->nreloads;
424             }
425           else
426             {
427               /* We have to create a new record.  Just allocate
428                  appropriate memory and copy it.  */
429               struct dataset *newp
430                 = (struct dataset *) mempool_alloc (db, total + req->key_len,
431                                                     1);
432               if (__glibc_likely (newp != NULL))
433                 {
434                   /* Adjust pointer into the memory block.  */
435                   key_copy = (char *) newp + (key_copy - (char *) dataset);
436
437                   dataset = memcpy (newp, dataset, total + req->key_len);
438                   alloca_used = false;
439                 }
440
441               /* Mark the old record as obsolete.  */
442               dh->usable = false;
443             }
444         }
445       else
446         {
447           /* We write the dataset before inserting it to the database
448              since while inserting this thread might block and so
449              would unnecessarily let the receiver wait.  */
450           assert (fd != -1);
451
452 #ifdef HAVE_SENDFILE
453           if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
454             {
455               assert (db->wr_fd != -1);
456               assert ((char *) &dataset->resp > (char *) db->data);
457               assert ((char *) dataset - (char *) db->head + total
458                       <= (sizeof (struct database_pers_head)
459                           + db->head->module * sizeof (ref_t)
460                           + db->head->data_size));
461 # ifndef __ASSUME_SENDFILE
462               ssize_t written;
463               written =
464 # endif
465                 sendfileall (fd, db->wr_fd, (char *) &dataset->resp
466                              - (char *) db->head, dataset->head.recsize);
467 # ifndef __ASSUME_SENDFILE
468               if (written == -1 && errno == ENOSYS)
469                 goto use_write;
470 # endif
471             }
472           else
473 # ifndef __ASSUME_SENDFILE
474           use_write:
475 # endif
476 #endif
477             writeall (fd, &dataset->resp, dataset->head.recsize);
478         }
479
480       goto out;
481
482 next_nip:
483       if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN)
484         break;
485
486       if (nip->next == NULL)
487         no_more = -1;
488       else
489         nip = nip->next;
490     }
491
492   /* No result found.  Create a negative result record.  */
493   if (he != NULL && rc4 == EAGAIN)
494     {
495       /* If we have an old record available but cannot find one now
496          because the service is not available we keep the old record
497          and make sure it does not get removed.  */
498       if (reload_count != UINT_MAX && dh->nreloads == reload_count)
499         /* Do not reset the value if we never not reload the record.  */
500         dh->nreloads = reload_count - 1;
501
502       /* Reload with the same time-to-live value.  */
503       timeout = dh->timeout = time (NULL) + dh->ttl;
504     }
505   else
506     {
507       /* We have no data.  This means we send the standard reply for
508          this case.  */
509       total = sizeof (notfound);
510
511       if (fd != -1)
512         TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
513
514       /* If we have a transient error or cannot permanently store the
515          result, so be it.  */
516       if (rc4 == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
517         {
518           /* Mark the old entry as obsolete.  */
519           if (dh != NULL)
520             dh->usable = false;
521           dataset = NULL;
522         }
523       else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
524                                               + req->key_len), 1)) != NULL)
525         {
526           timeout = datahead_init_neg (&dataset->head,
527                                        sizeof (struct dataset) + req->key_len,
528                                        total, db->negtimeout);
529
530           /* This is the reply.  */
531           memcpy (&dataset->resp, &notfound, total);
532
533           /* Copy the key data.  */
534           key_copy = memcpy (dataset->strdata, key, req->key_len);
535         }
536    }
537
538  out:
539   _res.options |= old_res_options & RES_USE_INET6;
540
541   if (dataset != NULL && !alloca_used)
542     {
543       /* If necessary, we also propagate the data to disk.  */
544       if (db->persistent)
545         {
546           // XXX async OK?
547           uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
548           msync ((void *) pval,
549                  ((uintptr_t) dataset & pagesize_m1) + total + req->key_len,
550                  MS_ASYNC);
551         }
552
553       (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
554                         true, db, uid, he == NULL);
555
556       pthread_rwlock_unlock (&db->lock);
557
558       /* Mark the old entry as obsolete.  */
559       if (dh != NULL)
560         dh->usable = false;
561     }
562
563   return timeout;
564 }
565
566
567 void
568 addhstai (struct database_dyn *db, int fd, request_header *req, void *key,
569           uid_t uid)
570 {
571   addhstaiX (db, fd, req, key, uid, NULL, NULL);
572 }
573
574
575 time_t
576 readdhstai (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
577 {
578   request_header req =
579     {
580       .type = GETAI,
581       .key_len = he->len
582     };
583
584   return addhstaiX (db, -1, &req, db->data + he->key, he->owner, he, dh);
585 }