1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-07-29 11:41:21 +03:00

* include/resolv.h: Adjust __libc_res_nquery and __libc_res_nsend

prototypes.
	* include/arpa/nameser_compat.h: Define T_UNSPEC.
	* nis/Versions (libnss_nis): Export _nss_nis_gethostbyname4_r.
	(libnss_nisplus): Export _nss_nisplus_gethostbyname4_r.
	* nis/nss_nis/nis-hosts.c (LINE_PARSER): Change to also handle
	af==AF_UNSPEC.
	(_nss_nis_gethostbyname4_r): New function.
	* nis/nss_nisplus/nisplus-hosts.c (_nss_nisplus_parse_hostent):
	Change to also handle af==AF_UNSPEC.
	(get_tablename): New function.  Use it to avoid duplication.
	(_nss_nisplus_gethostbyname4_r): New function.
	* nscd/aicache.c (addhstaiX): Use gethostbyname4_r function is
	available.
	* nss/Versions (libnss_files): Export _nss_files_gethostbyname4_r.
	* nss/nss.h: Define struct gaih_addrtuple.
	* nss/nss_files/files-hosts.c (LINE_PARSER): Change to also handle
	af==AF_UNSPEC.
	(_nss_files_gethostbyname4_r): New function.
	* resolv/Versions (libnss_dns): Export _nss_dns_gethostbyname4_r.
	* resolv/gethnmaddr.c: Adjust __libc_res_nsearch and __libc_res_nquery
	calls.
	* resolv/res_query.c (__libc_res_nquery): Take two additional
	parameters for second answer buffer.  Handle type=T_UNSPEC to mean
	look up IPv4 and IPv6.
	Change all callers.
	* resolv/res_send.c (__libc_res_nsend): Take five aditional parameters
	for an additional query and answer buffer.  Pass to send_vc and
	send_dg.
	(send_vc): Send possibly two requests and receive two answers.
	(send_dg): Likewise.
	* resolv/nss_dns/dns-host.c: Adjust calls to __libc_res_nsearch and
	__libc_res_nquery.
	(_nss_dns_gethostbyname4_r): New function.
	(gaih_getanswer_slice): Likewise.
	(gaih_getanswer): Likewise.
	* resolv/nss_dns/dns-canon.c (_nss_dns_getcanonname_r): Adjust
	__libc_res_nquery call.
	* resolv/nss_dns/dns-network.c (_nss_dns_getnetbyaddr_r): Likewise.
	(_nss_dns_getnetbyname_r): Adjust __libc_res_nsearch call.
	* sysdeps/posix/getaddrinfo.c: Use gethostbyname4_r function is
	available.
This commit is contained in:
Ulrich Drepper
2008-05-10 23:27:39 +00:00
parent a82e8bb8d0
commit 1eb946b935
17 changed files with 1565 additions and 527 deletions

View File

@ -1,3 +1,48 @@
2008-05-10 Ulrich Drepper <drepper@redhat.com>
* include/resolv.h: Adjust __libc_res_nquery and __libc_res_nsend
prototypes.
* include/arpa/nameser_compat.h: Define T_UNSPEC.
* nis/Versions (libnss_nis): Export _nss_nis_gethostbyname4_r.
(libnss_nisplus): Export _nss_nisplus_gethostbyname4_r.
* nis/nss_nis/nis-hosts.c (LINE_PARSER): Change to also handle
af==AF_UNSPEC.
(_nss_nis_gethostbyname4_r): New function.
* nis/nss_nisplus/nisplus-hosts.c (_nss_nisplus_parse_hostent):
Change to also handle af==AF_UNSPEC.
(get_tablename): New function. Use it to avoid duplication.
(_nss_nisplus_gethostbyname4_r): New function.
* nscd/aicache.c (addhstaiX): Use gethostbyname4_r function is
available.
* nss/Versions (libnss_files): Export _nss_files_gethostbyname4_r.
* nss/nss.h: Define struct gaih_addrtuple.
* nss/nss_files/files-hosts.c (LINE_PARSER): Change to also handle
af==AF_UNSPEC.
(_nss_files_gethostbyname4_r): New function.
* resolv/Versions (libnss_dns): Export _nss_dns_gethostbyname4_r.
* resolv/gethnmaddr.c: Adjust __libc_res_nsearch and __libc_res_nquery
calls.
* resolv/res_query.c (__libc_res_nquery): Take two additional
parameters for second answer buffer. Handle type=T_UNSPEC to mean
look up IPv4 and IPv6.
Change all callers.
* resolv/res_send.c (__libc_res_nsend): Take five aditional parameters
for an additional query and answer buffer. Pass to send_vc and
send_dg.
(send_vc): Send possibly two requests and receive two answers.
(send_dg): Likewise.
* resolv/nss_dns/dns-host.c: Adjust calls to __libc_res_nsearch and
__libc_res_nquery.
(_nss_dns_gethostbyname4_r): New function.
(gaih_getanswer_slice): Likewise.
(gaih_getanswer): Likewise.
* resolv/nss_dns/dns-canon.c (_nss_dns_getcanonname_r): Adjust
__libc_res_nquery call.
* resolv/nss_dns/dns-network.c (_nss_dns_getnetbyaddr_r): Likewise.
(_nss_dns_getnetbyname_r): Adjust __libc_res_nsearch call.
* sysdeps/posix/getaddrinfo.c: Use gethostbyname4_r function is
available.
2008-05-05 David S. Miller <davem@davemloft.net> 2008-05-05 David S. Miller <davem@davemloft.net>
* sysdeps/sparc/sparc32/Makefile: Use -mcpu=v7 for initfini.s build. * sysdeps/sparc/sparc32/Makefile: Use -mcpu=v7 for initfini.s build.

View File

@ -1 +1,8 @@
#ifndef _ARPA_NAMESER_COMPAT_
#include <resolv/arpa/nameser_compat.h> #include <resolv/arpa/nameser_compat.h>
/* Picksome unused number to represent lookups of IPv4 and IPv6 (i.e.,
T_A and T_AAAA). */
#define T_UNSPEC 62321
#endif

View File

@ -95,7 +95,7 @@ libnss_nis {
_nss_nis_setgrent; _nss_nis_sethostent; _nss_nis_setnetent; _nss_nis_setgrent; _nss_nis_sethostent; _nss_nis_setnetent;
_nss_nis_setnetgrent; _nss_nis_setprotoent; _nss_nis_setpwent; _nss_nis_setnetgrent; _nss_nis_setprotoent; _nss_nis_setpwent;
_nss_nis_setrpcent; _nss_nis_setservent; _nss_nis_setspent; _nss_nis_setrpcent; _nss_nis_setservent; _nss_nis_setspent;
_nss_nis_initgroups_dyn; _nss_nis_initgroups_dyn; _nss_nis_gethostbyname4_r;
} }
} }
@ -126,5 +126,6 @@ libnss_nisplus {
_nss_nisplus_setnetent; _nss_nisplus_setnetgrent; _nss_nisplus_setprotoent; _nss_nisplus_setnetent; _nss_nisplus_setnetgrent; _nss_nisplus_setprotoent;
_nss_nisplus_setpwent; _nss_nisplus_setrpcent; _nss_nisplus_setservent; _nss_nisplus_setpwent; _nss_nisplus_setrpcent; _nss_nisplus_setservent;
_nss_nisplus_setspent; _nss_nisplus_initgroups_dyn; _nss_nisplus_setspent; _nss_nisplus_initgroups_dyn;
_nss_nisplus_gethostbyname4_r;
} }
} }

View File

