Back to index

glibc  2.9
hstcache.c
Go to the documentation of this file.
00001 /* Cache handling for host lookup.
00002    Copyright (C) 1998-2005, 2006, 2007, 2008 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published
00008    by the Free Software Foundation; version 2 of the License, or
00009    (at your option) any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software Foundation,
00018    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00019 
00020 #include <alloca.h>
00021 #include <assert.h>
00022 #include <errno.h>
00023 #include <error.h>
00024 #include <libintl.h>
00025 #include <netdb.h>
00026 #include <stdbool.h>
00027 #include <stddef.h>
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #include <time.h>
00032 #include <unistd.h>
00033 #include <arpa/inet.h>
00034 #include <arpa/nameser.h>
00035 #include <sys/mman.h>
00036 #include <stackinfo.h>
00037 
00038 #include "nscd.h"
00039 #include "dbg_log.h"
00040 #ifdef HAVE_SENDFILE
00041 # include <kernel-features.h>
00042 #endif
00043 
00044 
00045 /* This is the standard reply in case the service is disabled.  */
00046 static const hst_response_header disabled =
00047 {
00048   .version = NSCD_VERSION,
00049   .found = -1,
00050   .h_name_len = 0,
00051   .h_aliases_cnt = 0,
00052   .h_addrtype = -1,
00053   .h_length = -1,
00054   .h_addr_list_cnt = 0,
00055   .error = NETDB_INTERNAL
00056 };
00057 
00058 /* This is the struct describing how to write this record.  */
00059 const struct iovec hst_iov_disabled =
00060 {
00061   .iov_base = (void *) &disabled,
00062   .iov_len = sizeof (disabled)
00063 };
00064 
00065 
00066 /* This is the standard reply in case we haven't found the dataset.  */
00067 static const hst_response_header notfound =
00068 {
00069   .version = NSCD_VERSION,
00070   .found = 0,
00071   .h_name_len = 0,
00072   .h_aliases_cnt = 0,
00073   .h_addrtype = -1,
00074   .h_length = -1,
00075   .h_addr_list_cnt = 0,
00076   .error = HOST_NOT_FOUND
00077 };
00078 
00079 
00080 static void
00081 cache_addhst (struct database_dyn *db, int fd, request_header *req,
00082              const void *key, struct hostent *hst, uid_t owner,
00083              struct hashentry *he, struct datahead *dh, int errval,
00084              int32_t ttl)
00085 {
00086   bool all_written = true;
00087   time_t t = time (NULL);
00088 
00089   /* We allocate all data in one memory block: the iov vector,
00090      the response header and the dataset itself.  */
00091   struct dataset
00092   {
00093     struct datahead head;
00094     hst_response_header resp;
00095     char strdata[0];
00096   } *dataset;
00097 
00098   assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
00099 
00100   if (hst == NULL)
00101     {
00102       if (he != NULL && errval == EAGAIN)
00103        {
00104          /* If we have an old record available but cannot find one
00105             now because the service is not available we keep the old
00106             record and make sure it does not get removed.  */
00107          if (reload_count != UINT_MAX)
00108            /* Do not reset the value if we never not reload the record.  */
00109            dh->nreloads = reload_count - 1;
00110        }
00111       else
00112        {
00113          /* We have no data.  This means we send the standard reply for this
00114             case.  */
00115          ssize_t total = sizeof (notfound);
00116 
00117          if (fd != -1 &&
00118              TEMP_FAILURE_RETRY (send (fd, &notfound, total,
00119                                    MSG_NOSIGNAL)) != total)
00120            all_written = false;
00121 
00122          dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len,
00123                                IDX_result_data);
00124          /* If we cannot permanently store the result, so be it.  */
00125          if (dataset != NULL)
00126            {
00127              dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
00128              dataset->head.recsize = total;
00129              dataset->head.notfound = true;
00130              dataset->head.nreloads = 0;
00131              dataset->head.usable = true;
00132 
00133              /* Compute the timeout time.  */
00134              dataset->head.timeout = t + (ttl == INT32_MAX
00135                                       ? db->negtimeout : ttl);
00136 
00137              /* This is the reply.  */
00138              memcpy (&dataset->resp, &notfound, total);
00139 
00140              /* Copy the key data.  */
00141              memcpy (dataset->strdata, key, req->key_len);
00142 
00143              /* If necessary, we also propagate the data to disk.  */
00144              if (db->persistent)
00145               {
00146                 // XXX async OK?
00147                 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
00148                 msync ((void *) pval,
00149                       ((uintptr_t) dataset & pagesize_m1)
00150                       + sizeof (struct dataset) + req->key_len, MS_ASYNC);
00151               }
00152 
00153              /* Now get the lock to safely insert the records.  */
00154              pthread_rwlock_rdlock (&db->lock);
00155 
00156              (void) cache_add (req->type, &dataset->strdata, req->key_len,
00157                             &dataset->head, true, db, owner, he == NULL);
00158 
00159              pthread_rwlock_unlock (&db->lock);
00160 
00161              /* Mark the old entry as obsolete.  */
00162              if (dh != NULL)
00163               dh->usable = false;
00164            }
00165          else
00166            ++db->head->addfailed;
00167        }
00168     }
00169   else
00170     {
00171       /* Determine the I/O structure.  */
00172       size_t h_name_len = strlen (hst->h_name) + 1;
00173       size_t h_aliases_cnt;
00174       uint32_t *h_aliases_len;
00175       size_t h_addr_list_cnt;
00176       int addr_list_type;
00177       char *addresses;
00178       char *aliases;
00179       char *key_copy = NULL;
00180       char *cp;
00181       size_t cnt;
00182       ssize_t total;
00183 
00184       /* Determine the number of aliases.  */
00185       h_aliases_cnt = 0;
00186       for (cnt = 0; hst->h_aliases[cnt] != NULL; ++cnt)
00187        ++h_aliases_cnt;
00188       /* Determine the length of all aliases.  */
00189       h_aliases_len = (uint32_t *) alloca (h_aliases_cnt * sizeof (uint32_t));
00190       total = 0;
00191       for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
00192        {
00193          h_aliases_len[cnt] = strlen (hst->h_aliases[cnt]) + 1;
00194          total += h_aliases_len[cnt];
00195        }
00196 
00197       /* Determine the number of addresses.  */
00198       h_addr_list_cnt = 0;
00199       while (hst->h_addr_list[h_addr_list_cnt] != NULL)
00200        ++h_addr_list_cnt;
00201 
00202       if (h_addr_list_cnt == 0)
00203        /* Invalid entry.  */
00204        return;
00205 
00206       total += (sizeof (struct dataset)
00207               + h_name_len
00208               + h_aliases_cnt * sizeof (uint32_t)
00209               + h_addr_list_cnt * hst->h_length);
00210 
00211       /* If we refill the cache, first assume the reconrd did not
00212         change.  Allocate memory on the cache since it is likely
00213         discarded anyway.  If it turns out to be necessary to have a
00214         new record we can still allocate real memory.  */
00215       bool alloca_used = false;
00216       dataset = NULL;
00217 
00218       /* If the record contains more than one IP address (used for
00219         load balancing etc) don't cache the entry.  This is something
00220         the current cache handling cannot handle and it is more than
00221         questionable whether it is worthwhile complicating the cache
00222         handling just for handling such a special case. */
00223       if (he == NULL && h_addr_list_cnt == 1)
00224        {
00225          dataset = (struct dataset *) mempool_alloc (db,
00226                                                 total + req->key_len,
00227                                                 IDX_result_data);
00228          if (dataset == NULL)
00229            ++db->head->addfailed;
00230        }
00231 
00232       if (dataset == NULL)
00233        {
00234          /* We cannot permanently add the result in the moment.  But
00235             we can provide the result as is.  Store the data in some
00236             temporary memory.  */
00237          dataset = (struct dataset *) alloca (total + req->key_len);
00238 
00239          /* We cannot add this record to the permanent database.  */
00240          alloca_used = true;
00241        }
00242 
00243       dataset->head.allocsize = total + req->key_len;
00244       dataset->head.recsize = total - offsetof (struct dataset, resp);
00245       dataset->head.notfound = false;
00246       dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
00247       dataset->head.usable = true;
00248 
00249       /* Compute the timeout time.  */
00250       dataset->head.timeout = t + (ttl == INT32_MAX ? db->postimeout : ttl);
00251 
00252       dataset->resp.version = NSCD_VERSION;
00253       dataset->resp.found = 1;
00254       dataset->resp.h_name_len = h_name_len;
00255       dataset->resp.h_aliases_cnt = h_aliases_cnt;
00256       dataset->resp.h_addrtype = hst->h_addrtype;
00257       dataset->resp.h_length = hst->h_length;
00258       dataset->resp.h_addr_list_cnt = h_addr_list_cnt;
00259       dataset->resp.error = NETDB_SUCCESS;
00260 
00261       /* Make sure there is no gap.  */
00262       assert ((char *) (&dataset->resp.error + 1) == dataset->strdata);
00263 
00264       cp = dataset->strdata;
00265 
00266       cp = mempcpy (cp, hst->h_name, h_name_len);
00267       cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (uint32_t));
00268 
00269       /* The normal addresses first.  */
00270       addresses = cp;
00271       for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
00272        cp = mempcpy (cp, hst->h_addr_list[cnt], hst->h_length);
00273 
00274       /* Then the aliases.  */
00275       aliases = cp;
00276       for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
00277        cp = mempcpy (cp, hst->h_aliases[cnt], h_aliases_len[cnt]);
00278 
00279       assert (cp
00280              == dataset->strdata + total - offsetof (struct dataset,
00281                                                 strdata));
00282 
00283       /* If we are adding a GETHOSTBYNAME{,v6} entry we must be prepared
00284         that the answer we get from the NSS does not contain the key
00285         itself.  This is the case if the resolver is used and the name
00286         is extended by the domainnames from /etc/resolv.conf.  Therefore
00287         we explicitly add the name here.  */
00288       key_copy = memcpy (cp, key, req->key_len);
00289 
00290       assert ((char *) &dataset->resp + dataset->head.recsize == cp);
00291 
00292       /* Now we can determine whether on refill we have to create a new
00293         record or not.  */
00294       if (he != NULL)
00295        {
00296          assert (fd == -1);
00297 
00298          if (total + req->key_len == dh->allocsize
00299              && total - offsetof (struct dataset, resp) == dh->recsize
00300              && memcmp (&dataset->resp, dh->data,
00301                       dh->allocsize - offsetof (struct dataset, resp)) == 0)
00302            {
00303              /* The data has not changed.  We will just bump the
00304                timeout value.  Note that the new record has been
00305                allocated on the stack and need not be freed.  */
00306              assert (h_addr_list_cnt == 1);
00307              dh->timeout = dataset->head.timeout;
00308              ++dh->nreloads;
00309            }
00310          else
00311            {
00312              if (h_addr_list_cnt == 1)
00313               {
00314                 /* We have to create a new record.  Just allocate
00315                    appropriate memory and copy it.  */
00316                 struct dataset *newp
00317                   = (struct dataset *) mempool_alloc (db,
00318                                                  total + req->key_len,
00319                                                  IDX_result_data);
00320                 if (newp != NULL)
00321                   {
00322                     /* Adjust pointers into the memory block.  */
00323                     addresses = (char *) newp + (addresses
00324                                              - (char *) dataset);
00325                     aliases = (char *) newp + (aliases - (char *) dataset);
00326                     assert (key_copy != NULL);
00327                     key_copy = (char *) newp + (key_copy - (char *) dataset);
00328 
00329                     dataset = memcpy (newp, dataset, total + req->key_len);
00330                     alloca_used = false;
00331                   }
00332                 else
00333                   ++db->head->addfailed;
00334               }
00335 
00336              /* Mark the old record as obsolete.  */
00337              dh->usable = false;
00338            }
00339        }
00340       else
00341        {
00342          /* We write the dataset before inserting it to the database
00343             since while inserting this thread might block and so would
00344             unnecessarily keep the receiver waiting.  */
00345          assert (fd != -1);
00346 
00347 #ifdef HAVE_SENDFILE
00348          if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
00349            {
00350              assert (db->wr_fd != -1);
00351              assert ((char *) &dataset->resp > (char *) db->data);
00352              assert ((char *) &dataset->resp - (char *) db->head
00353                     + total
00354                     <= (sizeof (struct database_pers_head)
00355                        + db->head->module * sizeof (ref_t)
00356                        + db->head->data_size));
00357              ssize_t written = sendfileall (fd, db->wr_fd,
00358                                         (char *) &dataset->resp
00359                                         - (char *) db->head,
00360                                         dataset->head.recsize);
00361              if (written != dataset->head.recsize)
00362               {
00363 # ifndef __ASSUME_SENDFILE
00364                 if (written == -1 && errno == ENOSYS)
00365                   goto use_write;
00366 # endif
00367                 all_written = false;
00368               }
00369            }
00370          else
00371 # ifndef __ASSUME_SENDFILE
00372          use_write:
00373 # endif
00374 #endif
00375            if (writeall (fd, &dataset->resp, dataset->head.recsize)
00376               != dataset->head.recsize)
00377              all_written = false;
00378        }
00379 
00380       /* Add the record to the database.  But only if it has not been
00381         stored on the stack.
00382 
00383         If the record contains more than one IP address (used for
00384         load balancing etc) don't cache the entry.  This is something
00385         the current cache handling cannot handle and it is more than
00386         questionable whether it is worthwhile complicating the cache
00387         handling just for handling such a special case. */
00388       if (! alloca_used)
00389        {
00390          /* If necessary, we also propagate the data to disk.  */
00391          if (db->persistent)
00392            {
00393              // XXX async OK?
00394              uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
00395              msync ((void *) pval,
00396                    ((uintptr_t) dataset & pagesize_m1)
00397                    + total + req->key_len, MS_ASYNC);
00398            }
00399 
00400          addr_list_type = (hst->h_length == NS_INADDRSZ
00401                          ? GETHOSTBYADDR : GETHOSTBYADDRv6);
00402 
00403          /* Now get the lock to safely insert the records.  */
00404          pthread_rwlock_rdlock (&db->lock);
00405 
00406          /* NB: the following code is really complicated.  It has
00407             seemlingly duplicated code paths which do the same.  The
00408             problem is that we always must add the hash table entry
00409             with the FIRST flag set first.  Otherwise we get dangling
00410             pointers in case memory allocation fails.  */
00411          assert (hst->h_addr_list[1] == NULL);
00412 
00413          /* Avoid adding names if more than one address is available.  See
00414             above for more info.  */
00415          assert (req->type == GETHOSTBYNAME
00416                 || req->type == GETHOSTBYNAMEv6
00417                 || req->type == GETHOSTBYADDR
00418                 || req->type == GETHOSTBYADDRv6);
00419 
00420          (void) cache_add (req->type, key_copy, req->key_len,
00421                          &dataset->head, true, db, owner, he == NULL);
00422 
00423          pthread_rwlock_unlock (&db->lock);
00424        }
00425     }
00426 
00427   if (__builtin_expect (!all_written, 0) && debug_level > 0)
00428     {
00429       char buf[256];
00430       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
00431               strerror_r (errno, buf, sizeof (buf)));
00432     }
00433 }
00434 
00435 
00436 static int
00437 lookup (int type, void *key, struct hostent *resultbufp, char *buffer,
00438        size_t buflen, struct hostent **hst, int32_t *ttlp)
00439 {
00440   if (type == GETHOSTBYNAME)
00441     return __gethostbyname3_r (key, AF_INET, resultbufp, buffer, buflen, hst,
00442                             &h_errno, ttlp, NULL);
00443   if (type == GETHOSTBYNAMEv6)
00444     return __gethostbyname3_r (key, AF_INET6, resultbufp, buffer, buflen, hst,
00445                             &h_errno, ttlp, NULL);
00446   if (type == GETHOSTBYADDR)
00447     return __gethostbyaddr2_r (key, NS_INADDRSZ, AF_INET, resultbufp, buffer,
00448                             buflen, hst, &h_errno, ttlp);
00449   return __gethostbyaddr2_r (key, NS_IN6ADDRSZ, AF_INET6, resultbufp, buffer,
00450                           buflen, hst, &h_errno, ttlp);
00451 }
00452 
00453 
00454 static void
00455 addhstbyX (struct database_dyn *db, int fd, request_header *req,
00456           void *key, uid_t uid, struct hashentry *he, struct datahead *dh)
00457 {
00458   /* Search for the entry matching the key.  Please note that we don't
00459      look again in the table whether the dataset is now available.  We
00460      simply insert it.  It does not matter if it is in there twice.  The
00461      pruning function only will look at the timestamp.  */
00462   int buflen = 1024;
00463   char *buffer = (char *) alloca (buflen);
00464   struct hostent resultbuf;
00465   struct hostent *hst;
00466   bool use_malloc = false;
00467   int errval = 0;
00468   int32_t ttl = INT32_MAX;
00469 
00470   if (__builtin_expect (debug_level > 0, 0))
00471     {
00472       const char *str;
00473       char buf[INET6_ADDRSTRLEN + 1];
00474       if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
00475        str = key;
00476       else
00477        str = inet_ntop (req->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
00478                       key, buf, sizeof (buf));
00479 
00480       if (he == NULL)
00481        dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) str);
00482       else
00483        dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) str);
00484     }
00485 
00486   while (lookup (req->type, key, &resultbuf, buffer, buflen, &hst, &ttl) != 0
00487         && h_errno == NETDB_INTERNAL
00488         && (errval = errno) == ERANGE)
00489     {
00490       errno = 0;
00491 
00492       if (__builtin_expect (buflen > 32768, 0))
00493        {
00494          char *old_buffer = buffer;
00495          buflen *= 2;
00496          buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
00497          if (buffer == NULL)
00498            {
00499              /* We ran out of memory.  We cannot do anything but
00500                sending a negative response.  In reality this should
00501                never happen.  */
00502              hst = NULL;
00503              buffer = old_buffer;
00504 
00505              /* We set the error to indicate this is (possibly) a
00506                temporary error and that it does not mean the entry
00507                is not available at all.  */
00508              errval = EAGAIN;
00509              break;
00510            }
00511          use_malloc = true;
00512        }
00513       else
00514        /* Allocate a new buffer on the stack.  If possible combine it
00515           with the previously allocated buffer.  */
00516        buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
00517     }
00518 
00519   cache_addhst (db, fd, req, key, hst, uid, he, dh,
00520               h_errno == TRY_AGAIN ? errval : 0, ttl);
00521 
00522   if (use_malloc)
00523     free (buffer);
00524 }
00525 
00526 
00527 void
00528 addhstbyname (struct database_dyn *db, int fd, request_header *req,
00529              void *key, uid_t uid)
00530 {
00531   addhstbyX (db, fd, req, key, uid, NULL, NULL);
00532 }
00533 
00534 
00535 void
00536 readdhstbyname (struct database_dyn *db, struct hashentry *he,
00537               struct datahead *dh)
00538 {
00539   request_header req =
00540     {
00541       .type = GETHOSTBYNAME,
00542       .key_len = he->len
00543     };
00544 
00545   addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
00546 }
00547 
00548 
00549 void
00550 addhstbyaddr (struct database_dyn *db, int fd, request_header *req,
00551              void *key, uid_t uid)
00552 {
00553   addhstbyX (db, fd, req, key, uid, NULL, NULL);
00554 }
00555 
00556 
00557 void
00558 readdhstbyaddr (struct database_dyn *db, struct hashentry *he,
00559               struct datahead *dh)
00560 {
00561   request_header req =
00562     {
00563       .type = GETHOSTBYADDR,
00564       .key_len = he->len
00565     };
00566 
00567   addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
00568 }
00569 
00570 
00571 void
00572 addhstbynamev6 (struct database_dyn *db, int fd, request_header *req,
00573               void *key, uid_t uid)
00574 {
00575   addhstbyX (db, fd, req, key, uid, NULL, NULL);
00576 }
00577 
00578 
00579 void
00580 readdhstbynamev6 (struct database_dyn *db, struct hashentry *he,
00581                 struct datahead *dh)
00582 {
00583   request_header req =
00584     {
00585       .type = GETHOSTBYNAMEv6,
00586       .key_len = he->len
00587     };
00588 
00589   addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
00590 }
00591 
00592 
00593 void
00594 addhstbyaddrv6 (struct database_dyn *db, int fd, request_header *req,
00595               void *key, uid_t uid)
00596 {
00597   addhstbyX (db, fd, req, key, uid, NULL, NULL);
00598 }
00599 
00600 
00601 void
00602 readdhstbyaddrv6 (struct database_dyn *db, struct hashentry *he,
00603                 struct datahead *dh)
00604 {
00605   request_header req =
00606     {
00607       .type = GETHOSTBYADDRv6,
00608       .key_len = he->len
00609     };
00610 
00611   addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
00612 }