2004-09-15  Ulrich Drepper  <drepper@redhat.com>

	* 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.
This commit is contained in:
Ulrich Drepper 2004-09-15 08:25:49 +00:00
parent 5d156bb641
commit d19687d6eb
11 changed files with 862 additions and 29 deletions

View File

@ -1,3 +1,22 @@
2004-09-15 Ulrich Drepper <drepper@redhat.com>
* 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 <drepper@redhat.com>
* misc/sys/cdefs.h: Remove debugging text from __P and __PMT.

View File

@ -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. */

View File

@ -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)

428
nscd/aicache.c Normal file
View File

@ -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 <drepper@redhat.com>, 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 <assert.h>
#include <errno.h>
#include <libintl.h>
#include <netdb.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/mman.h>
#include <dbg_log.h>
#include <nscd.h>
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, &notfound, 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, &notfound, 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);
}

View File

@ -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");
}

View File

@ -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
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:

View File

@ -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 */

View File

@ -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 ();
}

View File

@ -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);

164
nscd/nscd_getai.c Normal file
View File

@ -0,0 +1,164 @@
/* Copyright (C) 2004 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@redhat.com>, 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 <assert.h>
#include <errno.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <not-cancel.h>
#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;
}

View File

@ -54,6 +54,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <net/if.h>
#include <nsswitch.h>
#include <not-cancel.h>
#include <nscd/nscd-client.h>
#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;
/* 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,7 +1541,18 @@ 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. */
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)
{
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)
{
@ -1396,10 +1561,14 @@ getaddrinfo (const char *name, const char *service,
&& __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. */
if (q->ai_canonname != NULL)