@ -1,4 +1,5 @@
/* Copyright (C) 1996-2000, 2002, 2003, 2006, 2007 Free Software Foundation, Inc. /* Copyright (C) 1996-2000, 2002, 2003, 2006, 2007, 2008
Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996. Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
@ -17,6 +18,7 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */ 02111-1307 USA. */
#include <assert.h>
#include <nss.h> #include <nss.h>
#include <ctype.h> #include <ctype.h>
/* The following is an ugly trick to avoid a prototype declaration for /* The following is an ugly trick to avoid a prototype declaration for
@ -61,9 +63,12 @@ LINE_PARSER
STRING_FIELD (addr, isspace, 1); STRING_FIELD (addr, isspace, 1);
assert (af == AF_INET || af == AF_INET6 || af == AF_UNSPEC);
/* Parse address. */ /* Parse address. */
if (af == AF_INET && inet_pton (AF_INET, addr, entdata->host_addr) > 0) if (af != AF_INET6 && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
{ {
assert ((flags & AI_V4MAPPED) == 0 || af != AF_UNSPEC);
if (flags & AI_V4MAPPED) if (flags & AI_V4MAPPED)
{ {
map_v4v6_address ((char *) entdata->host_addr, map_v4v6_address ((char *) entdata->host_addr,
@ -77,7 +82,7 @@ LINE_PARSER
result->h_length = INADDRSZ; result->h_length = INADDRSZ;
} }
} }
else if (af == AF_INET6 else if (af != AF_INET
&& inet_pton (AF_INET6, addr, entdata->host_addr) > 0) && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
{ {
result->h_addrtype = AF_INET6; result->h_addrtype = AF_INET6;
@ -102,6 +107,7 @@ static bool_t new_start = 1;
static char *oldkey = NULL; static char *oldkey = NULL;
static int oldkeylen = 0; static int oldkeylen = 0;
enum nss_status enum nss_status
_nss_nis_sethostent (int stayopen) _nss_nis_sethostent (int stayopen)
{ {
@ -124,6 +130,7 @@ _nss_nis_sethostent (int stayopen)
is used so this makes no difference. */ is used so this makes no difference. */
strong_alias (_nss_nis_sethostent, _nss_nis_endhostent) strong_alias (_nss_nis_sethostent, _nss_nis_endhostent)
/* The calling function always need to get a lock first. */ /* The calling function always need to get a lock first. */
static enum nss_status static enum nss_status
internal_nis_gethostent_r (struct hostent *host, char *buffer, internal_nis_gethostent_r (struct hostent *host, char *buffer,
@ -216,6 +223,7 @@ internal_nis_gethostent_r (struct hostent *host, char *buffer,
return NSS_STATUS_SUCCESS; return NSS_STATUS_SUCCESS;
} }
enum nss_status enum nss_status
_nss_nis_gethostent_r (struct hostent *host, char *buffer, size_t buflen, _nss_nis_gethostent_r (struct hostent *host, char *buffer, size_t buflen,
int *errnop, int *h_errnop) int *errnop, int *h_errnop)
@ -233,6 +241,7 @@ _nss_nis_gethostent_r (struct hostent *host, char *buffer, size_t buflen,
return status; return status;
} }
static enum nss_status static enum nss_status
internal_gethostbyname2_r (const char *name, int af, struct hostent *host, internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
char *buffer, size_t buflen, int *errnop, char *buffer, size_t buflen, int *errnop,
@ -323,16 +332,24 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
return NSS_STATUS_SUCCESS; return NSS_STATUS_SUCCESS;
} }
enum nss_status enum nss_status
_nss_nis_gethostbyname2_r (const char *name, int af, struct hostent *host, _nss_nis_gethostbyname2_r (const char *name, int af, struct hostent *host,
char *buffer, size_t buflen, int *errnop, char *buffer, size_t buflen, int *errnop,
int *h_errnop) int *h_errnop)
{ {
if (af != AF_INET && af != AF_INET6)
{
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop, return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
h_errnop, h_errnop,
((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)); ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
} }
enum nss_status enum nss_status
_nss_nis_gethostbyname_r (const char *name, struct hostent *host, char *buffer, _nss_nis_gethostbyname_r (const char *name, struct hostent *host, char *buffer,
size_t buflen, int *errnop, int *h_errnop) size_t buflen, int *errnop, int *h_errnop)
@ -351,6 +368,7 @@ _nss_nis_gethostbyname_r (const char *name, struct hostent *host, char *buffer,
errnop, h_errnop, 0); errnop, h_errnop, 0);
} }
enum nss_status enum nss_status
_nss_nis_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af, _nss_nis_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
struct hostent *host, char *buffer, size_t buflen, struct hostent *host, char *buffer, size_t buflen,
@ -430,13 +448,93 @@ _nss_nis_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
return NSS_STATUS_SUCCESS; return NSS_STATUS_SUCCESS;
} }
#if 0
enum nss_status enum nss_status
_nss_nis_getipnodebyname_r (const char *name, int af, int flags, _nss_nis_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
struct hostent *result, char *buffer, char *buffer, size_t buflen, int *errnop,
size_t buflen, int *errnop, int *herrnop) int *herrnop, int32_t *ttlp)
{ {
return internal_gethostbyname2_r (name, af, result, buffer, buflen, char *domain;
errnop, herrnop, flags); if (yp_get_default_domain (&domain))
return NSS_STATUS_UNAVAIL;
/* Convert name to lowercase. */
size_t namlen = strlen (name);
char name2[namlen + 1];
size_t i;
for (i = 0; i < namlen; ++i)
name2[i] = tolower (name[i]);
name2[i] = '\0';
char *result;
int len;
int yperr = yp_match (domain, "hosts.byname", name2, namlen, &result, &len);
if (__builtin_expect (yperr != YPERR_SUCCESS, 0))
{
enum nss_status retval = yperr2nss (yperr);
if (retval == NSS_STATUS_TRYAGAIN)
{
*herrnop = TRY_AGAIN;
*errnop = errno;
}
if (retval == NSS_STATUS_NOTFOUND)
*herrnop = HOST_NOT_FOUND;
return retval;
}
struct parser_data data;
struct hostent host;
int parse_res = parse_line (result, &host, &data, buflen, errnop, AF_UNSPEC,
0);
if (__builtin_expect (parse_res < 1, 0))
{
if (parse_res == -1)
{
*herrnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
else
{
*herrnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
}
if (*pat == NULL)
{
uintptr_t pad = (-(uintptr_t) buffer
% __alignof__ (struct gaih_addrtuple));
buffer += pad;
buflen = buflen > pad ? buflen - pad : 0;
if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple), 0))
{
erange:
free (result);
*errnop = ERANGE;
*herrnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
*pat = (struct gaih_addrtuple *) buffer;
buffer += sizeof (struct gaih_addrtuple);
buflen -= sizeof (struct gaih_addrtuple);
}
(*pat)->next = NULL;
size_t h_name_len = strlen (host.h_name);
if (h_name_len >= buflen)
goto erange;
(*pat)->name = memcpy (buffer, host.h_name, h_name_len + 1);
(*pat)->family = host.h_addrtype;
memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
(*pat)->scopeid = 0;
assert (host.h_addr_list[1] == NULL);
free (result);
return NSS_STATUS_SUCCESS;
} }
#endif

View File

@ -1,4 +1,4 @@
/* Copyright (C) 1997-2002, 2003, 2005, 2006 Free Software Foundation, Inc. /* Copyright (C) 1997-2003, 2005, 2006, 2008 Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997. Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997.
@ -17,6 +17,7 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */ 02111-1307 USA. */
#include <assert.h>
#include <atomic.h> #include <atomic.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
@ -58,15 +59,15 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
if (result == NULL) if (result == NULL)
return 0; return 0;
if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) || if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS)
__type_of (NIS_RES_OBJECT (result)) != NIS_ENTRY_OBJ || || __type_of (NIS_RES_OBJECT (result)) != NIS_ENTRY_OBJ
strcmp(NIS_RES_OBJECT (result)[0].EN_data.en_type, "hosts_tbl") != 0 || || strcmp (NIS_RES_OBJECT (result)[0].EN_data.en_type, "hosts_tbl") != 0
NIS_RES_OBJECT (result)[0].EN_data.en_cols.en_cols_len < 4) || NIS_RES_OBJECT (result)[0].EN_data.en_cols.en_cols_len < 4)
return 0; return 0;
char *data = first_unused; char *data = first_unused;
if (room_left < (af == AF_INET6 || (flags & AI_V4MAPPED) != 0 if (room_left < (af != AF_INET || (flags & AI_V4MAPPED) != 0
? IN6ADDRSZ : INADDRSZ)) ? IN6ADDRSZ : INADDRSZ))
{ {
no_more_room: no_more_room:
@ -75,8 +76,10 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
} }
/* Parse address. */ /* Parse address. */
if (af == AF_INET && inet_pton (af, NISENTRYVAL (0, 2, result), data) > 0) if (af != AF_INET6
&& inet_pton (AF_INET, NISENTRYVAL (0, 2, result), data) > 0)
{ {
assert ((flags & AI_V4MAPPED) == 0 || af != AF_UNSPEC);
if (flags & AI_V4MAPPED) if (flags & AI_V4MAPPED)
{ {
map_v4v6_address (data, data); map_v4v6_address (data, data);
@ -89,7 +92,7 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
host->h_length = INADDRSZ; host->h_length = INADDRSZ;
} }
} }
else if (af == AF_INET6 else if (af != AF_INET
&& inet_pton (AF_INET6, NISENTRYVAL (0, 2, result), data) > 0) && inet_pton (AF_INET6, NISENTRYVAL (0, 2, result), data) > 0)
{ {
host->h_addrtype = AF_INET6; host->h_addrtype = AF_INET6;
@ -109,27 +112,33 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
first_unused = __stpncpy (first_unused, NISENTRYVAL (0, 0, result), first_unused = __stpncpy (first_unused, NISENTRYVAL (0, 0, result),
NISENTRYLEN (0, 0, result)); NISENTRYLEN (0, 0, result));
*first_unused++ = '\0'; *first_unused++ = '\0';
room_left -= NISENTRYLEN (0, 0, result) + 1; room_left -= NISENTRYLEN (0, 0, result) + 1;
/* XXX Rewrite at some point to allocate the array first and then
copy the strings. It wasteful to first concatenate the strings
to just split them again later. */
char *line = first_unused; char *line = first_unused;
for (i = 0; i < NIS_RES_NUMOBJ (result); ++i)
{
if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0)
{
if (NISENTRYLEN (i, 1, result) + 2 > room_left)
goto no_more_room;
*first_unused++ = ' '; /* When this is a call to gethostbyname4_r we do not need the aliases. */
first_unused = __stpncpy (first_unused, NISENTRYVAL (i, 1, result), if (af != AF_UNSPEC)
NISENTRYLEN (i, 1, result)); {
*first_unused = '\0'; /* XXX Rewrite at some point to allocate the array first and then
room_left -= NISENTRYLEN (i, 1, result) + 1; copy the strings. It is wasteful to first concatenate the strings
to just split them again later. */
for (i = 0; i < NIS_RES_NUMOBJ (result); ++i)
{
if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0)
{
if (NISENTRYLEN (i, 1, result) + 2 > room_left)
goto no_more_room;
*first_unused++ = ' ';
first_unused = __stpncpy (first_unused,
NISENTRYVAL (i, 1, result),
NISENTRYLEN (i, 1, result));
*first_unused = '\0';
room_left -= NISENTRYLEN (i, 1, result) + 1;
}
} }
*first_unused++ = '\0';
} }
*first_unused++ = '\0';
/* Adjust the pointer so it is aligned for /* Adjust the pointer so it is aligned for
storing pointers. */ storing pointers. */
@ -147,31 +156,35 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
host->h_addr_list[1] = NULL; host->h_addr_list[1] = NULL;
host->h_aliases = &host->h_addr_list[2]; host->h_aliases = &host->h_addr_list[2];
i = 0; /* When this is a call to gethostbyname4_r we do not need the aliases. */
while (*line != '\0') if (af != AF_UNSPEC)
{ {
/* Skip leading blanks. */ i = 0;
while (isspace (*line)) while (*line != '\0')
++line; {
/* Skip leading blanks. */
while (isspace (*line))
++line;
if (*line == '\0') if (*line == '\0')
break; break;
if (room_left < sizeof (char *)) if (room_left < sizeof (char *))
goto no_more_room; goto no_more_room;
room_left -= sizeof (char *); room_left -= sizeof (char *);
host->h_aliases[i++] = line; host->h_aliases[i++] = line;
while (*line != '\0' && *line != ' ') while (*line != '\0' && *line != ' ')
++line; ++line;
if (*line == ' ') if (*line == ' ')
*line++ = '\0'; *line++ = '\0';
}
host->h_aliases[i] = NULL;
} }
host->h_aliases[i] = NULL;
return 1; return 1;
} }
@ -204,6 +217,7 @@ _nss_create_tablename (int *errnop)
return NSS_STATUS_SUCCESS; return NSS_STATUS_SUCCESS;
} }
enum nss_status enum nss_status
_nss_nisplus_sethostent (int stayopen) _nss_nisplus_sethostent (int stayopen)
{ {
@ -226,6 +240,7 @@ _nss_nisplus_sethostent (int stayopen)
return status; return status;
} }
enum nss_status enum nss_status
_nss_nisplus_endhostent (void) _nss_nisplus_endhostent (void)
{ {
@ -242,6 +257,7 @@ _nss_nisplus_endhostent (void)
return NSS_STATUS_SUCCESS; return NSS_STATUS_SUCCESS;
} }
static enum nss_status static enum nss_status
internal_nisplus_gethostent_r (struct hostent *host, char *buffer, internal_nisplus_gethostent_r (struct hostent *host, char *buffer,
size_t buflen, int *errnop, int *herrnop) size_t buflen, int *errnop, int *herrnop)
@ -329,6 +345,7 @@ internal_nisplus_gethostent_r (struct hostent *host, char *buffer,
return NSS_STATUS_SUCCESS; return NSS_STATUS_SUCCESS;
} }
enum nss_status enum nss_status
_nss_nisplus_gethostent_r (struct hostent *result, char *buffer, _nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
size_t buflen, int *errnop, int *herrnop) size_t buflen, int *errnop, int *herrnop)
@ -345,26 +362,33 @@ _nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
return status; return status;
} }
static enum nss_status
get_tablename (int *herrnop)
{
__libc_lock_lock (lock);
enum nss_status status = _nss_create_tablename (herrnop);
__libc_lock_unlock (lock);
if (status != NSS_STATUS_SUCCESS)
*herrnop = NETDB_INTERNAL;
return status;
}
static enum nss_status static enum nss_status
internal_gethostbyname2_r (const char *name, int af, struct hostent *host, internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
char *buffer, size_t buflen, int *errnop, char *buffer, size_t buflen, int *errnop,
int *herrnop, int flags) int *herrnop, int flags)
{ {
int parse_res, retval;
if (tablename_val == NULL) if (tablename_val == NULL)
{ {
__libc_lock_lock (lock); enum nss_status status = get_tablename (herrnop);
enum nss_status status = _nss_create_tablename (errnop);
__libc_lock_unlock (lock);
if (status != NSS_STATUS_SUCCESS) if (status != NSS_STATUS_SUCCESS)
{ return status;
*herrnop = NETDB_INTERNAL;
return NSS_STATUS_UNAVAIL;
}
} }
if (name == NULL) if (name == NULL)
@ -374,39 +398,36 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
return NSS_STATUS_NOTFOUND; return NSS_STATUS_NOTFOUND;
} }
nis_result *result;
char buf[strlen (name) + 10 + tablename_len]; char buf[strlen (name) + 10 + tablename_len];
int olderr = errno; int olderr = errno;
/* Search at first in the alias list, and use the correct name /* Search at first in the alias list, and use the correct name
for the next search. */ for the next search. */
snprintf (buf, sizeof (buf), "[name=%s],%s", name, tablename_val); snprintf (buf, sizeof (buf), "[name=%s],%s", name, tablename_val);
result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL); nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
if (result != NULL) if (result != NULL)
{ {
char *bufptr = buf;
/* If we did not find it, try it as original name. But if the /* If we did not find it, try it as original name. But if the
database is correct, we should find it in the first case, too. */ database is correct, we should find it in the first case, too. */
if ((result->status != NIS_SUCCESS char *bufptr = buf;
&& result->status != NIS_S_SUCCESS) size_t buflen = sizeof (buf);
|| __type_of (result->objects.objects_val) != NIS_ENTRY_OBJ
|| strcmp (result->objects.objects_val->EN_data.en_type, if ((result->status == NIS_SUCCESS || result->status == NIS_S_SUCCESS)
"hosts_tbl") != 0 && __type_of (result->objects.objects_val) == NIS_ENTRY_OBJ
|| result->objects.objects_val->EN_data.en_cols.en_cols_len < 3) && strcmp (result->objects.objects_val->EN_data.en_type,
snprintf (buf, sizeof (buf), "[cname=%s],%s", name, tablename_val); "hosts_tbl") == 0
else && result->objects.objects_val->EN_data.en_cols.en_cols_len >= 3)
{ {
/* We need to allocate a new buffer since there is no /* We need to allocate a new buffer since there is no
guarantee the returned name has a length limit. */ guarantee the returned alias name has a length limit. */
const char *entryval = NISENTRYVAL(0, 0, result); name = NISENTRYVAL(0, 0, result);
size_t buflen = strlen (entryval) + 10 + tablename_len; size_t buflen = strlen (name) + 10 + tablename_len;
bufptr = alloca (buflen); bufptr = alloca (buflen);
snprintf (bufptr, buflen, "[cname=%s],%s",
entryval, tablename_val);
} }
snprintf (bufptr, buflen, "[cname=%s],%s", name, tablename_val);
nis_freeresult (result); nis_freeresult (result);
result = nis_list (bufptr, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL); result = nis_list (bufptr, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
} }
@ -417,7 +438,7 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
return NSS_STATUS_TRYAGAIN; return NSS_STATUS_TRYAGAIN;
} }
retval = niserr2nss (result->status); int retval = niserr2nss (result->status);
if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0)) if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0))
{ {
if (retval == NSS_STATUS_TRYAGAIN) if (retval == NSS_STATUS_TRYAGAIN)
@ -431,8 +452,8 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
return retval; return retval;
} }
parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer, int parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer,
buflen, errnop, flags); buflen, errnop, flags);
nis_freeresult (result); nis_freeresult (result);
@ -450,16 +471,24 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
return NSS_STATUS_NOTFOUND; return NSS_STATUS_NOTFOUND;
} }
enum nss_status enum nss_status
_nss_nisplus_gethostbyname2_r (const char *name, int af, struct hostent *host, _nss_nisplus_gethostbyname2_r (const char *name, int af, struct hostent *host,
char *buffer, size_t buflen, int *errnop, char *buffer, size_t buflen, int *errnop,
int *herrnop) int *herrnop)
{ {
if (af != AF_INET && af != AF_INET6)
{
*herrnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop, return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
herrnop, herrnop,
((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)); ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
} }
enum nss_status enum nss_status
_nss_nisplus_gethostbyname_r (const char *name, struct hostent *host, _nss_nisplus_gethostbyname_r (const char *name, struct hostent *host,
char *buffer, size_t buflen, int *errnop, char *buffer, size_t buflen, int *errnop,
@ -480,6 +509,7 @@ _nss_nisplus_gethostbyname_r (const char *name, struct hostent *host,
buflen, errnop, h_errnop, 0); buflen, errnop, h_errnop, 0);
} }
enum nss_status enum nss_status
_nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af, _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
struct hostent *host, char *buffer, struct hostent *host, char *buffer,
@ -487,12 +517,7 @@ _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
{ {
if (tablename_val == NULL) if (tablename_val == NULL)
{ {
__libc_lock_lock (lock); enum nss_status status = get_tablename (herrnop);
enum nss_status status = _nss_create_tablename (errnop);
__libc_lock_unlock (lock);
if (status != NSS_STATUS_SUCCESS) if (status != NSS_STATUS_SUCCESS)
return status; return status;
} }
@ -547,3 +572,44 @@ _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
__set_errno (olderr); __set_errno (olderr);
return NSS_STATUS_NOTFOUND; return NSS_STATUS_NOTFOUND;
} }
enum nss_status
_nss_nisplus_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
char *buffer, size_t buflen, int *errnop,
int *herrnop, int32_t *ttlp)
{
struct hostent host;
enum nss_status status = internal_gethostbyname2_r (name, AF_UNSPEC, &host,
buffer, buflen,
errnop, herrnop, 0);
if (__builtin_expect (status == NSS_STATUS_SUCCESS, 1))
{
if (*pat == NULL)
{
uintptr_t pad = (-(uintptr_t) buffer
% __alignof__ (struct gaih_addrtuple));
buffer += pad;
buflen = buflen > pad ? buflen - pad : 0;
if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple), 0))
{
free (result);
*errnop = ERANGE;
*herrnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
}
(*pat)->next = NULL;
(*pat)->name = host.h_name;
(*pat)->family = host.h_addrtype;
memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
(*pat)->scopeid = 0;
assert (host.h_addr_list[1] == NULL);
}
return status;
}

