From d19687d6ebc545b633e14c07429f7892a599d0b9 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 15 Sep 2004 08:25:49 +0000 Subject: Update. 2004-09-15 Ulrich Drepper * nscd/Makefile (rountines): Add nscd_getai. (nscd-modules): Add aicache. * nscd/aicache.c: New file. * nscd/nscd_getai.c: New file. * nscd/cache.c (prune_cache): Handle GETAI request type. * nscd/connections.c: Add GETAI support in request handling. * nscd/nscd-client.h (request_type): Add GETAI. Define ai_response_header and struct nscd_ai_result types. (struct datahead): Add aidata field. Declare __nscd_getai. * nscd/nscd.c: Add getaddrinfo definition to catch problems. * nscd/nscd.h: Declare addhstai and readdhstai. * sysdeps/posix/getaddrinfo.c: Add support for using cached results. * nscd/nscd-client.h (struct datahead): Use uint8_t instead of bool. --- ChangeLog | 19 ++ misc/sys/cdefs.h | 12 +- nscd/Makefile | 4 +- nscd/aicache.c | 428 ++++++++++++++++++++++++++++++++++++++++++++ nscd/cache.c | 4 + nscd/connections.c | 13 +- nscd/nscd-client.h | 32 +++- nscd/nscd.c | 11 ++ nscd/nscd.h | 5 + nscd/nscd_getai.c | 164 +++++++++++++++++ sysdeps/posix/getaddrinfo.c | 201 +++++++++++++++++++-- 11 files changed, 863 insertions(+), 30 deletions(-) create mode 100644 nscd/aicache.c create mode 100644 nscd/nscd_getai.c diff --git a/ChangeLog b/ChangeLog index 9a8215a97f..1d96d3ca9d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2004-09-15 Ulrich Drepper + + * nscd/Makefile (rountines): Add nscd_getai. + (nscd-modules): Add aicache. + * nscd/aicache.c: New file. + * nscd/nscd_getai.c: New file. + * nscd/cache.c (prune_cache): Handle GETAI request type. + * nscd/connections.c: Add GETAI support in request handling. + * nscd/nscd-client.h (request_type): Add GETAI. + Define ai_response_header and struct nscd_ai_result types. + (struct datahead): Add aidata field. + Declare __nscd_getai. + * nscd/nscd.c: Add getaddrinfo definition to catch problems. + * nscd/nscd.h: Declare addhstai and readdhstai. + + * sysdeps/posix/getaddrinfo.c: Add support for using cached results. + + * nscd/nscd-client.h (struct datahead): Use uint8_t instead of bool. + 2004-09-14 Ulrich Drepper * misc/sys/cdefs.h: Remove debugging text from __P and __PMT. diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h index 1449d2e502..1a9b0e78a1 100644 --- a/misc/sys/cdefs.h +++ b/misc/sys/cdefs.h @@ -54,11 +54,6 @@ # define __NTH(fct) fct # endif # endif -/* These two macros are not usde in glibc anymore. They are kept here - only because some other projects expect the macros to be - defined. */ -# define __P(args) args -# define __PMT(args) args #else /* Not GCC. */ @@ -66,8 +61,6 @@ # define __THROW # define __NTH(fct) fct -# define __P(args) args -# define __PMT(args) args # define __const const # define __signed signed @@ -75,6 +68,11 @@ #endif /* GCC. */ +/* These two macros are not used in glibc anymore. They are kept here + only because some other projects expect the macros to be defined. */ +#define __P(args) args +#define __PMT(args) args + /* For these things, GCC behaves the ANSI way normally, and the non-ANSI way under -traditional. */ diff --git a/nscd/Makefile b/nscd/Makefile index 7d0a9e38f1..a6a08a3ab8 100644 --- a/nscd/Makefile +++ b/nscd/Makefile @@ -21,7 +21,7 @@ # subdir := nscd -routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r +routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r nscd_getai aux := nscd_helper include ../Makeconfig @@ -32,7 +32,7 @@ vpath %.c ../locale/programs nscd-modules := nscd connections pwdcache getpwnam_r getpwuid_r grpcache \ getgrnam_r getgrgid_r hstcache gethstbyad_r gethstbynm2_r \ dbg_log nscd_conf nscd_stat cache mem nscd_setup_thread \ - xmalloc xstrdup + xmalloc xstrdup aicache ifeq ($(have-thread-library),yes) diff --git a/nscd/aicache.c b/nscd/aicache.c new file mode 100644 index 0000000000..e7489065d4 --- /dev/null +++ b/nscd/aicache.c @@ -0,0 +1,428 @@ +/* Cache handling for host lookup. + Copyright (C) 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 2004. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef enum nss_status (*nss_gethostbyname2_r) + (const char *name, int af, struct hostent *host, + char *buffer, size_t buflen, int *errnop, + int *h_errnop); +typedef enum nss_status (*nss_getcanonname_r) + (const char *name, char *buffer, size_t buflen, char **result, + int *errnop, int *h_errnop); + + +static const ai_response_header notfound = +{ + .version = NSCD_VERSION, + .found = 0, + .naddrs = 0, + .addrslen = 0, + .canonlen = 0, + .error = 0 +}; + + +static void +addhstaiX (struct database_dyn *db, int fd, request_header *req, + void *key, uid_t uid, struct hashentry *he, struct datahead *dh) +{ + /* Search for the entry matching the key. Please note that we don't + look again in the table whether the dataset is now available. We + simply insert it. It does not matter if it is in there twice. The + pruning function only will look at the timestamp. */ + uid_t oldeuid = 0; + + /* We allocate all data in one memory block: the iov vector, + the response header and the dataset itself. */ + struct dataset + { + struct datahead head; + ai_response_header resp; + char strdata[0]; + } *dataset = NULL; + + if (__builtin_expect (debug_level > 0, 0)) + { + if (he == NULL) + dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key); + else + dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key); + } + + if (db->secure) + { + oldeuid = geteuid (); + seteuid (uid); + } + + static service_user *hosts_database; + service_user *nip = NULL; + int no_more; + int rc6 = 0; + int rc4 = 0; + int herrno = 0; + + if (hosts_database != NULL) + { + nip = hosts_database; + no_more = 0; + } + else + no_more = __nss_database_lookup ("hosts", NULL, + "dns [!UNAVAIL=return] files", &nip); + + if (__res_maybe_init (&_res, 0) == -1) + no_more = 1; + + /* If we are looking for both IPv4 and IPv6 address we don't want + the lookup functions to automatically promote IPv4 addresses to + IPv6 addresses. Currently this is decided by setting the + RES_USE_INET6 bit in _res.options. */ + int old_res_options = _res.options; + _res.options &= ~RES_USE_INET6; + + size_t tmpbuf6len = 512; + char *tmpbuf6 = alloca (tmpbuf6len); + size_t tmpbuf4len = 0; + char *tmpbuf4 = NULL; + char *canon = NULL; + ssize_t total = 0; + char *key_copy = NULL; + bool alloca_used = false; + + while (!no_more) + { + nss_gethostbyname2_r fct = __nss_lookup_function (nip, + "gethostbyname2_r"); + int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL }; + + if (fct != NULL) + { + printf("fct=%p\n",fct); + struct hostent th[2]; + + /* Collect IPv6 information first. */ + while (1) + { + rc6 = 0; + status[0] = DL_CALL_FCT (fct, (key, AF_INET6, &th[0], tmpbuf6, + tmpbuf6len, &rc6, &herrno)); + if (rc6 != ERANGE || herrno != NETDB_INTERNAL) + break; + tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len); + } + + if (rc6 != 0 && herrno == NETDB_INTERNAL) + goto out; + + /* If the IPv6 lookup has been successful do not use the + buffer used in that lookup, use a new one. */ + if (status[0] == NSS_STATUS_SUCCESS && rc6 == 0) + { + tmpbuf4len = 512; + tmpbuf4 = alloca (tmpbuf4len); + } + else + { + tmpbuf4len = tmpbuf6len; + tmpbuf4 = tmpbuf6; + } + + /* Next collect IPv4 information first. */ + while (1) + { + rc4 = 0; + status[1] = DL_CALL_FCT (fct, (key, AF_INET, &th[1], tmpbuf4, + tmpbuf4len, &rc4, &herrno)); + if (rc4 != ERANGE || herrno != NETDB_INTERNAL) + break; + tmpbuf4 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len); + } + + if (rc4 != 0 || herrno == NETDB_INTERNAL) + goto out; + + if (status[0] == NSS_STATUS_SUCCESS + || status[1] == NSS_STATUS_SUCCESS) + { + /* We found the data. Count the addresses and the size. */ + int naddrs = 0; + size_t addrslen = 0; + for (int j = 0; j < 2; ++j) + if (status[j] == NSS_STATUS_SUCCESS) + for (int i = 0; th[j].h_addr_list[i] != NULL; ++i) + { + ++naddrs; + addrslen += th[j].h_length; + } + + /* Determine the canonical name. */ + nss_getcanonname_r cfct; + cfct = __nss_lookup_function (nip, "getcanonname_r"); + if (cfct != NULL) + { + const size_t max_fqdn_len = 256; + char *buf = alloca (max_fqdn_len); + char *s; + int rc; + + if (DL_CALL_FCT (cfct, (key, buf, max_fqdn_len, &s, &rc, + &herrno)) == NSS_STATUS_SUCCESS) + canon = s; + else + /* Set to name now to avoid using gethostbyaddr. */ + canon = key; + } + else + { + // XXX use gethostbyaddr + } + size_t canonlen = canon == NULL ? 0 : (strlen (canon) + 1); + + total = sizeof (*dataset) + naddrs + addrslen + canonlen; + + /* Now we can allocate the data structure. */ + if (he == NULL) + { + dataset = (struct dataset *) mempool_alloc (db, + total + + req->key_len); + if (dataset == NULL) + ++db->head->addfailed; + } + + if (dataset == NULL) + { + /* We cannot permanently add the result in the moment. But + we can provide the result as is. Store the data in some + temporary memory. */ + dataset = (struct dataset *) alloca (total + req->key_len); + + /* We cannot add this record to the permanent database. */ + alloca_used = true; + } + + dataset->head.allocsize = total + req->key_len; + dataset->head.recsize = total - offsetof (struct dataset, resp); + dataset->head.notfound = false; + dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1); + dataset->head.usable = true; + + /* Compute the timeout time. */ + dataset->head.timeout = time (NULL) + db->postimeout; + + dataset->resp.version = NSCD_VERSION; + dataset->resp.found = 1; + dataset->resp.naddrs = naddrs; + dataset->resp.addrslen = addrslen; + dataset->resp.canonlen = canonlen; + dataset->resp.error = NETDB_SUCCESS; + + char *addrs = (char *) (&dataset->resp + 1); + uint8_t *family = (uint8_t *) (addrs + addrslen); + + for (int j = 0; j < 2; ++j) + if (status[j] == NSS_STATUS_SUCCESS) + for (int i = 0; th[j].h_addr_list[i] != NULL; ++i) + { + addrs = mempcpy (addrs, th[j].h_addr_list[i], + th[j].h_length); + *family++ = th[j].h_addrtype; + } + + char *cp = family; + if (canon != NULL) + cp = mempcpy (cp, canon, canonlen); + + key_copy = memcpy (cp, key, req->key_len); + + /* Now we can determine whether on refill we have to + create a new record or not. */ + if (he != NULL) + { + assert (fd == -1); + + if (total + req->key_len == dh->allocsize + && total - offsetof (struct dataset, resp) == dh->recsize + && memcmp (&dataset->resp, dh->data, + dh->allocsize + - offsetof (struct dataset, resp)) == 0) + { + /* The data has not changed. We will just bump the + timeout value. Note that the new record has been + allocated on the stack and need not be freed. */ + dh->timeout = dataset->head.timeout; + ++dh->nreloads; + } + else + { + /* We have to create a new record. Just allocate + appropriate memory and copy it. */ + struct dataset *newp + = (struct dataset *) mempool_alloc (db, + total + + req->key_len); + if (newp != NULL) + { + /* Adjust pointer into the memory block. */ + key_copy = (char *) newp + (key_copy + - (char *) dataset); + + dataset = memcpy (newp, dataset, + total + req->key_len); + alloca_used = false; + } + + /* Mark the old record as obsolete. */ + dh->usable = false; + } + } + else + { + /* We write the dataset before inserting it to the + database since while inserting this thread might + block and so would unnecessarily let the receiver + wait. */ + assert (fd != -1); + + TEMP_FAILURE_RETRY (write (fd, &dataset->resp, total)); + } + + goto out; + } + + } + + if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN) + break; + + if (nip->next == NULL) + no_more = -1; + else + nip = nip->next; + } + + /* No result found. Create a negative result record. */ + if (he != NULL && rc4 == EAGAIN) + { + /* If we have an old record available but cannot find one now + because the service is not available we keep the old record + and make sure it does not get removed. */ + if (reload_count != UINT_MAX && dh->nreloads == reload_count) + /* Do not reset the value if we never not reload the record. */ + dh->nreloads = reload_count - 1; + } + else + { + /* We have no data. This means we send the standard reply for + this case. */ + total = sizeof (notfound); + + if (fd != -1) + TEMP_FAILURE_RETRY (write (fd, ¬found, total)); + + dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len); + /* If we cannot permanently store the result, so be it. */ + if (dataset != NULL) + { + dataset->head.allocsize = sizeof (struct dataset) + req->key_len; + dataset->head.recsize = total; + dataset->head.notfound = true; + dataset->head.nreloads = 0; + dataset->head.usable = true; + + /* Compute the timeout time. */ + dataset->head.timeout = time (NULL) + db->negtimeout; + + /* This is the reply. */ + memcpy (&dataset->resp, ¬found, total); + + /* Copy the key data. */ + key_copy = memcpy (dataset->strdata, key, req->key_len); + } + else + ++db->head->addfailed; + } + + out: + _res.options = old_res_options; + + if (db->secure) + seteuid (oldeuid); + + if (dataset != NULL && !alloca_used) + { + /* If necessary, we also propagate the data to disk. */ + if (db->persistent) + { + // XXX async OK? + uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1; + msync ((void *) pval, + ((uintptr_t) dataset & pagesize_m1) + total + req->key_len, + MS_ASYNC); + } + + /* Now get the lock to safely insert the records. */ + pthread_rwlock_rdlock (&db->lock); + + if (cache_add (req->type, key_copy, req->key_len, &dataset->head, true, + db, uid) < 0) + /* Ensure the data can be recovered. */ + dataset->head.usable = false; + + pthread_rwlock_unlock (&db->lock); + + /* Mark the old entry as obsolete. */ + if (dh != NULL) + dh->usable = false; + } +} + + +void +addhstai (struct database_dyn *db, int fd, request_header *req, void *key, + uid_t uid) +{ + addhstaiX (db, fd, req, key, uid, NULL, NULL); +} + + +void +readdhstai (struct database_dyn *db, struct hashentry *he, struct datahead *dh) +{ + request_header req = + { + .type = GETAI, + .key_len = he->len + }; + + addhstaiX (db, -1, &req, db->data + he->key, he->owner, he, dh); +} diff --git a/nscd/cache.c b/nscd/cache.c index 69c2efb4de..468b8d272a 100644 --- a/nscd/cache.c +++ b/nscd/cache.c @@ -313,6 +313,10 @@ prune_cache (struct database_dyn *table, time_t now) readdhstbyaddrv6 (table, runp, dh); break; + case GETAI: + readdhstai (table, runp, dh); + break; + default: assert (! "should never happen"); } diff --git a/nscd/connections.c b/nscd/connections.c index b95ab05b3a..fe33c76bcf 100644 --- a/nscd/connections.c +++ b/nscd/connections.c @@ -83,7 +83,8 @@ const char *serv2str[LASTREQ] = [INVALIDATE] = "INVALIDATE", [GETFDPW] = "GETFDPW", [GETFDGR] = "GETFDGR", - [GETFDHST] = "GETFDHST" + [GETFDHST] = "GETFDHST", + [GETAI] = "GETAI" }; /* The control data structures for the services. */ @@ -151,6 +152,7 @@ static struct database_dyn *const serv2db[LASTREQ] = [GETFDPW] = &dbs[pwddb], [GETFDGR] = &dbs[grpdb], [GETFDHST] = &dbs[hstdb], + [GETAI] = &dbs[hstdb], }; @@ -592,8 +594,9 @@ cannot handle old request version %d; current version is %d"), struct database_dyn *db = serv2db[req->type]; - if (__builtin_expect (req->type, GETPWBYNAME) >= GETPWBYNAME - && __builtin_expect (req->type, LASTDBREQ) <= LASTDBREQ) + if ((__builtin_expect (req->type, GETPWBYNAME) >= GETPWBYNAME + && __builtin_expect (req->type, LASTDBREQ) <= LASTDBREQ) + || req->type == GETAI) { if (__builtin_expect (debug_level, 0) > 0) { @@ -702,6 +705,10 @@ cannot handle old request version %d; current version is %d"), addhstbyaddrv6 (db, fd, req, key, uid); break; + case GETAI: + addhstai (db, fd, req, key, uid); + break; + case GETSTAT: case SHUTDOWN: case INVALIDATE: diff --git a/nscd/nscd-client.h b/nscd/nscd-client.h index 8f3f3a11d7..1a0dd732db 100644 --- a/nscd/nscd-client.h +++ b/nscd/nscd-client.h @@ -61,6 +61,7 @@ typedef enum GETFDPW, GETFDGR, GETFDHST, + GETAI, LASTREQ } request_type; @@ -118,6 +119,28 @@ typedef struct } hst_response_header; +/* Structure sent in reply to addrinfo query. Note that this struct is + sent also if the service is disabled or there is no record found. */ +typedef struct +{ + int32_t version; + int32_t found; + nscd_ssize_t naddrs; + nscd_ssize_t addrslen; + nscd_ssize_t canonlen; + int32_t error; +} ai_response_header; + +/* Structure filled in by __nscd_getai. */ +struct nscd_ai_result +{ + int naddrs; + char *canon; + uint8_t *family; + char *addrs; +}; + + /* Type for offsets in data part of database. */ typedef uint32_t ref_t; /* Value for invalid/no reference. */ @@ -136,9 +159,9 @@ struct datahead nscd_ssize_t allocsize; /* Allocated Bytes. */ nscd_ssize_t recsize; /* Size of the record. */ nscd_time_t timeout; /* Time when this entry becomes invalid. */ - bool notfound; /* Nonzero if data has not been found. */ + uint8_t notfound; /* Nonzero if data has not been found. */ uint8_t nreloads; /* Reloads without use. */ - bool usable; /* False if the entry must be ignored. */ + uint8_t usable; /* False if the entry must be ignored. */ uint64_t :40; /* Alignment. */ /* We need to have the following element aligned for the response @@ -149,6 +172,7 @@ struct datahead pw_response_header pwdata; gr_response_header grdata; hst_response_header hstdata; + ai_response_header aidata; nscd_ssize_t align1; nscd_time_t align2; } data[0]; @@ -270,4 +294,8 @@ extern const struct datahead *__nscd_cache_search (request_type type, size_t keylen, const struct mapped_database *mapped); +/* Look up in addrinfo cache. */ +extern int __nscd_getai (const char *key, struct nscd_ai_result **result, + int *h_errnop); + #endif /* nscd.h */ diff --git a/nscd/nscd.c b/nscd/nscd.c index 5c5c15598b..facea5ecdb 100644 --- a/nscd/nscd.c +++ b/nscd/nscd.c @@ -465,3 +465,14 @@ write_pid (const char *file) return 0; } + + +/* This is an ugly hack which prevents getaddrinfo from being dragged + into nscd. There currently is no special getaddrinfo version for + use in nscd. In case it should be necessary such a version must be + created and this dummy version should be removed. */ +void +getaddrinfo (void) +{ + abort (); +} diff --git a/nscd/nscd.h b/nscd/nscd.h index d32f5fed47..01c8ca11e0 100644 --- a/nscd/nscd.h +++ b/nscd/nscd.h @@ -193,6 +193,11 @@ extern void readdhstbynamev6 (struct database_dyn *db, struct hashentry *he, extern void readdhstbyaddrv6 (struct database_dyn *db, struct hashentry *he, struct datahead *dh); +/* aicache.c */ +extern void addhstai (struct database_dyn *db, int fd, request_header *req, + void *key, uid_t uid); +extern void readdhstai (struct database_dyn *db, struct hashentry *he, + struct datahead *dh); /* mem.c */ extern void *mempool_alloc (struct database_dyn *db, size_t len); diff --git a/nscd/nscd_getai.c b/nscd/nscd_getai.c new file mode 100644 index 0000000000..453f459425 --- /dev/null +++ b/nscd/nscd_getai.c @@ -0,0 +1,164 @@ +/* Copyright (C) 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 2004. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nscd-client.h" + + +/* Define in nscd_gethst_r.c. */ +extern int __nss_not_use_nscd_hosts; + + +libc_locked_map_ptr (map_handle); +/* Note that we only free the structure if necessary. The memory + mapping is not removed since it is not visible to the malloc + handling. */ +libc_freeres_fn (gr_map_free) +{ + + if (map_handle.mapped != NO_MAPPING) + free (map_handle.mapped); +} + + +int +__nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop) +{ + size_t keylen = strlen (key) + 1; + const ai_response_header *ai_resp = NULL; + struct nscd_ai_result *resultbuf = NULL; + const char *recend = (const char *) ~UINTMAX_C (0); + char *respdata = NULL; + int retval = -1; + int sock = -1; + int gc_cycle; + + /* If the mapping is available, try to search there instead of + communicating with the nscd. */ + struct mapped_database *mapped = __nscd_get_map_ref (GETFDHST, "hosts", + &map_handle, &gc_cycle); + retry: + if (mapped != MAP_FAILED) + { + const struct datahead *found = __nscd_cache_search (GETAI, key, keylen, + mapped); + if (found != NULL) + { + ai_resp = &found->data[0].aidata; + respdata = (char *) (ai_resp + 1); + recend = (const char *) found->data + found->recsize; + } + } + + /* If we do not have the cache mapped, try to get the data over the + socket. */ + ai_response_header ai_resp_mem; + if (ai_resp == NULL) + { + sock = __nscd_open_socket (key, keylen, GETAI, &ai_resp_mem, + sizeof (ai_resp_mem)); + if (sock == -1) + { + /* nscd not running or wrong version or hosts caching disabled. */ + __nss_not_use_nscd_hosts = 1; + goto out;; + } + + ai_resp = &ai_resp_mem; + } + + if (ai_resp->found == 1) + { + size_t datalen = ai_resp->naddrs + ai_resp->addrslen + ai_resp->canonlen; + + /* This check is really only affects the case where the data + comes from the mapped cache. */ + if ((char *) (ai_resp + 1) + datalen > recend) + { + assert (sock == -1); + goto out; + } + + /* Create result. */ + resultbuf = (struct nscd_ai_result *) malloc (sizeof (*resultbuf) + + datalen); + if (resultbuf == NULL) + { + *h_errnop = NETDB_INTERNAL; + return -1; + } + + /* Set up the data structure, including pointers. */ + resultbuf->naddrs = ai_resp->naddrs; + resultbuf->addrs = (char *) (resultbuf + 1); + resultbuf->family = (uint8_t *) (resultbuf->addrs + ai_resp->addrslen); + if (ai_resp->canonlen != 0) + resultbuf->canon = (char *) (resultbuf->family + resultbuf->naddrs); + else + resultbuf->canon = NULL; + + if (respdata == NULL) + { + /* Read the data from the socket. */ + if ((size_t) TEMP_FAILURE_RETRY (__read (sock, resultbuf + 1, + datalen)) == datalen) + { + retval = 0; + *result = resultbuf; + } + } + else + { + /* Copy the data in the block. */ + memcpy (resultbuf + 1, respdata, datalen); + + retval = 0; + *result = resultbuf; + } + } + else + { + /* Store the error number. */ + *h_errnop = ai_resp->error; + + /* The `errno' to some value != ERANGE. */ + __set_errno (ENOENT); + /* Even though we have not found anything, the result is zero. */ + retval = 0; + } + + if (sock != -1) + close_not_cancel_no_status (sock); + out: + if (__nscd_drop_map_ref (mapped, gc_cycle) != 0) + /* When we come here this means there has been a GC cycle while we + were looking for the data. This means the data might have been + inconsistent. Retry. */ + goto retry; + + return retval; +} diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c index b936b252fe..c9553849fd 100644 --- a/sysdeps/posix/getaddrinfo.c +++ b/sysdeps/posix/getaddrinfo.c @@ -54,6 +54,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #ifdef HAVE_LIBIDN extern int __idna_to_ascii_lz (const char *input, char **output, int flags); @@ -586,10 +587,156 @@ gaih_inet (const char *name, const struct gaih_service *service, enum nss_status inet6_status = NSS_STATUS_UNAVAIL; enum nss_status status = NSS_STATUS_UNAVAIL; int no_more; - nss_gethostbyname2_r fct; int old_res_options; - size_t tmpbuflen = 512; - char *tmpbuf = alloca (tmpbuflen); + + /* If we do not have to look for IPv4 and IPv6 together, use + the simple, old functions. */ + if (req->ai_family == AF_INET || req->ai_family == AF_INET6) + { + int family = req->ai_family; + size_t tmpbuflen = 512; + char *tmpbuf = alloca (tmpbuflen); + int rc; + struct hostent th; + struct hostent *h; + int herrno; + + simple_again: + while (1) + { + rc = __gethostbyname2_r (name, family, &th, tmpbuf, + tmpbuflen, &h, &herrno); + if (rc != ERANGE || herrno != NETDB_INTERNAL) + break; + tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); + } + + if (rc == 0) + { + if (h == NULL) + { + if (req->ai_family == AF_INET6 + && (req->ai_flags & AI_V4MAPPED) + && family == AF_INET6) + { + /* Try again, this time looking for IPv4 + addresses. */ + family = AF_INET; + goto simple_again; + } + } + else + { + /* We found data, now convert it into the list. */ + for (int i = 0; h->h_addr_list[i]; ++i) + { + if (*pat == NULL) + { + *pat = __alloca (sizeof (struct gaih_addrtuple)); + (*pat)->scopeid = 0; + } + (*pat)->next = NULL; + (*pat)->family = req->ai_family; + if (family == req->ai_family) + memcpy ((*pat)->addr, h->h_addr_list[i], + h->h_length); + else + { + int32_t *addr = (uint32_t *) (*pat)->addr; + addr[3] = *(uint32_t *) h->h_addr_list[i]; + addr[2] = htonl (0xffff); + addr[1] = 0; + addr[0] = 0; + } + pat = &((*pat)->next); + } + } + } + else + { + if (herrno == NETDB_INTERNAL) + { + __set_h_errno (herrno); + return -EAI_SYSTEM; + } + if (herrno == TRY_AGAIN) + { + return -EAI_AGAIN; + } + /* We made requests but they turned out no data. + The name is known, though. */ + return (GAIH_OKIFUNSPEC | -EAI_NODATA); + } + + goto process_list; + } + +#ifdef USE_NSCD + /* Try to use nscd. */ + struct nscd_ai_result *air = NULL; + int herrno; + int err = __nscd_getai (name, &air, &herrno); + if (air != NULL) + { + /* Transform into gaih_addrtuple list. */ + bool added_canon = (req->ai_flags & AI_CANONNAME) == 0; + char *addrs = air->addrs; + + for (int i = 0; i < air->naddrs; ++i) + { + socklen_t size = (air->family[i] == AF_INET + ? INADDRSZ : IN6ADDRSZ); + if (*pat == NULL) + { + *pat = __alloca (sizeof (struct gaih_addrtuple)); + (*pat)->scopeid = 0; + } + uint32_t *pataddr = (*pat)->addr; + (*pat)->next = NULL; + if (added_canon || air->canon == NULL) + (*pat)->name = NULL; + else + canon = (*pat)->name = strdupa (air->canon); + + if (air->family[i] == AF_INET + && req->ai_family == AF_INET6 + && (req->ai_flags & AI_V4MAPPED)) + { + (*pat)->family = AF_INET6; + pataddr[3] = *(uint32_t *) addrs; + pataddr[2] = htonl (0xffff); + pataddr[1] = 0; + pataddr[0] = 0; + pat = &((*pat)->next); + added_canon = true; + } + else if (req->ai_family == AF_UNSPEC + || air->family[i] == req->ai_family) + { + (*pat)->family = air->family[i]; + memcpy (pataddr, addrs, size); + pat = &((*pat)->next); + added_canon = true; + if (air->family[i] == AF_INET6) + got_ipv6 = true; + } + addrs += size; + } + + if (at->family == AF_UNSPEC) + return (GAIH_OKIFUNSPEC | -EAI_NONAME); + + goto process_list; + } + else if (err != 0) + { + if (herrno == NETDB_INTERNAL && errno == ENOMEM) + return -EAI_MEMORY; + if (herrno == TRY_AGAIN) + return -EAI_AGAIN; + return -EAI_SYSTEM; + } +#endif if (__nss_hosts_database != NULL) { @@ -611,9 +758,13 @@ gaih_inet (const char *name, const struct gaih_service *service, old_res_options = _res.options; _res.options &= ~RES_USE_INET6; + size_t tmpbuflen = 512; + char *tmpbuf = alloca (tmpbuflen); + while (!no_more) { - fct = __nss_lookup_function (nip, "gethostbyname2_r"); + nss_gethostbyname2_r fct + = __nss_lookup_function (nip, "gethostbyname2_r"); if (fct != NULL) { @@ -707,6 +858,7 @@ gaih_inet (const char *name, const struct gaih_service *service, } } + process_list: if (at->family == AF_UNSPEC) return (GAIH_OKIFUNSPEC | -EAI_NONAME); } @@ -917,6 +1069,7 @@ struct sort_result { struct addrinfo *dest_addr; struct sockaddr_storage source_addr; + uint8_t source_addr_len; bool got_source_addr; }; @@ -1377,9 +1530,10 @@ getaddrinfo (const char *name, const char *service, /* Sort results according to RFC 3484. */ struct sort_result results[nresults]; struct addrinfo *q; + struct addrinfo *last = NULL; char *canonname = NULL; - for (i = 0, q = p; q != NULL; ++i, q = q->ai_next) + for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next) { results[i].dest_addr = q; results[i].got_source_addr = false; @@ -1387,18 +1541,33 @@ getaddrinfo (const char *name, const char *service, /* We overwrite the type with SOCK_DGRAM since we do not want connect() to connect to the other side. If we cannot determine the source address remember this - fact. */ - int fd = __socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP); - if (fd != -1) + fact. If we just looked up the address for a different + protocol, reuse the result. */ + if (last != NULL && last->ai_addrlen == q->ai_addrlen + && memcmp (last->ai_addr, q->ai_addr, q->ai_addrlen) == 0) { - socklen_t sl = sizeof (results[i].source_addr); - if (__connect (fd, q->ai_addr, q->ai_addrlen) == 0 - && __getsockname (fd, - (struct sockaddr *) &results[i].source_addr, - &sl) == 0) - results[i].got_source_addr = true; - - close_not_cancel_no_status (fd); + memcpy (&results[i].source_addr, &results[i - 1].source_addr, + results[i - 1].source_addr_len); + results[i].source_addr_len = results[i - 1].source_addr_len; + results[i].got_source_addr = results[i - 1].got_source_addr; + } + else + { + int fd = __socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP); + if (fd != -1) + { + socklen_t sl = sizeof (results[i].source_addr); + if (__connect (fd, q->ai_addr, q->ai_addrlen) == 0 + && __getsockname (fd, + (struct sockaddr *) &results[i].source_addr, + &sl) == 0) + { + results[i].source_addr_len = sl; + results[i].got_source_addr = true; + } + + close_not_cancel_no_status (fd); + } } /* Remember the canonical name. */ -- cgit 1.4.1