View File

@ -21,6 +21,7 @@
#include <errno.h> #include <errno.h>
#include <libintl.h> #include <libintl.h>
#include <netdb.h> #include <netdb.h>
#include <nss.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
@ -33,6 +34,10 @@
#endif #endif
typedef enum nss_status (*nss_gethostbyname4_r)
(const char *name, struct gaih_addrtuple **pat,
char *buffer, size_t buflen, int *errnop,
int *h_errnop, int32_t *ttlp);
typedef enum nss_status (*nss_gethostbyname3_r) typedef enum nss_status (*nss_gethostbyname3_r)
(const char *name, int af, struct hostent *host, (const char *name, int af, struct hostent *host,
char *buffer, size_t buflen, int *errnop, char *buffer, size_t buflen, int *errnop,
@ -117,16 +122,104 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
while (!no_more) while (!no_more)
{ {
void *cp;
int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL }; int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL };
int naddrs = 0;
size_t addrslen = 0;
size_t canonlen;
/* Prefer the function which also returns the TTL and canonical name. */ nss_gethostbyname4_r fct4 = __nss_lookup_function (nip,
nss_gethostbyname3_r fct = __nss_lookup_function (nip, "gethostbyname4_r");
"gethostbyname3_r"); if (fct4 != NULL)
if (fct == NULL)
fct = __nss_lookup_function (nip, "gethostbyname2_r");
if (fct != NULL)
{ {
struct gaih_addrtuple *at = NULL;
while (1)
{
rc6 = 0;
status[0] = DL_CALL_FCT (fct4, (key, &at, tmpbuf6, tmpbuf6len,
&rc6, &herrno, &ttl));
if (rc6 != ERANGE || herrno != NETDB_INTERNAL)
break;
tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len);
}
if (rc6 != 0 && herrno == NETDB_INTERNAL)
goto out;
if (status[0] != NSS_STATUS_SUCCESS)
goto next_nip;
/* We found the data. Count the addresses and the size. */
for (struct gaih_addrtuple *at2 = at; at2 != NULL; at2 = at2->next)
{
++naddrs;
/* We handle unknown types here the best we can: assume
the maximum size for the address. */
if (at2->family == AF_INET)
addrslen += INADDRSZ;
else if (at2->family == AF_INET6
&& IN6ADDRSZ != sizeof (at2->addr))
addrslen += IN6ADDRSZ;
else
addrslen += sizeof (at2->addr);
}
canon = at->name;
canonlen = strlen (canon) + 1;
total = sizeof (*dataset) + naddrs + addrslen + canonlen;
/* Now we can allocate the data structure. If the TTL of the
entry is reported as zero do not cache the entry at all. */
if (ttl != 0 && he == NULL)
{
dataset = (struct dataset *) mempool_alloc (db, total
+ req->key_len,
IDX_result_data);
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;
}
/* Fill in the address and address families. */
char *addrs = (char *) (&dataset->resp + 1);
uint8_t *family = (uint8_t *) (addrs + addrslen);
for (struct gaih_addrtuple *at2 = at; at2 != NULL; at2 = at2->next)
{
*family++ = at2->family;
if (at2->family == AF_INET)
addrs = mempcpy (addrs, at2->addr, INADDRSZ);
else if (at2->family == AF_INET6
&& IN6ADDRSZ != sizeof (at2->addr))
addrs = mempcpy (addrs, at2->addr, IN6ADDRSZ);
else
addrs = mempcpy (addrs, at2->addr, sizeof (at2->addr));
}
cp = family;
}
else
{
/* Prefer the function which also returns the TTL and
canonical name. */
nss_gethostbyname3_r fct = __nss_lookup_function (nip,
"gethostbyname3_r");
if (fct == NULL)
fct = __nss_lookup_function (nip, "gethostbyname2_r");
if (fct == NULL)
goto next_nip;
struct hostent th[2]; struct hostent th[2];
/* Collect IPv6 information first. */ /* Collect IPv6 information first. */
@ -134,8 +227,8 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
{ {
rc6 = 0; rc6 = 0;
status[0] = DL_CALL_FCT (fct, (key, AF_INET6, &th[0], tmpbuf6, status[0] = DL_CALL_FCT (fct, (key, AF_INET6, &th[0], tmpbuf6,
tmpbuf6len, &rc6, &herrno, tmpbuf6len, &rc6, &herrno, &ttl,
&ttl, &canon)); &canon));
if (rc6 != ERANGE || herrno != NETDB_INTERNAL) if (rc6 != ERANGE || herrno != NETDB_INTERNAL)
break; break;
tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len); tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len);
@ -173,231 +266,226 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
if (rc4 != 0 && herrno == NETDB_INTERNAL) if (rc4 != 0 && herrno == NETDB_INTERNAL)
goto out; goto out;
if (status[0] == NSS_STATUS_SUCCESS if (status[0] != NSS_STATUS_SUCCESS
|| status[1] == NSS_STATUS_SUCCESS) && status[1] != NSS_STATUS_SUCCESS)
goto next_nip;
/* We found the data. Count the addresses and the size. */
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;
}
if (canon == NULL)
{ {
/* We found the data. Count the addresses and the size. */ /* Determine the canonical name. */
int naddrs = 0; nss_getcanonname_r cfct;
size_t addrslen = 0; cfct = __nss_lookup_function (nip, "getcanonname_r");
for (int j = 0; j < 2; ++j) if (cfct != NULL)
if (status[j] == NSS_STATUS_SUCCESS)
for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
{
++naddrs;
addrslen += th[j].h_length;
}
if (canon == NULL)
{ {
/* Determine the canonical name. */ const size_t max_fqdn_len = 256;
nss_getcanonname_r cfct; char *buf = alloca (max_fqdn_len);
cfct = __nss_lookup_function (nip, "getcanonname_r"); char *s;
if (cfct != NULL) int rc;
{
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, if (DL_CALL_FCT (cfct, (key, buf, max_fqdn_len, &s,
&herrno)) == NSS_STATUS_SUCCESS) &rc, &herrno))
canon = s; == NSS_STATUS_SUCCESS)
else canon = s;
/* Set to name now to avoid using gethostbyaddr. */
canon = key;
}
else else
{ /* Set to name now to avoid using gethostbyaddr. */
struct hostent *he = NULL; canon = key;
int herrno;
struct hostent he_mem;
void *addr;
size_t addrlen;
int addrfamily;
if (status[1] == NSS_STATUS_SUCCESS)
{
addr = th[1].h_addr_list[0];
addrlen = sizeof (struct in_addr);
addrfamily = AF_INET;
}
else
{
addr = th[0].h_addr_list[0];
addrlen = sizeof (struct in6_addr);
addrfamily = AF_INET6;
}
size_t tmpbuflen = 512;
char *tmpbuf = alloca (tmpbuflen);
int rc;
while (1)
{
rc = __gethostbyaddr2_r (addr, addrlen, addrfamily,
&he_mem, tmpbuf, tmpbuflen,
&he, &herrno, NULL);
if (rc != ERANGE || herrno != NETDB_INTERNAL)
break;
tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
tmpbuflen * 2);
}
if (rc == 0)
{
if (he != NULL)
canon = he->h_name;
else
canon = key;
}
}
}
size_t canonlen = canon == NULL ? 0 : (strlen (canon) + 1);
total = sizeof (*dataset) + naddrs + addrslen + canonlen;
/* Now we can allocate the data structure. If the TTL
of the entry is reported as zero do not cache the
entry at all. */
if (ttl != 0 && he == NULL)
{
dataset = (struct dataset *) mempool_alloc (db,
total
+ req->key_len,
IDX_result_data);
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) + (ttl == INT32_MAX
? db->postimeout : ttl);
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;
}
void *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,
IDX_result_data);
if (__builtin_expect (newp != NULL, 1))
{
/* 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;
}
else
++db->head->addfailed;
/* Mark the old record as obsolete. */
dh->usable = false;
}
} }
else else
{ {
/* We write the dataset before inserting it to the struct hostent *he = NULL;
database since while inserting this thread might int herrno;
block and so would unnecessarily let the receiver struct hostent he_mem;
wait. */ void *addr;
assert (fd != -1); size_t addrlen;
int addrfamily;
#ifdef HAVE_SENDFILE if (status[1] == NSS_STATUS_SUCCESS)
if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
{ {
assert (db->wr_fd != -1); addr = th[1].h_addr_list[0];
assert ((char *) &dataset->resp > (char *) db->data); addrlen = sizeof (struct in_addr);
assert ((char *) &dataset->resp - (char *) db->head addrfamily = AF_INET;
+ total
<= (sizeof (struct database_pers_head)
+ db->head->module * sizeof (ref_t)
+ db->head->data_size));
ssize_t written;
written = sendfileall (fd, db->wr_fd,
(char *) &dataset->resp
- (char *) db->head, total);
# ifndef __ASSUME_SENDFILE
if (written == -1 && errno == ENOSYS)
goto use_write;
# endif
} }
else else
# ifndef __ASSUME_SENDFILE {
use_write: addr = th[0].h_addr_list[0];
# endif addrlen = sizeof (struct in6_addr);
#endif addrfamily = AF_INET6;
writeall (fd, &dataset->resp, total); }
}
goto out; size_t tmpbuflen = 512;
char *tmpbuf = alloca (tmpbuflen);
int rc;
while (1)
{
rc = __gethostbyaddr2_r (addr, addrlen, addrfamily,
&he_mem, tmpbuf, tmpbuflen,
&he, &herrno, NULL);
if (rc != ERANGE || herrno != NETDB_INTERNAL)
break;
tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
tmpbuflen * 2);
}
if (rc == 0)
{
if (he != NULL)
canon = he->h_name;
else
canon = key;
}
}
} }
canonlen = canon == NULL ? 0 : (strlen (canon) + 1);
total = sizeof (*dataset) + naddrs + addrslen + canonlen;
/* Now we can allocate the data structure. If the TTL of the
entry is reported as zero do not cache the entry at all. */
if (ttl != 0 && he == NULL)
{
dataset = (struct dataset *) mempool_alloc (db, total
+ req->key_len,
IDX_result_data);
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;
}
/* Fill in the address and address families. */
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;
}
cp = family;
} }
/* Fill in the rest of the dataset. */
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) + (ttl == INT32_MAX
? db->postimeout : ttl);
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;
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,
IDX_result_data);
if (__builtin_expect (newp != NULL, 1))
{
/* 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;
}
else
++db->head->addfailed;
/* 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);
#ifdef HAVE_SENDFILE
if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
{
assert (db->wr_fd != -1);
assert ((char *) &dataset->resp > (char *) db->data);
assert ((char *) &dataset->resp - (char *) db->head + total
<= (sizeof (struct database_pers_head)
+ db->head->module * sizeof (ref_t)
+ db->head->data_size));
ssize_t written;
written = sendfileall (fd, db->wr_fd, (char *) &dataset->resp
- (char *) db->head, total);
# ifndef __ASSUME_SENDFILE
if (written == -1 && errno == ENOSYS)
goto use_write;
# endif
}
else
# ifndef __ASSUME_SENDFILE
use_write:
# endif
#endif
writeall (fd, &dataset->resp, total);
}
goto out;
next_nip:
if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN) if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN)
break; break;

View File

@ -38,6 +38,7 @@ libnss_files {
_nss_files_endhostent; _nss_files_endhostent;
_nss_files_gethostbyaddr_r; _nss_files_gethostbyaddr_r;
_nss_files_gethostbyname2_r; _nss_files_gethostbyname2_r;
_nss_files_gethostbyname4_r;
_nss_files_gethostbyname_r; _nss_files_gethostbyname_r;
_nss_files_gethostent_r; _nss_files_gethostent_r;
_nss_files_gethostton_r; _nss_files_gethostton_r;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc. /* Copyright (C) 1996, 1997, 1999, 2008 Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or The GNU C Library is free software; you can redistribute it and/or
@ -23,6 +23,7 @@
#define _NSS_H 1 #define _NSS_H 1
#include <features.h> #include <features.h>
#include <stdint.h>
__BEGIN_DECLS __BEGIN_DECLS
@ -38,6 +39,17 @@ enum nss_status
}; };
/* Data structure used for the 'gethostbyname4_r' function. */
struct gaih_addrtuple
{
struct gaih_addrtuple *next;
char *name;
int family;
uint32_t addr[4];
uint32_t scopeid;
};
/* Overwrite service selection for database DBNAME using specification /* Overwrite service selection for database DBNAME using specification
in STRING. in STRING.
This function should only be used by system programs which have to This function should only be used by system programs which have to

View File

@ -1,6 +1,5 @@
/* Hosts file parser in nss_files module. /* Hosts file parser in nss_files module.
Copyright (C) 1996-2001, 2003, 2004, 2006, 2007 Copyright (C) 1996-2001, 2003-2007, 2008 Free Software Foundation, Inc.
Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or The GNU C Library is free software; you can redistribute it and/or
@ -56,7 +55,10 @@ LINE_PARSER
STRING_FIELD (addr, isspace, 1); STRING_FIELD (addr, isspace, 1);
/* Parse address. */ /* Parse address. */
if (inet_pton (af, addr, entdata->host_addr) <= 0) if (inet_pton (af == AF_UNSPEC ? AF_INET : af, addr, entdata->host_addr)
> 0)
af = af == AF_UNSPEC ? AF_INET : af;
else
{ {
if (af == AF_INET6 && (flags & AI_V4MAPPED) != 0 if (af == AF_INET6 && (flags & AI_V4MAPPED) != 0
&& inet_pton (AF_INET, addr, entdata->host_addr) > 0) && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
@ -76,6 +78,9 @@ LINE_PARSER
/* Illegal address: ignore line. */ /* Illegal address: ignore line. */
return 0; return 0;
} }
else if (af == AF_UNSPEC
&& inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
af = AF_INET6;
else else
/* Illegal address: ignore line. */ /* Illegal address: ignore line. */
return 0; return 0;
@ -101,8 +106,6 @@ _nss_files_get##name##_r (proto, \
struct STRUCTURE *result, char *buffer, \ struct STRUCTURE *result, char *buffer, \
size_t buflen, int *errnop H_ERRNO_PROTO) \ size_t buflen, int *errnop H_ERRNO_PROTO) \
{ \ { \
enum nss_status status; \
\
uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data); \ uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data); \
buffer += pad; \ buffer += pad; \
buflen = buflen > pad ? buflen - pad : 0; \ buflen = buflen > pad ? buflen - pad : 0; \
@ -110,7 +113,7 @@ _nss_files_get##name##_r (proto, \
__libc_lock_lock (lock); \ __libc_lock_lock (lock); \
\ \
/* Reset file pointer to beginning or open file. */ \ /* Reset file pointer to beginning or open file. */ \
status = internal_setent (keep_stream); \ enum nss_status status = internal_setent (keep_stream); \
\ \
if (status == NSS_STATUS_SUCCESS) \ if (status == NSS_STATUS_SUCCESS) \
{ \ { \
@ -288,9 +291,9 @@ HOST_DB_LOOKUP (hostbyname, ,,
{ {
LOOKUP_NAME_CASE (h_name, h_aliases) LOOKUP_NAME_CASE (h_name, h_aliases)
}, const char *name) }, const char *name)
#undef EXTRA_ARGS_VALUE #undef EXTRA_ARGS_VALUE
/* XXX Is using _res to determine whether we want to convert IPv4 addresses /* XXX Is using _res to determine whether we want to convert IPv4 addresses
to IPv6 addresses really the right thing to do? */ to IPv6 addresses really the right thing to do? */
#define EXTRA_ARGS_VALUE \ #define EXTRA_ARGS_VALUE \
@ -299,8 +302,9 @@ HOST_DB_LOOKUP (hostbyname2, ,,
{ {
LOOKUP_NAME_CASE (h_name, h_aliases) LOOKUP_NAME_CASE (h_name, h_aliases)
}, const char *name, int af) }, const char *name, int af)
#undef EXTRA_ARGS_VALUE #undef EXTRA_ARGS_VALUE
/* We only need to consider IPv4 mapped addresses if the input to the /* We only need to consider IPv4 mapped addresses if the input to the
gethostbyaddr() function is an IPv6 address. */ gethostbyaddr() function is an IPv6 address. */
#define EXTRA_ARGS_VALUE \ #define EXTRA_ARGS_VALUE \
@ -311,3 +315,116 @@ DB_LOOKUP (hostbyaddr, ,,
&& ! memcmp (addr, result->h_addr_list[0], len)) && ! memcmp (addr, result->h_addr_list[0], len))
break; break;
}, const void *addr, socklen_t len, int af) }, const void *addr, socklen_t len, int af)
#undef EXTRA_ARGS_VALUE
enum nss_status
_nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
char *buffer, size_t buflen, int *errnop,
int *herrnop, int32_t *ttlp)
{
__libc_lock_lock (lock);
/* Reset file pointer to beginning or open file. */
enum nss_status status = internal_setent (keep_stream);
if (status == NSS_STATUS_SUCCESS)
{
/* Tell getent function that we have repositioned the file pointer. */
last_use = getby;
bool any = false;
bool got_canon = false;
while (1)
{
/* Align the buffer for the next record. */
uintptr_t pad = (-(uintptr_t) buffer
% __alignof__ (struct hostent_data));
buffer += pad;
buflen = buflen > pad ? buflen - pad : 0;
struct hostent result;
status = internal_getent (&result, buffer, buflen, errnop
H_ERRNO_ARG, AF_UNSPEC, 0);
if (status != NSS_STATUS_SUCCESS)
break;
int naliases = 0;
if (__strcasecmp (name, result.h_name) != 0)
{
for (; result.h_aliases[naliases] != NULL; ++naliases)
if (! __strcasecmp (name, result.h_aliases[naliases]))
break;
if (result.h_aliases[naliases] == NULL)
continue;
/* We know this alias exist. Count it. */
++naliases;
}
/* Determine how much memory has been used so far. */
// XXX It is not necessary to preserve the aliases array
while (result.h_aliases[naliases] != NULL)
++naliases;
char *bufferend = (char *) &result.h_aliases[naliases + 1];
assert (buflen >= bufferend - buffer);
buflen -= bufferend - buffer;
buffer = bufferend;
/* We found something. */
any = true;
/* Create the record the caller expects. There is only one
address. */
assert (result.h_addr_list[1] == NULL);
if (*pat == NULL)
{
uintptr_t pad = (-(uintptr_t) buffer
% __alignof__ (struct gaih_addrtuple));
buffer += pad;
buflen = buflen > pad ? buflen - pad : 0;
if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
0))
{
*errnop = ERANGE;
*herrnop = NETDB_INTERNAL;
status = NSS_STATUS_TRYAGAIN;
break;
}
*pat = (struct gaih_addrtuple *) buffer;
buffer += sizeof (struct gaih_addrtuple);
buflen -= sizeof (struct gaih_addrtuple);
}
(*pat)->next = NULL;
(*pat)->name = got_canon ? NULL : result.h_name;
got_canon = true;
(*pat)->family = result.h_addrtype;
memcpy ((*pat)->addr, result.h_addr_list[0], result.h_length);
(*pat)->scopeid = 0;
pat = &((*pat)->next);
/* If we only look for the first matching entry we are done. */
if ((_res_hconf.flags & HCONF_FLAG_MULTI) == 0)
break;
}
/* If we have to look for multiple records and found one, this
is a success. */
if (status == NSS_STATUS_NOTFOUND && any)
{
assert ((_res_hconf.flags & HCONF_FLAG_MULTI) != 0);
status = NSS_STATUS_SUCCESS;
}
if (! keep_stream)
internal_endent ();
}
__libc_lock_unlock (lock);
return status;
}

View File

@ -89,6 +89,7 @@ libnss_dns {
_nss_dns_gethostbyname_r; _nss_dns_getnetbyaddr_r; _nss_dns_gethostbyname_r; _nss_dns_getnetbyaddr_r;
_nss_dns_getnetbyname_r; _nss_dns_getcanonname_r; _nss_dns_getnetbyname_r; _nss_dns_getcanonname_r;
_nss_dns_gethostbyaddr2_r; _nss_dns_gethostbyaddr2_r;
_nss_dns_gethostbyname4_r;
} }
} }

View File

@ -621,7 +621,7 @@ gethostbyname2(name, af)
buf.buf = origbuf = (querybuf *) alloca (1024); buf.buf = origbuf = (querybuf *) alloca (1024);
if ((n = __libc_res_nsearch(&_res, name, C_IN, type, buf.buf->buf, 1024, if ((n = __libc_res_nsearch(&_res, name, C_IN, type, buf.buf->buf, 1024,
&buf.ptr)) < 0) { &buf.ptr, NULL, NULL)) < 0) {
if (buf.buf != origbuf) if (buf.buf != origbuf)
free (buf.buf); free (buf.buf);
Dprintf("res_nsearch failed (%d)\n", n); Dprintf("res_nsearch failed (%d)\n", n);
@ -716,12 +716,12 @@ gethostbyaddr(addr, len, af)
buf.buf = orig_buf = (querybuf *) alloca (1024); buf.buf = orig_buf = (querybuf *) alloca (1024);
n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf, 1024, n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf, 1024,
&buf.ptr); &buf.ptr, NULL, NULL);
if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0) { if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0) {
strcpy(qp, "ip6.int"); strcpy(qp, "ip6.int");
n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf, n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf,
buf.buf != orig_buf ? MAXPACKET : 1024, buf.buf != orig_buf ? MAXPACKET : 1024,
&buf.ptr); &buf.ptr, NULL, NULL);
} }
if (n < 0) { if (n < 0) {
if (buf.buf != orig_buf) if (buf.buf != orig_buf)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2004, 2006 Free Software Foundation, Inc. /* Copyright (C) 2004, 2006, 2008 Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@redhat.com>, 2004. Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
@ -61,7 +61,7 @@ _nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen,
for (int i = 0; i < nqtypes; ++i) for (int i = 0; i < nqtypes; ++i)
{ {
int r = __libc_res_nquery (&_res, name, ns_c_in, qtypes[i], int r = __libc_res_nquery (&_res, name, ns_c_in, qtypes[i],
buf, sizeof (buf), &ansp.ptr); buf, sizeof (buf), &ansp.ptr, NULL, NULL);
if (r > 0) if (r > 0)
{ {
/* We need to decode the response. Just one question record. /* We need to decode the response. Just one question record.

View File

@ -71,6 +71,7 @@
* --Copyright-- * --Copyright--
*/ */
#include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <netdb.h> #include <netdb.h>
@ -127,6 +128,14 @@ static enum nss_status getanswer_r (const querybuf *answer, int anslen,
size_t buflen, int *errnop, int *h_errnop, size_t buflen, int *errnop, int *h_errnop,
int map, int32_t *ttlp, char **canonp); int map, int32_t *ttlp, char **canonp);
static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1,
const querybuf *answer2, int anslen2,
const char *qname,
struct gaih_addrtuple **pat,
char *buffer, size_t buflen,
int *errnop, int *h_errnop,
int32_t *ttlp);
extern enum nss_status _nss_dns_gethostbyname3_r (const char *name, int af, extern enum nss_status _nss_dns_gethostbyname3_r (const char *name, int af,
struct hostent *result, struct hostent *result,
char *buffer, size_t buflen, char *buffer, size_t buflen,
@ -186,11 +195,11 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024); host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024);
n = __libc_res_nsearch (&_res, name, C_IN, type, host_buffer.buf->buf, n = __libc_res_nsearch (&_res, name, C_IN, type, host_buffer.buf->buf,
1024, &host_buffer.ptr); 1024, &host_buffer.ptr, NULL, NULL);
if (n < 0) if (n < 0)
{ {
enum nss_status status = (errno == ECONNREFUSED status = (errno == ECONNREFUSED
? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND); ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND);
*h_errnop = h_errno; *h_errnop = h_errno;
if (h_errno == TRY_AGAIN) if (h_errno == TRY_AGAIN)
*errnop = EAGAIN; *errnop = EAGAIN;
@ -203,7 +212,8 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
if (af == AF_INET6 && (_res.options & RES_USE_INET6)) if (af == AF_INET6 && (_res.options & RES_USE_INET6))
n = __libc_res_nsearch (&_res, name, C_IN, T_A, host_buffer.buf->buf, n = __libc_res_nsearch (&_res, name, C_IN, T_A, host_buffer.buf->buf,
host_buffer.buf != orig_host_buffer host_buffer.buf != orig_host_buffer
? MAXPACKET : 1024, &host_buffer.ptr); ? MAXPACKET : 1024, &host_buffer.ptr,
NULL, NULL);
if (n < 0) if (n < 0)
{ {
@ -255,6 +265,70 @@ _nss_dns_gethostbyname_r (const char *name, struct hostent *result,
} }
enum nss_status
_nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
char *buffer, size_t buflen, int *errnop,
int *herrnop, int32_t *ttlp)
{
if (__res_maybe_init (&_res, 0) == -1)
return NSS_STATUS_UNAVAIL;
char tmp[NS_MAXDNAME];
/*
* if there aren't any dots, it could be a user-level alias.
* this is also done in res_query() since we are not the only
* function that looks up host names.
*/
if (strchr (name, '.') == NULL)
{
const char *cp = res_hostalias (&_res, name, tmp, sizeof (tmp));
if (cp != NULL)
name = cp;
}
union
{
querybuf *buf;
u_char *ptr;
} host_buffer;
querybuf *orig_host_buffer;
host_buffer.buf = orig_host_buffer = (querybuf *) alloca (2048);
u_char *ans2p = NULL;
int nans2p = 0;
int olderr = errno;
enum nss_status status;
int n = __libc_res_nsearch (&_res, name, C_IN, T_UNSPEC,
host_buffer.buf->buf, 2048, &host_buffer.ptr,
&ans2p, &nans2p);
if (n < 0)
{
status = (errno == ECONNREFUSED
? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND);
*herrnop = h_errno;
if (h_errno == TRY_AGAIN)
*errnop = EAGAIN;
else
__set_errno (olderr);
if (host_buffer.buf != orig_host_buffer)
free (host_buffer.buf);
return status;
}
status = gaih_getanswer(host_buffer.buf, n, (const querybuf *) ans2p,
nans2p, name, pat, buffer, buflen,
errnop, herrnop, ttlp);
if (host_buffer.buf != orig_host_buffer)
free (host_buffer.buf);
return status;
}
extern enum nss_status _nss_dns_gethostbyaddr2_r (const void *addr, extern enum nss_status _nss_dns_gethostbyaddr2_r (const void *addr,
socklen_t len, int af, socklen_t len, int af,
struct hostent *result, struct hostent *result,
@ -342,7 +416,8 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
qp += sprintf (qp, "%02hhx", uaddr[n]); qp += sprintf (qp, "%02hhx", uaddr[n]);
strcpy (qp, "].ip6.arpa"); strcpy (qp, "].ip6.arpa");
n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR,
host_buffer.buf->buf, 1024, &host_buffer.ptr); host_buffer.buf->buf, 1024, &host_buffer.ptr,
NULL, NULL);
if (n >= 0) if (n >= 0)
goto got_it_already; goto got_it_already;
} }
@ -363,13 +438,14 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
} }
n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf, n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf,
1024, &host_buffer.ptr); 1024, &host_buffer.ptr, NULL, NULL);
if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0) if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0)
{ {
strcpy (qp, "ip6.int"); strcpy (qp, "ip6.int");
n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf, n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf,
host_buffer.buf != orig_host_buffer host_buffer.buf != orig_host_buffer
? MAXPACKET : 1024, &host_buffer.ptr); ? MAXPACKET : 1024, &host_buffer.ptr,
NULL, NULL);
} }
if (n < 0) if (n < 0)
{ {
@ -555,7 +631,8 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
if (n > 0 && bp[0] == '.') if (n > 0 && bp[0] == '.')
bp[0] = '\0'; bp[0] = '\0';
if (n < 0 || (*name_ok) (bp) == 0) if (__builtin_expect (n < 0 || ((*name_ok) (bp) == 0 && (errno = EBADMSG)),
0))
{ {
*errnop = errno; *errnop = errno;
*h_errnop = NO_RECOVERY; *h_errnop = NO_RECOVERY;
@ -629,7 +706,7 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
continue; /* XXX - had_error++ ? */ continue; /* XXX - had_error++ ? */
} }
if ((qtype ==T_A || qtype == T_AAAA) && type == T_CNAME) if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME)
{ {
if (ap >= &host_data->aliases[MAX_NR_ALIASES - 1]) if (ap >= &host_data->aliases[MAX_NR_ALIASES - 1])
continue; continue;
@ -857,3 +934,245 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases
? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN); ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN);
} }
static enum nss_status
gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname,
struct gaih_addrtuple ***patp,
char **bufferp, size_t *buflenp,
int *errnop, int *h_errnop, int32_t *ttlp, int *firstp)
{
char *buffer = *bufferp;
size_t buflen = *buflenp;
struct gaih_addrtuple **pat = *patp;
const HEADER *hp = &answer->hdr;
int ancount = ntohs (hp->ancount);
int qdcount = ntohs (hp->qdcount);
const u_char *cp = answer->buf + HFIXEDSZ;
const u_char *end_of_message = answer->buf + anslen;
if (__builtin_expect (qdcount != 1, 0))
{
*h_errnop = NO_RECOVERY;
return NSS_STATUS_UNAVAIL;
}
u_char packtmp[NS_MAXCDNAME];
int n = __ns_name_unpack (answer->buf, end_of_message, cp,
packtmp, sizeof packtmp);
/* We unpack the name to check it for validity. But we do not need
it later. */
if (n != -1 && __ns_name_ntop (packtmp, buffer, buflen) == -1)
{
if (__builtin_expect (errno, 0) == EMSGSIZE)
{
too_small:
*errnop = ERANGE;
*h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
n = -1;
}
if (__builtin_expect (n < 0 || (res_hnok (buffer) == 0
&& (errno = EBADMSG)), 0))
{
*errnop = errno;
*h_errnop = NO_RECOVERY;
return NSS_STATUS_UNAVAIL;
}
cp += n + QFIXEDSZ;
int haveanswer = 0;
int had_error = 0;
char *canon = NULL;
char *h_name = NULL;
int h_namelen = 0;
while (ancount-- > 0 && cp < end_of_message && had_error == 0)
{
n = __ns_name_unpack (answer->buf, end_of_message, cp,
packtmp, sizeof packtmp);
if (n != -1 &&
(h_namelen = __ns_name_ntop (packtmp, buffer, buflen)) == -1)
{
if (__builtin_expect (errno, 0) == EMSGSIZE)
goto too_small;
n = -1;
}
if (n < 0 || res_hnok (buffer) == 0)
{
++had_error;
continue;
}
if (*firstp)
{
h_name = buffer;
buffer += h_namelen;
buflen -= h_namelen;
}
cp += n; /* name */
int type = ns_get16 (cp);
cp += INT16SZ; /* type */
int class = ns_get16 (cp);
cp += INT16SZ; /* class */
int32_t ttl = ns_get32 (cp);
cp += INT32SZ; /* TTL */
n = ns_get16 (cp);
cp += INT16SZ; /* len */
if (class != C_IN)
{
cp += n;
continue;
}
if (type == T_CNAME)
{
char tbuf[MAXDNAME];
n = dn_expand (answer->buf, end_of_message, cp, tbuf, sizeof tbuf);
if (n < 0 || res_hnok (tbuf) == 0)
{
++had_error;
continue;
}
cp += n;
if (*firstp)
{
/* Reclaim buffer space. */
if (h_name + h_namelen == buffer)
{
buffer = h_name;
buflen += h_namelen;
}
n = strlen (tbuf) + 1;
if (__builtin_expect (n > buflen, 0))
goto too_small;
if (__builtin_expect (n >= MAXHOSTNAMELEN, 0))
{
++had_error;
continue;
}
canon = buffer;
buffer = __mempcpy (buffer, tbuf, n);
buflen -= n;
h_namelen = 0;
}
continue;
}
if (__builtin_expect (type == T_SIG, 0)
|| __builtin_expect (type == T_KEY, 0)
|| __builtin_expect (type == T_NXT, 0)
|| __builtin_expect (type == T_PTR, 0))
{
/* We don't support DNSSEC yet. For now, ignore the record
and send a low priority message to syslog.
We also don't expect T_PTR messages. */
syslog (LOG_DEBUG | LOG_AUTH,
"getaddrinfo*.gaih_getanswer: got type \"%s\"",
p_type (type));
cp += n;
continue;
}
if (type != T_A && type != T_AAAA)
abort ();
if (*pat == NULL)
{
uintptr_t pad = (-(uintptr_t) buffer
% __alignof__ (struct gaih_addrtuple));
buffer += pad;
buflen = buflen > pad ? buflen - pad : 0;
if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
0))
{
*errnop = ERANGE;
*h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
*pat = (struct gaih_addrtuple *) buffer;
buffer += sizeof (struct gaih_addrtuple);
buflen -= sizeof (struct gaih_addrtuple);
}
(*pat)->name = NULL;
(*pat)->next = NULL;
if (*firstp)
{
if (ttl != 0 && ttlp != NULL)
*ttlp = ttl;
if (canon != NULL)
{
(*pat)->name = canon;
/* Reclaim buffer space. */
if (h_name + h_namelen == buffer)
{
buffer = h_name;
buflen += h_namelen;
}
}
else
(*pat)->name = h_name;
*firstp = 0;
}
(*pat)->family = type == T_A ? AF_INET : AF_INET6;
memcpy ((*pat)->addr, cp, n);
cp += n;
(*pat)->scopeid = 0;
pat = &((*pat)->next);
haveanswer = 1;
}
if (haveanswer)
{
*patp = pat;
*bufferp = buffer;
*buflenp = buflen;
*h_errnop = NETDB_SUCCESS;
return NSS_STATUS_SUCCESS;
}
/* Special case here: if the resolver sent a result but it only
contains a CNAME while we are looking for a T_A or T_AAAA record,
we fail with NOTFOUND instead of TRYAGAIN. */
return canon == NULL ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND;
}
static enum nss_status
gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2,
int anslen2, const char *qname,
struct gaih_addrtuple **pat, char *buffer, size_t buflen,
int *errnop, int *h_errnop, int32_t *ttlp)
{
int first = 1;
enum nss_status status = gaih_getanswer_slice(answer1, anslen1, qname,
&pat, &buffer, &buflen,
errnop, h_errnop, ttlp,
&first);
if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND)
&& answer2 != NULL)
status = gaih_getanswer_slice(answer2, anslen2, qname,
&pat, &buffer, &buflen,
errnop, h_errnop, ttlp, &first);
return status;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 1996, 1997, 1998, 1999, 2002, 2004, 2007 /* Copyright (C) 1996, 1997, 1998, 1999, 2002, 2004, 2007, 2008
Free Software Foundation, Inc. Free Software Foundation, Inc.
This file is part of the GNU C Library. This file is part of the GNU C Library.
Extended from original form by Ulrich Drepper <drepper@cygnus.com>, 1996. Extended from original form by Ulrich Drepper <drepper@cygnus.com>, 1996.
@ -130,7 +130,7 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024); net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
anslen = __libc_res_nsearch (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf, anslen = __libc_res_nsearch (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
1024, &net_buffer.ptr); 1024, &net_buffer.ptr, NULL, NULL);
if (anslen < 0) if (anslen < 0)
{ {
/* Nothing found. */ /* Nothing found. */
@ -206,7 +206,7 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024); net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
anslen = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf, anslen = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
1024, &net_buffer.ptr); 1024, &net_buffer.ptr, NULL, NULL);
if (anslen < 0) if (anslen < 0)
{ {
/* Nothing found. */ /* Nothing found. */

View File

@ -97,7 +97,7 @@ static const char rcsid[] = "$BINDId: res_query.c,v 8.20 2000/02/29 05:39:12 vix
static int static int
__libc_res_nquerydomain(res_state statp, const char *name, const char *domain, __libc_res_nquerydomain(res_state statp, const char *name, const char *domain,
int class, int type, u_char *answer, int anslen, int class, int type, u_char *answer, int anslen,
u_char **answerp); u_char **answerp, u_char **answerp2, int *nanswerp2);
/* /*
* Formulate a normal query, send, and await answer. * Formulate a normal query, send, and await answer.
@ -115,15 +115,20 @@ __libc_res_nquery(res_state statp,
int class, int type, /* class and type of query */ int class, int type, /* class and type of query */
u_char *answer, /* buffer to put answer */ u_char *answer, /* buffer to put answer */
int anslen, /* size of answer buffer */ int anslen, /* size of answer buffer */
u_char **answerp) /* if buffer needs to be enlarged */ u_char **answerp, /* if buffer needs to be enlarged */
u_char **answerp2,
int *nanswerp2)
{ {
u_char *buf;
HEADER *hp = (HEADER *) answer; HEADER *hp = (HEADER *) answer;
int n, use_malloc = 0; int n, use_malloc = 0;
u_int oflags = statp->_flags; u_int oflags = statp->_flags;
size_t bufsize = QUERYSIZE; size_t bufsize = (type == T_UNSPEC ? 2 : 1) * QUERYSIZE;
buf = alloca (bufsize); u_char *buf = alloca (bufsize);
u_char *query1 = buf;
int nquery1 = -1;
u_char *query2 = NULL;
int nquery2 = 0;
again: again:
hp->rcode = NOERROR; /* default */ hp->rcode = NOERROR; /* default */
@ -133,18 +138,47 @@ __libc_res_nquery(res_state statp,
printf(";; res_query(%s, %d, %d)\n", name, class, type); printf(";; res_query(%s, %d, %d)\n", name, class, type);
#endif #endif
n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL, if (type == T_UNSPEC)
buf, bufsize); {
if (n > 0 n = res_nmkquery(statp, QUERY, name, class, T_A, NULL, 0, NULL,
&& (oflags & RES_F_EDNS0ERR) == 0 query1, bufsize);
&& (statp->options & RES_USE_EDNS0) != 0) if (n > 0)
n = __res_nopt(statp, n, buf, bufsize, anslen); {
if ((oflags & RES_F_EDNS0ERR) == 0
&& (statp->options & RES_USE_EDNS0) != 0)
n = __res_nopt(statp, n, query1, bufsize, anslen / 2);
nquery1 = n;
query2 = buf + nquery1;
n = res_nmkquery(statp, QUERY, name, class, T_AAAA, NULL, 0,
NULL, query2, bufsize - n);
if (n > 0
&& (oflags & RES_F_EDNS0ERR) == 0
&& (statp->options & RES_USE_EDNS0) != 0)
n = __res_nopt(statp, n, query2, bufsize - n, anslen / 2);
nquery2 = n;
}
}
else
{
n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL,
query1, bufsize);
if (n > 0
&& (oflags & RES_F_EDNS0ERR) == 0
&& (statp->options & RES_USE_EDNS0) != 0)
n = __res_nopt(statp, n, query1, bufsize, anslen);
nquery1 = n;
}
if (__builtin_expect (n <= 0, 0) && !use_malloc) { if (__builtin_expect (n <= 0, 0) && !use_malloc) {
/* Retry just in case res_nmkquery failed because of too /* Retry just in case res_nmkquery failed because of too
short buffer. Shouldn't happen. */ short buffer. Shouldn't happen. */
bufsize = MAXPACKET; bufsize = (type == T_UNSPEC ? 2 : 1) * MAXPACKET;
buf = malloc (bufsize); buf = malloc (bufsize);
if (buf != NULL) { if (buf != NULL) {
query1 = buf;
use_malloc = 1; use_malloc = 1;
goto again; goto again;
} }
@ -168,7 +202,8 @@ __libc_res_nquery(res_state statp,
return (n); return (n);
} }
assert (answerp == NULL || (void *) *answerp == (void *) answer); assert (answerp == NULL || (void *) *answerp == (void *) answer);
n = __libc_res_nsend(statp, buf, n, answer, anslen, answerp); n = __libc_res_nsend(statp, query1, nquery1, query2, nquery2, answer,
anslen, answerp, answerp2, nanswerp2);
if (use_malloc) if (use_malloc)
free (buf); free (buf);
if (n < 0) { if (n < 0) {
@ -184,20 +219,37 @@ __libc_res_nquery(res_state statp,
/* __libc_res_nsend might have reallocated the buffer. */ /* __libc_res_nsend might have reallocated the buffer. */
hp = (HEADER *) *answerp; hp = (HEADER *) *answerp;
if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { /* We simplify the following tests by assigning HP to HP2. It
is easy to verify that this is the same as ignoring all
tests of HP2. */
HEADER *hp2 = answerp2 ? (HEADER *) *answerp2 : hp;
if ((hp->rcode != NOERROR || ntohs(hp->ancount) == 0)
&& (hp2->rcode != NOERROR || ntohs(hp2->ancount) == 0)) {
#ifdef DEBUG #ifdef DEBUG
if (statp->options & RES_DEBUG) if (statp->options & RES_DEBUG) {
printf(";; rcode = %d, ancount=%d\n", hp->rcode, printf(";; rcode = %d, ancount=%d\n", hp->rcode,
ntohs(hp->ancount)); ntohs(hp->ancount));
if (hp != hp2)
printf(";; rcode2 = %d, ancount2=%d\n", hp2->rcode,
ntohs(hp2->ancount));
}
#endif #endif
switch (hp->rcode) { switch (hp->rcode == NOERROR ? hp2->rcode : hp->rcode) {
case NXDOMAIN: case NXDOMAIN:
if ((hp->rcode == NOERROR && ntohs (hp->ancount) != 0)
|| (hp2->rcode == NOERROR
&& ntohs (hp2->ancount) != 0))
goto success;
RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
break; break;
case SERVFAIL: case SERVFAIL:
RES_SET_H_ERRNO(statp, TRY_AGAIN); RES_SET_H_ERRNO(statp, TRY_AGAIN);
break; break;
case NOERROR: case NOERROR:
if (ntohs (hp->ancount) != 0
|| ntohs (hp2->ancount) != 0)
goto success;
RES_SET_H_ERRNO(statp, NO_DATA); RES_SET_H_ERRNO(statp, NO_DATA);
break; break;
case FORMERR: case FORMERR:
@ -209,6 +261,7 @@ __libc_res_nquery(res_state statp,
} }
return (-1); return (-1);
} }
success:
return (n); return (n);
} }
libresolv_hidden_def (__libc_res_nquery) libresolv_hidden_def (__libc_res_nquery)
@ -221,7 +274,7 @@ res_nquery(res_state statp,
int anslen) /* size of answer buffer */ int anslen) /* size of answer buffer */
{ {
return __libc_res_nquery(statp, name, class, type, answer, anslen, return __libc_res_nquery(statp, name, class, type, answer, anslen,
NULL); NULL, NULL, NULL);
} }
libresolv_hidden_def (res_nquery) libresolv_hidden_def (res_nquery)
@ -233,11 +286,13 @@ libresolv_hidden_def (res_nquery)
*/ */
int int
__libc_res_nsearch(res_state statp, __libc_res_nsearch(res_state statp,
const char *name, /* domain name */ const char *name, /* domain name */
int class, int type, /* class and type of query */ int class, int type, /* class and type of query */
u_char *answer, /* buffer to put answer */ u_char *answer, /* buffer to put answer */
int anslen, /* size of answer */ int anslen, /* size of answer */
u_char **answerp) u_char **answerp,
u_char **answerp2,
int *nanswerp2)
{ {
const char *cp, * const *domain; const char *cp, * const *domain;
HEADER *hp = (HEADER *) answer; HEADER *hp = (HEADER *) answer;
@ -260,7 +315,8 @@ __libc_res_nsearch(res_state statp,
/* If there aren't any dots, it could be a user-level alias. */ /* If there aren't any dots, it could be a user-level alias. */
if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL) if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
return (__libc_res_nquery(statp, cp, class, type, answer, return (__libc_res_nquery(statp, cp, class, type, answer,
anslen, answerp)); anslen, answerp, answerp2,
nanswerp2));
#ifdef DEBUG #ifdef DEBUG
if (statp->options & RES_DEBUG) if (statp->options & RES_DEBUG)
@ -276,7 +332,8 @@ __libc_res_nsearch(res_state statp,
saved_herrno = -1; saved_herrno = -1;
if (dots >= statp->ndots || trailing_dot) { if (dots >= statp->ndots || trailing_dot) {
ret = __libc_res_nquerydomain(statp, name, NULL, class, type, ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
answer, anslen, answerp); answer, anslen, answerp,
answerp2, nanswerp2);
if (ret > 0 || trailing_dot) if (ret > 0 || trailing_dot)
return (ret); return (ret);
saved_herrno = h_errno; saved_herrno = h_errno;
@ -285,6 +342,12 @@ __libc_res_nsearch(res_state statp,
answer = *answerp; answer = *answerp;
anslen = MAXPACKET; anslen = MAXPACKET;
} }
if (answerp2
&& (*answerp2 < answer || *answerp2 >= answer + anslen))
{
free (*answerp2);
*answerp2 = NULL;
}
} }
/* /*
@ -307,7 +370,8 @@ __libc_res_nsearch(res_state statp,
ret = __libc_res_nquerydomain(statp, name, *domain, ret = __libc_res_nquerydomain(statp, name, *domain,
class, type, class, type,
answer, anslen, answerp); answer, anslen, answerp,
answerp2, nanswerp2);
if (ret > 0) if (ret > 0)
return (ret); return (ret);
@ -315,6 +379,13 @@ __libc_res_nsearch(res_state statp,
answer = *answerp; answer = *answerp;
anslen = MAXPACKET; anslen = MAXPACKET;
} }
if (answerp2
&& (*answerp2 < answer
|| *answerp2 >= answer + anslen))
{
free (*answerp2);
*answerp2 = NULL;
}
/* /*
* If no server present, give up. * If no server present, give up.
@ -368,7 +439,8 @@ __libc_res_nsearch(res_state statp,
*/ */
if (dots && !(tried_as_is || root_on_list)) { if (dots && !(tried_as_is || root_on_list)) {
ret = __libc_res_nquerydomain(statp, name, NULL, class, type, ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
answer, anslen, answerp); answer, anslen, answerp,
answerp2, nanswerp2);
if (ret > 0) if (ret > 0)
return (ret); return (ret);
} }
@ -380,6 +452,11 @@ __libc_res_nsearch(res_state statp,
* else send back meaningless H_ERRNO, that being the one from * else send back meaningless H_ERRNO, that being the one from
* the last DNSRCH we did. * the last DNSRCH we did.
*/ */
if (answerp2 && (*answerp2 < answer || *answerp2 >= answer + anslen))
{
free (*answerp2);
*answerp2 = NULL;
}
if (saved_herrno != -1) if (saved_herrno != -1)
RES_SET_H_ERRNO(statp, saved_herrno); RES_SET_H_ERRNO(statp, saved_herrno);
else if (got_nodata) else if (got_nodata)
@ -398,7 +475,7 @@ res_nsearch(res_state statp,
int anslen) /* size of answer */ int anslen) /* size of answer */
{ {
return __libc_res_nsearch(statp, name, class, type, answer, return __libc_res_nsearch(statp, name, class, type, answer,
anslen, NULL); anslen, NULL, NULL, NULL);
} }
libresolv_hidden_def (res_nsearch) libresolv_hidden_def (res_nsearch)
@ -408,12 +485,14 @@ libresolv_hidden_def (res_nsearch)
*/ */
static int static int
__libc_res_nquerydomain(res_state statp, __libc_res_nquerydomain(res_state statp,
const char *name, const char *name,
const char *domain, const char *domain,
int class, int type, /* class and type of query */ int class, int type, /* class and type of query */
u_char *answer, /* buffer to put answer */ u_char *answer, /* buffer to put answer */
int anslen, /* size of answer */ int anslen, /* size of answer */
u_char **answerp) u_char **answerp,
u_char **answerp2,
int *nanswerp2)
{ {
char nbuf[MAXDNAME]; char nbuf[MAXDNAME];
const char *longname = nbuf; const char *longname = nbuf;
@ -450,7 +529,7 @@ __libc_res_nquerydomain(res_state statp,
sprintf(nbuf, "%s.%s", name, domain); sprintf(nbuf, "%s.%s", name, domain);
} }
return (__libc_res_nquery(statp, longname, class, type, answer, return (__libc_res_nquery(statp, longname, class, type, answer,
anslen, answerp)); anslen, answerp, answerp2, nanswerp2));
} }
int int
@ -462,7 +541,7 @@ res_nquerydomain(res_state statp,
int anslen) /* size of answer */ int anslen) /* size of answer */
{ {
return __libc_res_nquerydomain(statp, name, domain, class, type, return __libc_res_nquerydomain(statp, name, domain, class, type,
answer, anslen, NULL); answer, anslen, NULL, NULL, NULL);
} }
libresolv_hidden_def (res_nquerydomain) libresolv_hidden_def (res_nquerydomain)

View File

@ -176,10 +176,14 @@ evNowTime(struct timespec *res) {
/* Forward. */ /* Forward. */
static int send_vc(res_state, const u_char *, int, static int send_vc(res_state, const u_char *, int,
u_char **, int *, int *, int, u_char **); const u_char *, int,
u_char **, int *, int *, int, u_char **,
u_char **, int *, int *);
static int send_dg(res_state, const u_char *, int, static int send_dg(res_state, const u_char *, int,
const u_char *, int,
u_char **, int *, int *, int, u_char **, int *, int *, int,
int *, int *, u_char **); int *, int *, u_char **,
u_char **, int *, int *);
#ifdef DEBUG #ifdef DEBUG
static void Aerror(const res_state, FILE *, const char *, int, static void Aerror(const res_state, FILE *, const char *, int,
const struct sockaddr *); const struct sockaddr *);
@ -334,33 +338,41 @@ libresolv_hidden_def (res_queriesmatch)
int int
__libc_res_nsend(res_state statp, const u_char *buf, int buflen, __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
u_char *ans, int anssiz, u_char **ansp) const u_char *buf2, int buflen2,
u_char *ans, int anssiz, u_char **ansp, u_char **ansp2,
int *nansp2)
{ {
int gotsomewhere, terrno, try, v_circuit, resplen, ns, n; int gotsomewhere, terrno, try, v_circuit, resplen, resplen2, ns, n;
if (statp->nscount == 0) { if (statp->nscount == 0) {
__set_errno (ESRCH); __set_errno (ESRCH);
return (-1); return (-1);
} }
if (anssiz < HFIXEDSZ) { if (anssiz < (buf2 == NULL ? 1 : 2) * HFIXEDSZ) {
__set_errno (EINVAL); __set_errno (EINVAL);
return (-1); return (-1);
} }
if ((statp->qhook || statp->rhook) && anssiz < MAXPACKET && ansp) { #ifdef USE_HOOKS
u_char *buf = malloc (MAXPACKET); if (__builtin_expect (statp->qhook || statp->rhook, 0)) {
if (buf == NULL) if (anssiz < MAXPACKET && ansp) {
return (-1); u_char *buf = malloc (MAXPACKET);
memcpy (buf, ans, HFIXEDSZ); if (buf == NULL)
*ansp = buf; return (-1);
ans = buf; memcpy (buf, ans, HFIXEDSZ);
anssiz = MAXPACKET; *ansp = buf;
ans = buf;
anssiz = MAXPACKET;
}
} }
#endif
DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY), DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY),
(stdout, ";; res_send()\n"), buf, buflen); (stdout, ";; res_send()\n"), buf, buflen);
v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ; v_circuit = ((statp->options & RES_USEVC)
|| buflen > PACKETSZ
|| buflen2 > PACKETSZ);
gotsomewhere = 0; gotsomewhere = 0;
terrno = ETIMEDOUT; terrno = ETIMEDOUT;
@ -442,7 +454,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
* Some resolvers want to even out the load on their nameservers. * Some resolvers want to even out the load on their nameservers.
* Note that RES_BLAST overrides RES_ROTATE. * Note that RES_BLAST overrides RES_ROTATE.
*/ */
if ((statp->options & RES_ROTATE) != 0 && if (__builtin_expect ((statp->options & RES_ROTATE) != 0, 0) &&
(statp->options & RES_BLAST) == 0) { (statp->options & RES_BLAST) == 0) {
struct sockaddr_in6 *ina; struct sockaddr_in6 *ina;
unsigned int map; unsigned int map;
@ -479,8 +491,9 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
if (nsap == NULL) if (nsap == NULL)
goto next_ns; goto next_ns;
same_ns: same_ns:
if (statp->qhook) { #ifdef USE_HOOKS
if (__builtin_expect (statp->qhook != NULL, 0)) {
int done = 0, loops = 0; int done = 0, loops = 0;
do { do {
@ -512,6 +525,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
} }
} while (!done); } while (!done);
} }
#endif
#ifdef DEBUG #ifdef DEBUG
char tmpbuf[40]; char tmpbuf[40];
@ -521,29 +535,34 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
ns + 1, inet_ntop(AF_INET6, &nsap->sin6_addr, ns + 1, inet_ntop(AF_INET6, &nsap->sin6_addr,
tmpbuf, sizeof (tmpbuf)))); tmpbuf, sizeof (tmpbuf))));
if (v_circuit) { if (__builtin_expect (v_circuit, 0)) {
/* Use VC; at most one attempt per server. */ /* Use VC; at most one attempt per server. */
try = statp->retry; try = statp->retry;
n = send_vc(statp, buf, buflen, &ans, &anssiz, &terrno, n = send_vc(statp, buf, buflen, buf2, buflen2,
ns, ansp); &ans, &anssiz, &terrno,
ns, ansp, ansp2, nansp2, &resplen2);
if (n < 0) if (n < 0)
return (-1); return (-1);
if (n == 0) if (n == 0)
goto next_ns; goto next_ns;
resplen = n;
} else { } else {
/* Use datagrams. */ /* Use datagrams. */
n = send_dg(statp, buf, buflen, &ans, &anssiz, &terrno, n = send_dg(statp, buf, buflen, buf2, buflen2,
ns, &v_circuit, &gotsomewhere, ansp); &ans, &anssiz, &terrno,
ns, &v_circuit, &gotsomewhere, ansp,
ansp2, nansp2, &resplen2);
if (n < 0) if (n < 0)
return (-1); return (-1);
if (n == 0) if (n == 0)
goto next_ns; goto next_ns;
if (v_circuit) if (v_circuit)
// XXX Check whether both requests failed or
// XXX whether one have been answered successfully
goto same_ns; goto same_ns;
resplen = n;
} }
resplen = n;
Dprint((statp->options & RES_DEBUG) || Dprint((statp->options & RES_DEBUG) ||
((statp->pfcode & RES_PRF_REPLY) && ((statp->pfcode & RES_PRF_REPLY) &&
(statp->pfcode & RES_PRF_HEAD1)), (statp->pfcode & RES_PRF_HEAD1)),
@ -553,6 +572,11 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
(statp->pfcode & RES_PRF_REPLY), (statp->pfcode & RES_PRF_REPLY),
(stdout, "%s", ""), (stdout, "%s", ""),
ans, (resplen > anssiz) ? anssiz : resplen); ans, (resplen > anssiz) ? anssiz : resplen);
if (buf2 != NULL)
DprintQ((statp->options & RES_DEBUG) ||
(statp->pfcode & RES_PRF_REPLY),
(stdout, "%s", ""),
*ansp2, (resplen2 > *nansp2) ? *nansp2 : resplen2);
/* /*
* If we have temporarily opened a virtual circuit, * If we have temporarily opened a virtual circuit,
@ -563,7 +587,8 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
(statp->options & RES_STAYOPEN) == 0) { (statp->options & RES_STAYOPEN) == 0) {
__res_iclose(statp, false); __res_iclose(statp, false);
} }
if (statp->rhook) { #ifdef USE_HOOKS
if (__builtin_expect (statp->rhook, 0)) {
int done = 0, loops = 0; int done = 0, loops = 0;
do { do {
@ -593,6 +618,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
} while (!done); } while (!done);
} }
#endif
return (resplen); return (resplen);
next_ns: ; next_ns: ;
} /*foreach ns*/ } /*foreach ns*/
@ -612,7 +638,8 @@ int
res_nsend(res_state statp, res_nsend(res_state statp,
const u_char *buf, int buflen, u_char *ans, int anssiz) const u_char *buf, int buflen, u_char *ans, int anssiz)
{ {
return __libc_res_nsend(statp, buf, buflen, ans, anssiz, NULL); return __libc_res_nsend(statp, buf, buflen, NULL, 0, ans, anssiz,
NULL, NULL, NULL);
} }
libresolv_hidden_def (res_nsend) libresolv_hidden_def (res_nsend)
@ -620,17 +647,23 @@ libresolv_hidden_def (res_nsend)
static int static int
send_vc(res_state statp, send_vc(res_state statp,
const u_char *buf, int buflen, u_char **ansp, int *anssizp, const u_char *buf, int buflen, const u_char *buf2, int buflen2,
int *terrno, int ns, u_char **anscp) u_char **ansp, int *anssizp,
int *terrno, int ns, u_char **anscp, u_char **ansp2, int *anssizp2,
int *resplen2)
{ {
const HEADER *hp = (HEADER *) buf; const HEADER *hp = (HEADER *) buf;
const HEADER *hp2 = (HEADER *) buf2;
u_char *ans = *ansp; u_char *ans = *ansp;
int anssiz = *anssizp; int orig_anssizp = *anssizp;
// XXX REMOVE
// int anssiz = *anssizp;
HEADER *anhp = (HEADER *) ans; HEADER *anhp = (HEADER *) ans;
struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns]; struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns];
int truncating, connreset, resplen, n; int truncating, connreset, resplen, n;
struct iovec iov[2]; struct iovec iov[4];
u_short len; u_short len;
u_short len2;
u_char *cp; u_char *cp;
connreset = 0; connreset = 0;
@ -677,11 +710,19 @@ send_vc(res_state statp,
/* /*
* Send length & message * Send length & message
*/ */
ns_put16((u_short)buflen, (u_char*)&len); len = htons ((u_short) buflen);
evConsIovec(&len, INT16SZ, &iov[0]); evConsIovec(&len, INT16SZ, &iov[0]);
evConsIovec((void*)buf, buflen, &iov[1]); evConsIovec((void*)buf, buflen, &iov[1]);
if (TEMP_FAILURE_RETRY (writev(statp->_vcsock, iov, 2)) int niov = 2;
!= (INT16SZ + buflen)) { ssize_t explen = INT16SZ + buflen;
if (buf2 != NULL) {
len2 = htons ((u_short) buflen2);
evConsIovec(&len2, INT16SZ, &iov[2]);
evConsIovec((void*)buf2, buflen2, &iov[3]);
niov = 4;
explen += INT16SZ + buflen2;
}
if (TEMP_FAILURE_RETRY (writev(statp->_vcsock, iov, niov)) != explen) {
*terrno = errno; *terrno = errno;
Perror(statp, stderr, "write failed", errno); Perror(statp, stderr, "write failed", errno);
__res_iclose(statp, false); __res_iclose(statp, false);
@ -690,6 +731,8 @@ send_vc(res_state statp,
/* /*
* Receive length & response * Receive length & response
*/ */
int recvresp1 = 0;
int recvresp2 = buf2 == NULL;
read_len: read_len:
cp = ans; cp = ans;
len = INT16SZ; len = INT16SZ;
@ -718,30 +761,66 @@ send_vc(res_state statp,
} }
return (0); return (0);
} }
#ifdef _STRING_ARCH_unaligned
resplen = ntohs (*(uint16_t *) ans);
#else
resplen = ns_get16(ans); resplen = ns_get16(ans);
if (resplen > anssiz) { #endif
int *thisanssizp;
u_char **thisansp;
int *thisresplenp;
if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
thisanssizp = anssizp;
thisansp = anscp ?: ansp;
assert (anscp != NULL || ansp2 == NULL);
thisresplenp = &resplen;
} else {
if (*anssizp != MAXPACKET) {
/* No buffer allocated for the first
reply. We can try to use the rest
of the user-provided buffer. */
*anssizp2 = orig_anssizp - resplen;
*ansp2 = *ansp + resplen;
} else {
/* The first reply did not fit into the
user-provided buffer. Maybe the second
answer will. */
*anssizp2 = orig_anssizp;
*ansp2 = *ansp;
}
thisanssizp = anssizp2;
thisansp = ansp2;
thisresplenp = resplen2;
}
anhp = (HEADER *) *thisansp;
*thisresplenp = resplen;
if (resplen > *thisanssizp) {
/* Yes, we test ANSCP here. If we have two buffers
both will be allocatable. */
if (anscp) { if (anscp) {
ans = malloc (MAXPACKET); u_char *newp = malloc (MAXPACKET);
if (ans == NULL) { if (newp == NULL) {
*terrno = ENOMEM; *terrno = ENOMEM;
__res_iclose(statp, false); __res_iclose(statp, false);
return (0); return (0);
} }
anssiz = MAXPACKET; *thisanssizp = MAXPACKET;
*anssizp = MAXPACKET; *thisansp = newp;
*ansp = ans; anhp = (HEADER *) newp;
*anscp = ans;
anhp = (HEADER *) ans;
len = resplen; len = resplen;
} else { } else {
Dprint(statp->options & RES_DEBUG, Dprint(statp->options & RES_DEBUG,
(stdout, ";; response truncated\n") (stdout, ";; response truncated\n")
); );
truncating = 1; truncating = 1;
len = anssiz; len = *thisanssizp;
} }
} else } else
len = resplen; len = resplen;
if (len < HFIXEDSZ) { if (len < HFIXEDSZ) {
/* /*
* Undersized message. * Undersized message.
@ -752,7 +831,8 @@ send_vc(res_state statp,
__res_iclose(statp, false); __res_iclose(statp, false);
return (0); return (0);
} }
cp = ans;
cp = *thisansp;
while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){ while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){
cp += n; cp += n;
len -= n; len -= n;
@ -768,7 +848,7 @@ send_vc(res_state statp,
* Flush rest of answer so connection stays in synch. * Flush rest of answer so connection stays in synch.
*/ */
anhp->tc = 1; anhp->tc = 1;
len = resplen - anssiz; len = resplen - *thisanssizp;
while (len != 0) { while (len != 0) {
char junk[PACKETSZ]; char junk[PACKETSZ];
@ -787,14 +867,25 @@ send_vc(res_state statp,
* itself confused, then drop the packet and * itself confused, then drop the packet and
* wait for the correct one. * wait for the correct one.
*/ */
if (hp->id != anhp->id) { if ((recvresp1 || hp->id != anhp->id)
&& (recvresp2 || hp2->id != anhp->id)) {
DprintQ((statp->options & RES_DEBUG) || DprintQ((statp->options & RES_DEBUG) ||
(statp->pfcode & RES_PRF_REPLY), (statp->pfcode & RES_PRF_REPLY),
(stdout, ";; old answer (unexpected):\n"), (stdout, ";; old answer (unexpected):\n"),
ans, (resplen > anssiz) ? anssiz: resplen); *thisansp,
(resplen > *thisanssiz) ? *thisanssiz: resplen);
goto read_len; goto read_len;
} }
/* Mark which reply we received. */
if (recvresp1 == 0 && hp->id == anhp->id)
recvresp1 = 1;
else
recvresp2 = 1;
/* Repeat waiting if we have a second answer to arrive. */
if ((recvresp1 & recvresp2) == 0)
goto read_len;
/* /*
* All is well, or the error is fatal. Signal that the * All is well, or the error is fatal. Signal that the
* next nameserver ought not be tried. * next nameserver ought not be tried.
@ -804,19 +895,20 @@ send_vc(res_state statp,
static int static int
send_dg(res_state statp, send_dg(res_state statp,
const u_char *buf, int buflen, u_char **ansp, int *anssizp, const u_char *buf, int buflen, const u_char *buf2, int buflen2,
int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp) u_char **ansp, int *anssizp,
int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp,
u_char **ansp2, int *anssizp2, int *resplen2)
{ {
const HEADER *hp = (HEADER *) buf; const HEADER *hp = (HEADER *) buf;
const HEADER *hp2 = (HEADER *) buf2;
u_char *ans = *ansp; u_char *ans = *ansp;
int anssiz = *anssizp; int orig_anssizp = *anssizp;
HEADER *anhp = (HEADER *) ans;
struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns]; struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns];
struct timespec now, timeout, finish; struct timespec now, timeout, finish;
struct pollfd pfd[1]; struct pollfd pfd[1];
int ptimeout; int ptimeout;
struct sockaddr_in6 from; struct sockaddr_in6 from;
socklen_t fromlen;
int resplen, seconds, n; int resplen, seconds, n;
if (EXT(statp).nssocks[ns] == -1) { if (EXT(statp).nssocks[ns] == -1) {
@ -879,6 +971,8 @@ send_dg(res_state statp,
evAddTime(&finish, &now, &timeout); evAddTime(&finish, &now, &timeout);
int need_recompute = 0; int need_recompute = 0;
int nwritten = 0; int nwritten = 0;
int recvresp1 = 0;
int recvresp2 = buf2 == NULL;
pfd[0].fd = EXT(statp).nssocks[ns]; pfd[0].fd = EXT(statp).nssocks[ns];
pfd[0].events = POLLOUT; pfd[0].events = POLLOUT;
wait: wait:
@ -918,35 +1012,73 @@ send_dg(res_state statp,
} }
__set_errno (0); __set_errno (0);
if (pfd[0].revents & POLLOUT) { if (pfd[0].revents & POLLOUT) {
if (send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL) != buflen) { ssize_t sr;
if (nwritten != 0)
sr = send (pfd[0].fd, buf2, buflen2, MSG_NOSIGNAL);
else
sr = send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL);
if (sr != buflen) {
if (errno == EINTR || errno == EAGAIN) if (errno == EINTR || errno == EAGAIN)
goto recompute_resend; goto recompute_resend;
Perror(statp, stderr, "send", errno); Perror(statp, stderr, "send", errno);
goto err_out; goto err_out;
} }
pfd[0].events = POLLIN; if (nwritten != 0 || buf2 == NULL)
pfd[0].events = POLLIN;
else
pfd[0].events = POLLIN | POLLOUT;
++nwritten; ++nwritten;
goto wait; goto wait;
} else if (pfd[0].revents & POLLIN) { } else if (pfd[0].revents & POLLIN) {
fromlen = sizeof(struct sockaddr_in6); int *thisanssizp;
if (anssiz < MAXPACKET u_char **thisansp;
int *thisresplenp;
if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
thisanssizp = anssizp;
thisansp = anscp ?: ansp;
assert (anscp != NULL || ansp2 == NULL);
thisresplenp = &resplen;
} else {
if (*anssizp != MAXPACKET) {
/* No buffer allocated for the first
reply. We can try to use the rest
of the user-provided buffer. */
*anssizp2 = orig_anssizp - resplen;
*ansp2 = *ansp + resplen;
} else {
/* The first reply did not fit into the
user-provided buffer. Maybe the second
answer will. */
*anssizp2 = orig_anssizp;
*ansp2 = *ansp;
}
thisanssizp = anssizp2;
thisansp = ansp2;
thisresplenp = resplen2;
}
if (*thisanssizp < MAXPACKET
/* Yes, we test ANSCP here. If we have two buffers
both will be allocatable. */
&& anscp && anscp
&& (ioctl (pfd[0].fd, FIONREAD, &resplen) < 0 && (ioctl (pfd[0].fd, FIONREAD, thisresplenp) < 0
|| anssiz < resplen)) { || *thisanssizp < *thisresplenp)) {
ans = malloc (MAXPACKET); u_char *newp = malloc (MAXPACKET);
if (ans == NULL) if (newp != NULL) {
ans = *ansp;
else {
anssiz = MAXPACKET;
*anssizp = MAXPACKET; *anssizp = MAXPACKET;
*ansp = ans; *thisansp = ans = newp;
*anscp = ans;
anhp = (HEADER *) ans;
} }
} }
resplen = recvfrom(pfd[0].fd, (char*)ans, anssiz,0, HEADER *anhp = (HEADER *) *thisansp;
(struct sockaddr *)&from, &fromlen); socklen_t fromlen = sizeof(struct sockaddr_in6);
if (resplen <= 0) { assert (sizeof(from) <= fromlen);
*thisresplenp = recvfrom(pfd[0].fd, (char*)*thisansp,
*thisanssizp, 0,
(struct sockaddr *)&from, &fromlen);
if (*thisresplenp <= 0) {
if (errno == EINTR || errno == EAGAIN) { if (errno == EINTR || errno == EAGAIN) {
need_recompute = 1; need_recompute = 1;
goto wait; goto wait;
@ -955,17 +1087,18 @@ send_dg(res_state statp,
goto err_out; goto err_out;
} }
*gotsomewhere = 1; *gotsomewhere = 1;
if (resplen < HFIXEDSZ) { if (*thisresplenp < HFIXEDSZ) {
/* /*
* Undersized message. * Undersized message.
*/ */
Dprint(statp->options & RES_DEBUG, Dprint(statp->options & RES_DEBUG,
(stdout, ";; undersized: %d\n", (stdout, ";; undersized: %d\n",
resplen)); *thisresplen));
*terrno = EMSGSIZE; *terrno = EMSGSIZE;
goto err_out; goto err_out;
} }
if (hp->id != anhp->id) { if ((recvresp1 || hp->id != anhp->id)
&& (recvresp2 || hp2->id != anhp->id)) {
/* /*
* response from old query, ignore it. * response from old query, ignore it.
* XXX - potential security hazard could * XXX - potential security hazard could
@ -974,7 +1107,9 @@ send_dg(res_state statp,
DprintQ((statp->options & RES_DEBUG) || DprintQ((statp->options & RES_DEBUG) ||
(statp->pfcode & RES_PRF_REPLY), (statp->pfcode & RES_PRF_REPLY),
(stdout, ";; old answer:\n"), (stdout, ";; old answer:\n"),
ans, (resplen > anssiz) ? anssiz : resplen); thisansp,
(*thisresplen > *thisanssiz)
? *thisanssiz : *thisresplen);
goto wait; goto wait;
} }
if (!(statp->options & RES_INSECURE1) && if (!(statp->options & RES_INSECURE1) &&
@ -987,14 +1122,16 @@ send_dg(res_state statp,
DprintQ((statp->options & RES_DEBUG) || DprintQ((statp->options & RES_DEBUG) ||
(statp->pfcode & RES_PRF_REPLY), (statp->pfcode & RES_PRF_REPLY),
(stdout, ";; not our server:\n"), (stdout, ";; not our server:\n"),
ans, (resplen > anssiz) ? anssiz : resplen); thisansp,
(*thisresplen > *thisanssiz)
? *thisanssiz : *thisresplen);
goto wait; goto wait;
} }
#ifdef RES_USE_EDNS0 #ifdef RES_USE_EDNS0
if (anhp->rcode == FORMERR if (anhp->rcode == FORMERR
&& (statp->options & RES_USE_EDNS0) != 0U) { && (statp->options & RES_USE_EDNS0) != 0U) {
/* /*
* Do not retry if the server do not understand * Do not retry if the server does not understand
* EDNS0. The case has to be captured here, as * EDNS0. The case has to be captured here, as
* FORMERR packet do not carry query section, hence * FORMERR packet do not carry query section, hence
* res_queriesmatch() returns 0. * res_queriesmatch() returns 0.
@ -1002,15 +1139,23 @@ send_dg(res_state statp,
DprintQ(statp->options & RES_DEBUG, DprintQ(statp->options & RES_DEBUG,
(stdout, (stdout,
"server rejected query with EDNS0:\n"), "server rejected query with EDNS0:\n"),
ans, (resplen > anssiz) ? anssiz : resplen); thisans,
(*thisresplen > *thisanssiz)
? *thisanssiz : *thisresplen);
/* record the error */ /* record the error */
statp->_flags |= RES_F_EDNS0ERR; statp->_flags |= RES_F_EDNS0ERR;
goto err_out; goto err_out;
} }
#endif #endif
if (!(statp->options & RES_INSECURE2) && if (!(statp->options & RES_INSECURE2)
!res_queriesmatch(buf, buf + buflen, && (recvresp1 || !res_queriesmatch(buf, buf + buflen,
ans, ans + anssiz)) { *thisansp,
*thisansp
+ *thisanssizp))
&& (recvresp2 || !res_queriesmatch(buf2, buf2 + buflen2,
*thisansp,
*thisansp
+ *thisanssizp))) {
/* /*
* response contains wrong query? ignore it. * response contains wrong query? ignore it.
* XXX - potential security hazard could * XXX - potential security hazard could
@ -1019,7 +1164,9 @@ send_dg(res_state statp,
DprintQ((statp->options & RES_DEBUG) || DprintQ((statp->options & RES_DEBUG) ||
(statp->pfcode & RES_PRF_REPLY), (statp->pfcode & RES_PRF_REPLY),
(stdout, ";; wrong query name:\n"), (stdout, ";; wrong query name:\n"),
ans, (resplen > anssiz) ? anssiz : resplen); thisansp,
(*thisresplen > *thisanssiz)
? *thisanssiz : *thisresplen);
goto wait; goto wait;
} }
if (anhp->rcode == SERVFAIL || if (anhp->rcode == SERVFAIL ||
@ -1027,7 +1174,9 @@ send_dg(res_state statp,
anhp->rcode == REFUSED) { anhp->rcode == REFUSED) {
DprintQ(statp->options & RES_DEBUG, DprintQ(statp->options & RES_DEBUG,
(stdout, "server rejected query:\n"), (stdout, "server rejected query:\n"),
ans, (resplen > anssiz) ? anssiz : resplen); thisansp,
(*thisresplen > *thisanssiz)
? *thisanssiz : *thisresplen);
next_ns: next_ns:
__res_iclose(statp, false); __res_iclose(statp, false);
/* don't retry if called from dig */ /* don't retry if called from dig */
@ -1038,7 +1187,9 @@ send_dg(res_state statp,
&& anhp->aa == 0 && anhp->ra == 0 && anhp->arcount == 0) { && anhp->aa == 0 && anhp->ra == 0 && anhp->arcount == 0) {
DprintQ(statp->options & RES_DEBUG, DprintQ(statp->options & RES_DEBUG,
(stdout, "referred query:\n"), (stdout, "referred query:\n"),
ans, (resplen > anssiz) ? anssiz : resplen); thisansp,
(*thisresplen > *thisanssiz)
? *thisanssiz : *thisresplen);
goto next_ns; goto next_ns;
} }
if (!(statp->options & RES_IGNTC) && anhp->tc) { if (!(statp->options & RES_IGNTC) && anhp->tc) {
@ -1050,8 +1201,18 @@ send_dg(res_state statp,
(stdout, ";; truncated answer\n")); (stdout, ";; truncated answer\n"));
*v_circuit = 1; *v_circuit = 1;
__res_iclose(statp, false); __res_iclose(statp, false);
// XXX if we have received one reply we could
// XXX use it and not repeat it over TCP...
return (1); return (1);
} }
/* Mark which reply we received. */
if (recvresp1 == 0 && hp->id == anhp->id)
recvresp1 = 1;
else
recvresp2 = 1;
/* Repeat waiting if we have a second answer to arrive. */
if ((recvresp1 & recvresp2) == 0)
goto wait;
/* /*
* All is well, or the error is fatal. Signal that the * All is well, or the error is fatal. Signal that the
* next nameserver ought not be tried. * next nameserver ought not be tried.

View File

@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <errno.h> #include <errno.h>
#include <ifaddrs.h> #include <ifaddrs.h>
#include <netdb.h> #include <netdb.h>
#include <nss.h>
#include <resolv.h> #include <resolv.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
@ -91,14 +92,6 @@ struct gaih_servtuple
static const struct gaih_servtuple nullserv; static const struct gaih_servtuple nullserv;
struct gaih_addrtuple
{
struct gaih_addrtuple *next;
char *name;
int family;
uint32_t addr[4];
uint32_t scopeid;
};
struct gaih_typeproto struct gaih_typeproto
{ {
@ -202,6 +195,7 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
if (herrno == NETDB_INTERNAL) \ if (herrno == NETDB_INTERNAL) \
{ \ { \
__set_h_errno (herrno); \ __set_h_errno (herrno); \
_res.options = old_res_options; \
return -EAI_SYSTEM; \ return -EAI_SYSTEM; \
} \ } \
if (herrno == TRY_AGAIN) \ if (herrno == TRY_AGAIN) \
@ -246,6 +240,10 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
} }
typedef enum nss_status (*nss_gethostbyname4_r)
(const char *name, struct gaih_addrtuple **pat,
char *buffer, size_t buflen, int *errnop,
int *h_errnop, int32_t *ttlp);
typedef enum nss_status (*nss_gethostbyname3_r) typedef enum nss_status (*nss_gethostbyname3_r)
(const char *name, int af, struct hostent *host, (const char *name, int af, struct hostent *host,
char *buffer, size_t buflen, int *errnop, char *buffer, size_t buflen, int *errnop,
@ -685,87 +683,132 @@ gaih_inet (const char *name, const struct gaih_service *service,
while (!no_more) while (!no_more)
{ {
nss_gethostbyname3_r fct = NULL; nss_gethostbyname4_r fct4
if (req->ai_flags & AI_CANONNAME) = __nss_lookup_function (nip, "gethostbyname4_r");
/* No need to use this function if we do not look for if (fct4 != NULL)
the canonical name. The function does not exist in
all NSS modules and therefore the lookup would
often fail. */
fct = __nss_lookup_function (nip, "gethostbyname3_r");
if (fct == NULL)
/* We are cheating here. The gethostbyname2_r function does
not have the same interface as gethostbyname3_r but the
extra arguments the latter takes are added at the end.
So the gethostbyname2_r code will just ignore them. */
fct = __nss_lookup_function (nip, "gethostbyname2_r");
if (fct != NULL)
{ {
if (req->ai_family == AF_INET6 int herrno;
|| req->ai_family == AF_UNSPEC)
{
gethosts (AF_INET6, struct in6_addr);
no_inet6_data = no_data;
inet6_status = status;
}
if (req->ai_family == AF_INET
|| req->ai_family == AF_UNSPEC
|| (req->ai_family == AF_INET6
&& (req->ai_flags & AI_V4MAPPED)
/* Avoid generating the mapped addresses if we
know we are not going to need them. */
&& ((req->ai_flags & AI_ALL) || !got_ipv6)))
{
gethosts (AF_INET, struct in_addr);
if (req->ai_family == AF_INET) while (1)
{
rc = 0;
status = DL_CALL_FCT (fct4, (name, pat, tmpbuf,
tmpbuflen, &rc, &herrno,
NULL));
if (status != NSS_STATUS_TRYAGAIN
|| rc != ERANGE || herrno != NETDB_INTERNAL)
{ {
if (herrno == NETDB_INTERNAL)
{
__set_h_errno (herrno);
_res.options = old_res_options;
return -EAI_SYSTEM;
}
if (herrno == TRY_AGAIN)
no_data = EAI_AGAIN;
else
no_data = herrno == NO_DATA;
break;
}
tmpbuf = extend_alloca (tmpbuf,
tmpbuflen, 2 * tmpbuflen);
}
if (status == NSS_STATUS_SUCCESS)
{
canon = (*pat)->name;
while (*pat != NULL)
pat = &((*pat)->next);
}
}
else
{
nss_gethostbyname3_r fct = NULL;
if (req->ai_flags & AI_CANONNAME)
/* No need to use this function if we do not look for
the canonical name. The function does not exist in
all NSS modules and therefore the lookup would
often fail. */
fct = __nss_lookup_function (nip, "gethostbyname3_r");
if (fct == NULL)
/* We are cheating here. The gethostbyname2_r
function does not have the same interface as
gethostbyname3_r but the extra arguments the
latter takes are added at the end. So the
gethostbyname2_r code will just ignore them. */
fct = __nss_lookup_function (nip, "gethostbyname2_r");
if (fct != NULL)
{
if (req->ai_family == AF_INET6
|| req->ai_family == AF_UNSPEC)
{
gethosts (AF_INET6, struct in6_addr);
no_inet6_data = no_data; no_inet6_data = no_data;
inet6_status = status; inet6_status = status;
} }
} if (req->ai_family == AF_INET
|| req->ai_family == AF_UNSPEC
/* If we found one address for AF_INET or AF_INET6, || (req->ai_family == AF_INET6
don't continue the search. */ && (req->ai_flags & AI_V4MAPPED)
if (inet6_status == NSS_STATUS_SUCCESS /* Avoid generating the mapped addresses if we
|| status == NSS_STATUS_SUCCESS) know we are not going to need them. */
{ && ((req->ai_flags & AI_ALL) || !got_ipv6)))
if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
{ {
/* If we need the canonical name, get it gethosts (AF_INET, struct in_addr);
from the same service as the result. */
nss_getcanonname_r cfct;
int herrno;
cfct = __nss_lookup_function (nip, "getcanonname_r"); if (req->ai_family == AF_INET)
if (cfct != NULL)
{ {
const size_t max_fqdn_len = 256; no_inet6_data = no_data;
char *buf = alloca (max_fqdn_len); inet6_status = status;
char *s;
if (DL_CALL_FCT (cfct, (at->name ?: name, buf,
max_fqdn_len, &s, &rc,
&herrno))
== NSS_STATUS_SUCCESS)
canon = s;
else
/* Set to name now to avoid using
gethostbyaddr. */
canon = name;
} }
} }
break; /* If we found one address for AF_INET or AF_INET6,
} don't continue the search. */
if (inet6_status == NSS_STATUS_SUCCESS
|| status == NSS_STATUS_SUCCESS)
{
if ((req->ai_flags & AI_CANONNAME) != 0
&& canon == NULL)
{
/* If we need the canonical name, get it
from the same service as the result. */
nss_getcanonname_r cfct;
int herrno;
/* We can have different states for AF_INET and cfct = __nss_lookup_function (nip,
AF_INET6. Try to find a useful one for both. */ "getcanonname_r");
if (inet6_status == NSS_STATUS_TRYAGAIN) if (cfct != NULL)
status = NSS_STATUS_TRYAGAIN; {
else if (status == NSS_STATUS_UNAVAIL const size_t max_fqdn_len = 256;
&& inet6_status != NSS_STATUS_UNAVAIL) char *buf = alloca (max_fqdn_len);
status = inet6_status; char *s;
if (DL_CALL_FCT (cfct, (at->name ?: name,
buf, max_fqdn_len,
&s, &rc, &herrno))
== NSS_STATUS_SUCCESS)
canon = s;
else
/* Set to name now to avoid using
gethostbyaddr. */
canon = name;
}
}
break;
}
/* We can have different states for AF_INET and
AF_INET6. Try to find a useful one for both. */
if (inet6_status == NSS_STATUS_TRYAGAIN)
status = NSS_STATUS_TRYAGAIN;
else if (status == NSS_STATUS_UNAVAIL
&& inet6_status != NSS_STATUS_UNAVAIL)
status = inet6_status;
}
} }
if (nss_next_action (nip, status) == NSS_ACTION_RETURN) if (nss_next_action (nip, status) == NSS_ACTION_RETURN)