From 1eb946b93509b94db2bddce741f2f3b483418a6d Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sat, 10 May 2008 23:27:39 +0000 Subject: [PATCH] * 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. --- ChangeLog | 45 ++++ include/arpa/nameser_compat.h | 7 + nis/Versions | 3 +- nis/nss_nis/nis-hosts.c | 118 +++++++++- nis/nss_nisplus/nisplus-hosts.c | 222 +++++++++++------- nscd/aicache.c | 488 ++++++++++++++++++++++++---------------- nss/Versions | 1 + nss/nss.h | 14 +- nss/nss_files/files-hosts.c | 133 ++++++++++- resolv/Versions | 1 + resolv/gethnamaddr.c | 6 +- resolv/nss_dns/dns-canon.c | 4 +- resolv/nss_dns/dns-host.c | 337 ++++++++++++++++++++++++++- resolv/nss_dns/dns-network.c | 6 +- resolv/res_query.c | 149 +++++++++--- resolv/res_send.c | 323 +++++++++++++++++++------- sysdeps/posix/getaddrinfo.c | 189 ++++++++++------ 17 files changed, 1542 insertions(+), 504 deletions(-) diff --git a/ChangeLog b/ChangeLog index cbeb159..c20a02d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,48 @@ +2008-05-10 Ulrich Drepper + + * 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 * sysdeps/sparc/sparc32/Makefile: Use -mcpu=v7 for initfini.s build. diff --git a/include/arpa/nameser_compat.h b/include/arpa/nameser_compat.h index bfbb2de..2e735ed 100644 --- a/include/arpa/nameser_compat.h +++ b/include/arpa/nameser_compat.h @@ -1 +1,8 @@ +#ifndef _ARPA_NAMESER_COMPAT_ #include + +/* Picksome unused number to represent lookups of IPv4 and IPv6 (i.e., + T_A and T_AAAA). */ +#define T_UNSPEC 62321 + +#endif diff --git a/nis/Versions b/nis/Versions index be4453e..ef9a512 100644 --- a/nis/Versions +++ b/nis/Versions @@ -95,7 +95,7 @@ libnss_nis { _nss_nis_setgrent; _nss_nis_sethostent; _nss_nis_setnetent; _nss_nis_setnetgrent; _nss_nis_setprotoent; _nss_nis_setpwent; _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_setpwent; _nss_nisplus_setrpcent; _nss_nisplus_setservent; _nss_nisplus_setspent; _nss_nisplus_initgroups_dyn; + _nss_nisplus_gethostbyname4_r; } } diff --git a/nis/nss_nis/nis-hosts.c b/nis/nss_nis/nis-hosts.c index 7bf4af7..24d1363 100644 --- a/nis/nss_nis/nis-hosts.c +++ b/nis/nss_nis/nis-hosts.c @@ -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. Contributed by Thorsten Kukuk , 1996. @@ -17,6 +18,7 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#include #include #include /* The following is an ugly trick to avoid a prototype declaration for @@ -61,9 +63,12 @@ LINE_PARSER STRING_FIELD (addr, isspace, 1); + assert (af == AF_INET || af == AF_INET6 || af == AF_UNSPEC); + /* 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) { map_v4v6_address ((char *) entdata->host_addr, @@ -77,7 +82,7 @@ LINE_PARSER result->h_length = INADDRSZ; } } - else if (af == AF_INET6 + else if (af != AF_INET && inet_pton (AF_INET6, addr, entdata->host_addr) > 0) { result->h_addrtype = AF_INET6; @@ -102,6 +107,7 @@ static bool_t new_start = 1; static char *oldkey = NULL; static int oldkeylen = 0; + enum nss_status _nss_nis_sethostent (int stayopen) { @@ -124,6 +130,7 @@ _nss_nis_sethostent (int stayopen) is used so this makes no difference. */ strong_alias (_nss_nis_sethostent, _nss_nis_endhostent) + /* The calling function always need to get a lock first. */ static enum nss_status 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; } + enum nss_status _nss_nis_gethostent_r (struct hostent *host, char *buffer, size_t buflen, int *errnop, int *h_errnop) @@ -233,6 +241,7 @@ _nss_nis_gethostent_r (struct hostent *host, char *buffer, size_t buflen, return status; } + static enum nss_status internal_gethostbyname2_r (const char *name, int af, struct hostent *host, 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; } + enum nss_status _nss_nis_gethostbyname2_r (const char *name, int af, struct hostent *host, char *buffer, size_t buflen, int *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, h_errnop, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)); } + enum nss_status _nss_nis_gethostbyname_r (const char *name, struct hostent *host, char *buffer, 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); } + enum nss_status _nss_nis_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af, 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; } -#if 0 + enum nss_status -_nss_nis_getipnodebyname_r (const char *name, int af, int flags, - struct hostent *result, char *buffer, - size_t buflen, int *errnop, int *herrnop) +_nss_nis_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat, + char *buffer, size_t buflen, int *errnop, + int *herrnop, int32_t *ttlp) { - return internal_gethostbyname2_r (name, af, result, buffer, buflen, - errnop, herrnop, flags); + char *domain; + 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 diff --git a/nis/nss_nisplus/nisplus-hosts.c b/nis/nss_nisplus/nisplus-hosts.c index f5f0ac9..37d4477 100644 --- a/nis/nss_nisplus/nisplus-hosts.c +++ b/nis/nss_nisplus/nisplus-hosts.c @@ -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. Contributed by Thorsten Kukuk , 1997. @@ -17,6 +17,7 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#include #include #include #include @@ -58,15 +59,15 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host, if (result == NULL) return 0; - if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) || - __type_of (NIS_RES_OBJECT (result)) != NIS_ENTRY_OBJ || - 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) + if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) + || __type_of (NIS_RES_OBJECT (result)) != NIS_ENTRY_OBJ + || 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) return 0; 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)) { no_more_room: @@ -75,8 +76,10 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host, } /* 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) { 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; } } - else if (af == AF_INET6 + else if (af != AF_INET && inet_pton (AF_INET6, NISENTRYVAL (0, 2, result), data) > 0) { 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), NISENTRYLEN (0, 0, result)); *first_unused++ = '\0'; - 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. */ + room_left -= NISENTRYLEN (0, 0, result) + 1; char *line = first_unused; - for (i = 0; i < NIS_RES_NUMOBJ (result); ++i) + + /* When this is a call to gethostbyname4_r we do not need the aliases. */ + if (af != AF_UNSPEC) { - if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0) + /* XXX Rewrite at some point to allocate the array first and then + 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 (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; + 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 storing pointers. */ @@ -147,30 +156,34 @@ _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host, host->h_addr_list[1] = NULL; host->h_aliases = &host->h_addr_list[2]; - i = 0; - while (*line != '\0') + /* When this is a call to gethostbyname4_r we do not need the aliases. */ + if (af != AF_UNSPEC) { - /* Skip leading blanks. */ - while (isspace (*line)) - ++line; + i = 0; + while (*line != '\0') + { + /* Skip leading blanks. */ + while (isspace (*line)) + ++line; - if (*line == '\0') - break; + if (*line == '\0') + break; - if (room_left < sizeof (char *)) - goto no_more_room; + if (room_left < sizeof (char *)) + goto no_more_room; - room_left -= sizeof (char *); - host->h_aliases[i++] = line; + room_left -= sizeof (char *); + host->h_aliases[i++] = line; - while (*line != '\0' && *line != ' ') - ++line; + while (*line != '\0' && *line != ' ') + ++line; - if (*line == ' ') - *line++ = '\0'; - } + if (*line == ' ') + *line++ = '\0'; + } - host->h_aliases[i] = NULL; + host->h_aliases[i] = NULL; + } return 1; } @@ -204,6 +217,7 @@ _nss_create_tablename (int *errnop) return NSS_STATUS_SUCCESS; } + enum nss_status _nss_nisplus_sethostent (int stayopen) { @@ -226,6 +240,7 @@ _nss_nisplus_sethostent (int stayopen) return status; } + enum nss_status _nss_nisplus_endhostent (void) { @@ -242,6 +257,7 @@ _nss_nisplus_endhostent (void) return NSS_STATUS_SUCCESS; } + static enum nss_status internal_nisplus_gethostent_r (struct hostent *host, char *buffer, size_t buflen, int *errnop, int *herrnop) @@ -329,6 +345,7 @@ internal_nisplus_gethostent_r (struct hostent *host, char *buffer, return NSS_STATUS_SUCCESS; } + enum nss_status _nss_nisplus_gethostent_r (struct hostent *result, char *buffer, size_t buflen, int *errnop, int *herrnop) @@ -345,26 +362,33 @@ _nss_nisplus_gethostent_r (struct hostent *result, char *buffer, 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 internal_gethostbyname2_r (const char *name, int af, struct hostent *host, char *buffer, size_t buflen, int *errnop, int *herrnop, int flags) { - int parse_res, retval; - if (tablename_val == NULL) { - __libc_lock_lock (lock); - - enum nss_status status = _nss_create_tablename (errnop); - - __libc_lock_unlock (lock); - + enum nss_status status = get_tablename (herrnop); if (status != NSS_STATUS_SUCCESS) - { - *herrnop = NETDB_INTERNAL; - return NSS_STATUS_UNAVAIL; - } + return status; } if (name == NULL) @@ -374,39 +398,36 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host, return NSS_STATUS_NOTFOUND; } - nis_result *result; char buf[strlen (name) + 10 + tablename_len]; int olderr = errno; /* Search at first in the alias list, and use the correct name for the next search. */ 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) { - char *bufptr = buf; - /* 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. */ - if ((result->status != NIS_SUCCESS - && result->status != NIS_S_SUCCESS) - || __type_of (result->objects.objects_val) != NIS_ENTRY_OBJ - || strcmp (result->objects.objects_val->EN_data.en_type, - "hosts_tbl") != 0 - || result->objects.objects_val->EN_data.en_cols.en_cols_len < 3) - snprintf (buf, sizeof (buf), "[cname=%s],%s", name, tablename_val); - else + char *bufptr = buf; + size_t buflen = sizeof (buf); + + if ((result->status == NIS_SUCCESS || result->status == NIS_S_SUCCESS) + && __type_of (result->objects.objects_val) == NIS_ENTRY_OBJ + && strcmp (result->objects.objects_val->EN_data.en_type, + "hosts_tbl") == 0 + && result->objects.objects_val->EN_data.en_cols.en_cols_len >= 3) { /* We need to allocate a new buffer since there is no - guarantee the returned name has a length limit. */ - const char *entryval = NISENTRYVAL(0, 0, result); - size_t buflen = strlen (entryval) + 10 + tablename_len; + guarantee the returned alias name has a length limit. */ + name = NISENTRYVAL(0, 0, result); + size_t buflen = strlen (name) + 10 + tablename_len; bufptr = alloca (buflen); - snprintf (bufptr, buflen, "[cname=%s],%s", - entryval, tablename_val); } + snprintf (bufptr, buflen, "[cname=%s],%s", name, tablename_val); + nis_freeresult (result); 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; } - retval = niserr2nss (result->status); + int retval = niserr2nss (result->status); if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0)) { if (retval == NSS_STATUS_TRYAGAIN) @@ -431,8 +452,8 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host, return retval; } - parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer, - buflen, errnop, flags); + int parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer, + buflen, errnop, flags); nis_freeresult (result); @@ -450,16 +471,24 @@ internal_gethostbyname2_r (const char *name, int af, struct hostent *host, return NSS_STATUS_NOTFOUND; } + enum nss_status _nss_nisplus_gethostbyname2_r (const char *name, int af, struct hostent *host, char *buffer, size_t buflen, int *errnop, 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, herrnop, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)); } + enum nss_status _nss_nisplus_gethostbyname_r (const char *name, struct hostent *host, 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); } + enum nss_status _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af, 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) { - __libc_lock_lock (lock); - - enum nss_status status = _nss_create_tablename (errnop); - - __libc_lock_unlock (lock); - + enum nss_status status = get_tablename (herrnop); if (status != NSS_STATUS_SUCCESS) return status; } @@ -547,3 +572,44 @@ _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af, __set_errno (olderr); 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; +} diff --git a/nscd/aicache.c b/nscd/aicache.c index 2518f80..4db3e65 100644 --- a/nscd/aicache.c +++ b/nscd/aicache.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,10 @@ #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) (const char *name, int af, struct hostent *host, char *buffer, size_t buflen, int *errnop, @@ -117,16 +122,104 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req, while (!no_more) { + void *cp; int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL }; + int naddrs = 0; + size_t addrslen = 0; + size_t canonlen; + + nss_gethostbyname4_r fct4 = __nss_lookup_function (nip, + "gethostbyname4_r"); + if (fct4 != 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; - /* 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 (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; + } - if (fct != NULL) + /* 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]; /* Collect IPv6 information first. */ @@ -134,8 +227,8 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req, { rc6 = 0; status[0] = DL_CALL_FCT (fct, (key, AF_INET6, &th[0], tmpbuf6, - tmpbuf6len, &rc6, &herrno, - &ttl, &canon)); + tmpbuf6len, &rc6, &herrno, &ttl, + &canon)); if (rc6 != ERANGE || herrno != NETDB_INTERNAL) break; 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) goto out; - if (status[0] == NSS_STATUS_SUCCESS - || status[1] == NSS_STATUS_SUCCESS) + if (status[0] != 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. */ - int naddrs = 0; - size_t addrslen = 0; - for (int j = 0; j < 2; ++j) - if (status[j] == NSS_STATUS_SUCCESS) - for (int i = 0; th[j].h_addr_list[i] != NULL; ++i) + /* Determine the canonical name. */ + nss_getcanonname_r cfct; + cfct = __nss_lookup_function (nip, "getcanonname_r"); + if (cfct != NULL) + { + const size_t max_fqdn_len = 256; + char *buf = alloca (max_fqdn_len); + char *s; + int rc; + + if (DL_CALL_FCT (cfct, (key, buf, max_fqdn_len, &s, + &rc, &herrno)) + == NSS_STATUS_SUCCESS) + canon = s; + else + /* Set to name now to avoid using gethostbyaddr. */ + canon = key; + } + else + { + struct hostent *he = NULL; + 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 { - ++naddrs; - addrslen += th[j].h_length; + addr = th[0].h_addr_list[0]; + addrlen = sizeof (struct in6_addr); + addrfamily = AF_INET6; } - if (canon == NULL) - { - /* Determine the canonical name. */ - nss_getcanonname_r cfct; - cfct = __nss_lookup_function (nip, "getcanonname_r"); - if (cfct != NULL) + size_t tmpbuflen = 512; + char *tmpbuf = alloca (tmpbuflen); + int rc; + while (1) { - const size_t max_fqdn_len = 256; - char *buf = alloca (max_fqdn_len); - char *s; - int rc; - - if (DL_CALL_FCT (cfct, (key, buf, max_fqdn_len, &s, &rc, - &herrno)) == NSS_STATUS_SUCCESS) - canon = s; - else - /* Set to name now to avoid using gethostbyaddr. */ - canon = key; + 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); } - else + + if (rc == 0) { - struct hostent *he = NULL; - 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; - } + if (he != NULL) + canon = he->h_name; 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; - } + canon = key; } } - size_t canonlen = canon == NULL ? 0 : (strlen (canon) + 1); + } - total = sizeof (*dataset) + naddrs + addrslen + canonlen; + 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; - } + /* 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) - { - /* 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); + ++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; + } - /* 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; } - 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; - } + cp = family; + } - void *cp = family; - if (canon != NULL) - cp = mempcpy (cp, canon, canonlen); + /* 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; - key_copy = memcpy (cp, key, req->key_len); + /* Compute the timeout time. */ + dataset->head.timeout = time (NULL) + (ttl == INT32_MAX + ? db->postimeout : ttl); - /* Now we can determine whether on refill we have to - create a new record or not. */ - if (he != NULL) - { - assert (fd == -1); + 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 (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; + if (canon != NULL) + cp = mempcpy (cp, canon, canonlen); - /* Mark the old record as obsolete. */ - dh->usable = false; - } + 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 - { - /* 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); + ++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); + 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; + if (written == -1 && errno == ENOSYS) + goto use_write; # endif - } - else + } + else # ifndef __ASSUME_SENDFILE - use_write: + use_write: # endif #endif - writeall (fd, &dataset->resp, total); - } - - goto out; - } - + writeall (fd, &dataset->resp, total); } + goto out; + +next_nip: if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN) break; diff --git a/nss/Versions b/nss/Versions index 8f2f0fb..f7f0e56 100644 --- a/nss/Versions +++ b/nss/Versions @@ -38,6 +38,7 @@ libnss_files { _nss_files_endhostent; _nss_files_gethostbyaddr_r; _nss_files_gethostbyname2_r; + _nss_files_gethostbyname4_r; _nss_files_gethostbyname_r; _nss_files_gethostent_r; _nss_files_gethostton_r; diff --git a/nss/nss.h b/nss/nss.h index f5c12af..611b258 100644 --- a/nss/nss.h +++ b/nss/nss.h @@ -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. The GNU C Library is free software; you can redistribute it and/or @@ -23,6 +23,7 @@ #define _NSS_H 1 #include +#include __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 in STRING. This function should only be used by system programs which have to diff --git a/nss/nss_files/files-hosts.c b/nss/nss_files/files-hosts.c index b064672..7b69d47 100644 --- a/nss/nss_files/files-hosts.c +++ b/nss/nss_files/files-hosts.c @@ -1,6 +1,5 @@ /* Hosts file parser in nss_files module. - Copyright (C) 1996-2001, 2003, 2004, 2006, 2007 - Free Software Foundation, Inc. + Copyright (C) 1996-2001, 2003-2007, 2008 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -56,7 +55,10 @@ LINE_PARSER STRING_FIELD (addr, isspace, 1); /* 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 && inet_pton (AF_INET, addr, entdata->host_addr) > 0) @@ -76,6 +78,9 @@ LINE_PARSER /* Illegal address: ignore line. */ return 0; } + else if (af == AF_UNSPEC + && inet_pton (AF_INET6, addr, entdata->host_addr) > 0) + af = AF_INET6; else /* Illegal address: ignore line. */ return 0; @@ -101,8 +106,6 @@ _nss_files_get##name##_r (proto, \ struct STRUCTURE *result, char *buffer, \ size_t buflen, int *errnop H_ERRNO_PROTO) \ { \ - enum nss_status status; \ - \ uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data); \ buffer += pad; \ buflen = buflen > pad ? buflen - pad : 0; \ @@ -110,7 +113,7 @@ _nss_files_get##name##_r (proto, \ __libc_lock_lock (lock); \ \ /* 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) \ { \ @@ -288,9 +291,9 @@ HOST_DB_LOOKUP (hostbyname, ,, { LOOKUP_NAME_CASE (h_name, h_aliases) }, const char *name) +#undef EXTRA_ARGS_VALUE -#undef EXTRA_ARGS_VALUE /* XXX Is using _res to determine whether we want to convert IPv4 addresses to IPv6 addresses really the right thing to do? */ #define EXTRA_ARGS_VALUE \ @@ -299,8 +302,9 @@ HOST_DB_LOOKUP (hostbyname2, ,, { LOOKUP_NAME_CASE (h_name, h_aliases) }, const char *name, int af) - #undef EXTRA_ARGS_VALUE + + /* We only need to consider IPv4 mapped addresses if the input to the gethostbyaddr() function is an IPv6 address. */ #define EXTRA_ARGS_VALUE \ @@ -311,3 +315,116 @@ DB_LOOKUP (hostbyaddr, ,, && ! memcmp (addr, result->h_addr_list[0], len)) break; }, 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; +} diff --git a/resolv/Versions b/resolv/Versions index fc2111a..355d95d 100644 --- a/resolv/Versions +++ b/resolv/Versions @@ -89,6 +89,7 @@ libnss_dns { _nss_dns_gethostbyname_r; _nss_dns_getnetbyaddr_r; _nss_dns_getnetbyname_r; _nss_dns_getcanonname_r; _nss_dns_gethostbyaddr2_r; + _nss_dns_gethostbyname4_r; } } diff --git a/resolv/gethnamaddr.c b/resolv/gethnamaddr.c index 7be2315..2a9bd0b 100644 --- a/resolv/gethnamaddr.c +++ b/resolv/gethnamaddr.c @@ -621,7 +621,7 @@ gethostbyname2(name, af) buf.buf = origbuf = (querybuf *) alloca (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) free (buf.buf); Dprintf("res_nsearch failed (%d)\n", n); @@ -716,12 +716,12 @@ gethostbyaddr(addr, len, af) buf.buf = orig_buf = (querybuf *) alloca (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) { strcpy(qp, "ip6.int"); n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf, buf.buf != orig_buf ? MAXPACKET : 1024, - &buf.ptr); + &buf.ptr, NULL, NULL); } if (n < 0) { if (buf.buf != orig_buf) diff --git a/resolv/nss_dns/dns-canon.c b/resolv/nss_dns/dns-canon.c index fca6cd8..47949b8 100644 --- a/resolv/nss_dns/dns-canon.c +++ b/resolv/nss_dns/dns-canon.c @@ -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. Contributed by Ulrich Drepper , 2004. @@ -61,7 +61,7 @@ _nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen, for (int i = 0; i < nqtypes; ++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) { /* We need to decode the response. Just one question record. diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c index dfdf9c5..c52f9f7 100644 --- a/resolv/nss_dns/dns-host.c +++ b/resolv/nss_dns/dns-host.c @@ -71,6 +71,7 @@ * --Copyright-- */ +#include #include #include #include @@ -127,6 +128,14 @@ static enum nss_status getanswer_r (const querybuf *answer, int anslen, size_t buflen, int *errnop, int *h_errnop, 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, struct hostent *result, 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); 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) { - enum nss_status status = (errno == ECONNREFUSED - ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND); + status = (errno == ECONNREFUSED + ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND); *h_errnop = h_errno; if (h_errno == TRY_AGAIN) *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)) n = __libc_res_nsearch (&_res, name, C_IN, T_A, host_buffer.buf->buf, host_buffer.buf != orig_host_buffer - ? MAXPACKET : 1024, &host_buffer.ptr); + ? MAXPACKET : 1024, &host_buffer.ptr, + NULL, NULL); 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, socklen_t len, int af, 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]); strcpy (qp, "].ip6.arpa"); 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) 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, - 1024, &host_buffer.ptr); + 1024, &host_buffer.ptr, NULL, NULL); if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0) { strcpy (qp, "ip6.int"); n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf, host_buffer.buf != orig_host_buffer - ? MAXPACKET : 1024, &host_buffer.ptr); + ? MAXPACKET : 1024, &host_buffer.ptr, + NULL, NULL); } if (n < 0) { @@ -555,7 +631,8 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype, if (n > 0 && bp[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; *h_errnop = NO_RECOVERY; @@ -629,7 +706,7 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype, 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]) 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 ? 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; +} diff --git a/resolv/nss_dns/dns-network.c b/resolv/nss_dns/dns-network.c index 4552b5b..40736fb 100644 --- a/resolv/nss_dns/dns-network.c +++ b/resolv/nss_dns/dns-network.c @@ -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. This file is part of the GNU C Library. Extended from original form by Ulrich Drepper , 1996. @@ -130,7 +130,7 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result, net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024); 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) { /* 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); 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) { /* Nothing found. */ diff --git a/resolv/res_query.c b/resolv/res_query.c index 4371af5..a8e8d7b 100644 --- a/resolv/res_query.c +++ b/resolv/res_query.c @@ -97,7 +97,7 @@ static const char rcsid[] = "$BINDId: res_query.c,v 8.20 2000/02/29 05:39:12 vix static int __libc_res_nquerydomain(res_state statp, const char *name, const char *domain, 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. @@ -115,15 +115,20 @@ __libc_res_nquery(res_state statp, int class, int type, /* class and type of query */ u_char *answer, /* buffer to put answer */ 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; int n, use_malloc = 0; u_int oflags = statp->_flags; - size_t bufsize = QUERYSIZE; - buf = alloca (bufsize); + size_t bufsize = (type == T_UNSPEC ? 2 : 1) * QUERYSIZE; + u_char *buf = alloca (bufsize); + u_char *query1 = buf; + int nquery1 = -1; + u_char *query2 = NULL; + int nquery2 = 0; again: hp->rcode = NOERROR; /* default */ @@ -133,18 +138,47 @@ __libc_res_nquery(res_state statp, printf(";; res_query(%s, %d, %d)\n", name, class, type); #endif - n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL, - buf, bufsize); - if (n > 0 - && (oflags & RES_F_EDNS0ERR) == 0 - && (statp->options & RES_USE_EDNS0) != 0) - n = __res_nopt(statp, n, buf, bufsize, anslen); + if (type == T_UNSPEC) + { + n = res_nmkquery(statp, QUERY, name, class, T_A, NULL, 0, NULL, + query1, bufsize); + if (n > 0) + { + 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) { /* Retry just in case res_nmkquery failed because of too short buffer. Shouldn't happen. */ - bufsize = MAXPACKET; + bufsize = (type == T_UNSPEC ? 2 : 1) * MAXPACKET; buf = malloc (bufsize); if (buf != NULL) { + query1 = buf; use_malloc = 1; goto again; } @@ -168,7 +202,8 @@ __libc_res_nquery(res_state statp, return (n); } 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) free (buf); if (n < 0) { @@ -184,20 +219,37 @@ __libc_res_nquery(res_state statp, /* __libc_res_nsend might have reallocated the buffer. */ 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 - if (statp->options & RES_DEBUG) + if (statp->options & RES_DEBUG) { printf(";; rcode = %d, ancount=%d\n", hp->rcode, ntohs(hp->ancount)); + if (hp != hp2) + printf(";; rcode2 = %d, ancount2=%d\n", hp2->rcode, + ntohs(hp2->ancount)); + } #endif - switch (hp->rcode) { + switch (hp->rcode == NOERROR ? hp2->rcode : hp->rcode) { 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); break; case SERVFAIL: RES_SET_H_ERRNO(statp, TRY_AGAIN); break; case NOERROR: + if (ntohs (hp->ancount) != 0 + || ntohs (hp2->ancount) != 0) + goto success; RES_SET_H_ERRNO(statp, NO_DATA); break; case FORMERR: @@ -209,6 +261,7 @@ __libc_res_nquery(res_state statp, } return (-1); } + success: return (n); } libresolv_hidden_def (__libc_res_nquery) @@ -221,7 +274,7 @@ res_nquery(res_state statp, int anslen) /* size of answer buffer */ { return __libc_res_nquery(statp, name, class, type, answer, anslen, - NULL); + NULL, NULL, NULL); } libresolv_hidden_def (res_nquery) @@ -233,11 +286,13 @@ libresolv_hidden_def (res_nquery) */ int __libc_res_nsearch(res_state statp, - const char *name, /* domain name */ - int class, int type, /* class and type of query */ - u_char *answer, /* buffer to put answer */ - int anslen, /* size of answer */ - u_char **answerp) + const char *name, /* domain name */ + int class, int type, /* class and type of query */ + u_char *answer, /* buffer to put answer */ + int anslen, /* size of answer */ + u_char **answerp, + u_char **answerp2, + int *nanswerp2) { const char *cp, * const *domain; 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 (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL) return (__libc_res_nquery(statp, cp, class, type, answer, - anslen, answerp)); + anslen, answerp, answerp2, + nanswerp2)); #ifdef DEBUG if (statp->options & RES_DEBUG) @@ -276,7 +332,8 @@ __libc_res_nsearch(res_state statp, saved_herrno = -1; if (dots >= statp->ndots || trailing_dot) { ret = __libc_res_nquerydomain(statp, name, NULL, class, type, - answer, anslen, answerp); + answer, anslen, answerp, + answerp2, nanswerp2); if (ret > 0 || trailing_dot) return (ret); saved_herrno = h_errno; @@ -285,6 +342,12 @@ __libc_res_nsearch(res_state statp, answer = *answerp; 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, class, type, - answer, anslen, answerp); + answer, anslen, answerp, + answerp2, nanswerp2); if (ret > 0) return (ret); @@ -315,6 +379,13 @@ __libc_res_nsearch(res_state statp, answer = *answerp; anslen = MAXPACKET; } + if (answerp2 + && (*answerp2 < answer + || *answerp2 >= answer + anslen)) + { + free (*answerp2); + *answerp2 = NULL; + } /* * If no server present, give up. @@ -368,7 +439,8 @@ __libc_res_nsearch(res_state statp, */ if (dots && !(tried_as_is || root_on_list)) { ret = __libc_res_nquerydomain(statp, name, NULL, class, type, - answer, anslen, answerp); + answer, anslen, answerp, + answerp2, nanswerp2); if (ret > 0) return (ret); } @@ -380,6 +452,11 @@ __libc_res_nsearch(res_state statp, * else send back meaningless H_ERRNO, that being the one from * the last DNSRCH we did. */ + if (answerp2 && (*answerp2 < answer || *answerp2 >= answer + anslen)) + { + free (*answerp2); + *answerp2 = NULL; + } if (saved_herrno != -1) RES_SET_H_ERRNO(statp, saved_herrno); else if (got_nodata) @@ -398,7 +475,7 @@ res_nsearch(res_state statp, int anslen) /* size of answer */ { return __libc_res_nsearch(statp, name, class, type, answer, - anslen, NULL); + anslen, NULL, NULL, NULL); } libresolv_hidden_def (res_nsearch) @@ -408,12 +485,14 @@ libresolv_hidden_def (res_nsearch) */ static int __libc_res_nquerydomain(res_state statp, - const char *name, - const char *domain, - int class, int type, /* class and type of query */ - u_char *answer, /* buffer to put answer */ - int anslen, /* size of answer */ - u_char **answerp) + const char *name, + const char *domain, + int class, int type, /* class and type of query */ + u_char *answer, /* buffer to put answer */ + int anslen, /* size of answer */ + u_char **answerp, + u_char **answerp2, + int *nanswerp2) { char nbuf[MAXDNAME]; const char *longname = nbuf; @@ -450,7 +529,7 @@ __libc_res_nquerydomain(res_state statp, sprintf(nbuf, "%s.%s", name, domain); } return (__libc_res_nquery(statp, longname, class, type, answer, - anslen, answerp)); + anslen, answerp, answerp2, nanswerp2)); } int @@ -462,7 +541,7 @@ res_nquerydomain(res_state statp, int anslen) /* size of answer */ { return __libc_res_nquerydomain(statp, name, domain, class, type, - answer, anslen, NULL); + answer, anslen, NULL, NULL, NULL); } libresolv_hidden_def (res_nquerydomain) diff --git a/resolv/res_send.c b/resolv/res_send.c index 23306a2..b3dbd70 100644 --- a/resolv/res_send.c +++ b/resolv/res_send.c @@ -176,10 +176,14 @@ evNowTime(struct timespec *res) { /* Forward. */ 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, + const u_char *, int, u_char **, int *, int *, int, - int *, int *, u_char **); + int *, int *, u_char **, + u_char **, int *, int *); #ifdef DEBUG static void Aerror(const res_state, FILE *, const char *, int, const struct sockaddr *); @@ -334,33 +338,41 @@ libresolv_hidden_def (res_queriesmatch) int __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) { __set_errno (ESRCH); return (-1); } - if (anssiz < HFIXEDSZ) { + if (anssiz < (buf2 == NULL ? 1 : 2) * HFIXEDSZ) { __set_errno (EINVAL); return (-1); } - if ((statp->qhook || statp->rhook) && anssiz < MAXPACKET && ansp) { - u_char *buf = malloc (MAXPACKET); - if (buf == NULL) - return (-1); - memcpy (buf, ans, HFIXEDSZ); - *ansp = buf; - ans = buf; - anssiz = MAXPACKET; +#ifdef USE_HOOKS + if (__builtin_expect (statp->qhook || statp->rhook, 0)) { + if (anssiz < MAXPACKET && ansp) { + u_char *buf = malloc (MAXPACKET); + if (buf == NULL) + return (-1); + memcpy (buf, ans, HFIXEDSZ); + *ansp = buf; + ans = buf; + anssiz = MAXPACKET; + } } +#endif DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY), (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; 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. * 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) { struct sockaddr_in6 *ina; unsigned int map; @@ -479,8 +491,9 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, if (nsap == NULL) goto next_ns; - same_ns: - if (statp->qhook) { + same_ns: +#ifdef USE_HOOKS + if (__builtin_expect (statp->qhook != NULL, 0)) { int done = 0, loops = 0; do { @@ -512,6 +525,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, } } while (!done); } +#endif #ifdef DEBUG 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, tmpbuf, sizeof (tmpbuf)))); - if (v_circuit) { + if (__builtin_expect (v_circuit, 0)) { /* Use VC; at most one attempt per server. */ try = statp->retry; - n = send_vc(statp, buf, buflen, &ans, &anssiz, &terrno, - ns, ansp); + n = send_vc(statp, buf, buflen, buf2, buflen2, + &ans, &anssiz, &terrno, + ns, ansp, ansp2, nansp2, &resplen2); if (n < 0) return (-1); if (n == 0) goto next_ns; - resplen = n; } else { /* Use datagrams. */ - n = send_dg(statp, buf, buflen, &ans, &anssiz, &terrno, - ns, &v_circuit, &gotsomewhere, ansp); + n = send_dg(statp, buf, buflen, buf2, buflen2, + &ans, &anssiz, &terrno, + ns, &v_circuit, &gotsomewhere, ansp, + ansp2, nansp2, &resplen2); if (n < 0) return (-1); if (n == 0) goto next_ns; if (v_circuit) + // XXX Check whether both requests failed or + // XXX whether one have been answered successfully goto same_ns; - resplen = n; } + resplen = n; + Dprint((statp->options & RES_DEBUG) || ((statp->pfcode & RES_PRF_REPLY) && (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), (stdout, "%s", ""), 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, @@ -563,7 +587,8 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, (statp->options & RES_STAYOPEN) == 0) { __res_iclose(statp, false); } - if (statp->rhook) { +#ifdef USE_HOOKS + if (__builtin_expect (statp->rhook, 0)) { int done = 0, loops = 0; do { @@ -593,6 +618,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, } while (!done); } +#endif return (resplen); next_ns: ; } /*foreach ns*/ @@ -612,7 +638,8 @@ int res_nsend(res_state statp, 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) @@ -620,17 +647,23 @@ libresolv_hidden_def (res_nsend) static int send_vc(res_state statp, - const u_char *buf, int buflen, u_char **ansp, int *anssizp, - int *terrno, int ns, u_char **anscp) + const u_char *buf, int buflen, const u_char *buf2, int buflen2, + 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 *hp2 = (HEADER *) buf2; u_char *ans = *ansp; - int anssiz = *anssizp; + int orig_anssizp = *anssizp; + // XXX REMOVE + // int anssiz = *anssizp; HEADER *anhp = (HEADER *) ans; struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns]; int truncating, connreset, resplen, n; - struct iovec iov[2]; + struct iovec iov[4]; u_short len; + u_short len2; u_char *cp; connreset = 0; @@ -677,11 +710,19 @@ send_vc(res_state statp, /* * Send length & message */ - ns_put16((u_short)buflen, (u_char*)&len); + len = htons ((u_short) buflen); evConsIovec(&len, INT16SZ, &iov[0]); evConsIovec((void*)buf, buflen, &iov[1]); - if (TEMP_FAILURE_RETRY (writev(statp->_vcsock, iov, 2)) - != (INT16SZ + buflen)) { + int niov = 2; + 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; Perror(statp, stderr, "write failed", errno); __res_iclose(statp, false); @@ -690,6 +731,8 @@ send_vc(res_state statp, /* * Receive length & response */ + int recvresp1 = 0; + int recvresp2 = buf2 == NULL; read_len: cp = ans; len = INT16SZ; @@ -718,30 +761,66 @@ send_vc(res_state statp, } return (0); } +#ifdef _STRING_ARCH_unaligned + resplen = ntohs (*(uint16_t *) ans); +#else 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) { - ans = malloc (MAXPACKET); - if (ans == NULL) { + u_char *newp = malloc (MAXPACKET); + if (newp == NULL) { *terrno = ENOMEM; __res_iclose(statp, false); return (0); } - anssiz = MAXPACKET; - *anssizp = MAXPACKET; - *ansp = ans; - *anscp = ans; - anhp = (HEADER *) ans; + *thisanssizp = MAXPACKET; + *thisansp = newp; + anhp = (HEADER *) newp; len = resplen; } else { Dprint(statp->options & RES_DEBUG, (stdout, ";; response truncated\n") ); truncating = 1; - len = anssiz; + len = *thisanssizp; } } else len = resplen; + if (len < HFIXEDSZ) { /* * Undersized message. @@ -752,7 +831,8 @@ send_vc(res_state statp, __res_iclose(statp, false); return (0); } - cp = ans; + + cp = *thisansp; while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){ cp += n; len -= n; @@ -768,7 +848,7 @@ send_vc(res_state statp, * Flush rest of answer so connection stays in synch. */ anhp->tc = 1; - len = resplen - anssiz; + len = resplen - *thisanssizp; while (len != 0) { char junk[PACKETSZ]; @@ -787,14 +867,25 @@ send_vc(res_state statp, * itself confused, then drop the packet and * 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) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; old answer (unexpected):\n"), - ans, (resplen > anssiz) ? anssiz: resplen); + *thisansp, + (resplen > *thisanssiz) ? *thisanssiz: resplen); 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 * next nameserver ought not be tried. @@ -804,19 +895,20 @@ send_vc(res_state statp, static int send_dg(res_state statp, - const u_char *buf, int buflen, u_char **ansp, int *anssizp, - int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp) + const u_char *buf, int buflen, const u_char *buf2, int buflen2, + 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 *hp2 = (HEADER *) buf2; u_char *ans = *ansp; - int anssiz = *anssizp; - HEADER *anhp = (HEADER *) ans; + int orig_anssizp = *anssizp; struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns]; struct timespec now, timeout, finish; struct pollfd pfd[1]; int ptimeout; struct sockaddr_in6 from; - socklen_t fromlen; int resplen, seconds, n; if (EXT(statp).nssocks[ns] == -1) { @@ -879,6 +971,8 @@ send_dg(res_state statp, evAddTime(&finish, &now, &timeout); int need_recompute = 0; int nwritten = 0; + int recvresp1 = 0; + int recvresp2 = buf2 == NULL; pfd[0].fd = EXT(statp).nssocks[ns]; pfd[0].events = POLLOUT; wait: @@ -918,35 +1012,73 @@ send_dg(res_state statp, } __set_errno (0); 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) goto recompute_resend; Perror(statp, stderr, "send", errno); goto err_out; } - pfd[0].events = POLLIN; + if (nwritten != 0 || buf2 == NULL) + pfd[0].events = POLLIN; + else + pfd[0].events = POLLIN | POLLOUT; ++nwritten; goto wait; } else if (pfd[0].revents & POLLIN) { - fromlen = sizeof(struct sockaddr_in6); - if (anssiz < MAXPACKET + 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; + } + + if (*thisanssizp < MAXPACKET + /* Yes, we test ANSCP here. If we have two buffers + both will be allocatable. */ && anscp - && (ioctl (pfd[0].fd, FIONREAD, &resplen) < 0 - || anssiz < resplen)) { - ans = malloc (MAXPACKET); - if (ans == NULL) - ans = *ansp; - else { - anssiz = MAXPACKET; + && (ioctl (pfd[0].fd, FIONREAD, thisresplenp) < 0 + || *thisanssizp < *thisresplenp)) { + u_char *newp = malloc (MAXPACKET); + if (newp != NULL) { *anssizp = MAXPACKET; - *ansp = ans; - *anscp = ans; - anhp = (HEADER *) ans; + *thisansp = ans = newp; } } - resplen = recvfrom(pfd[0].fd, (char*)ans, anssiz,0, - (struct sockaddr *)&from, &fromlen); - if (resplen <= 0) { + HEADER *anhp = (HEADER *) *thisansp; + socklen_t fromlen = sizeof(struct sockaddr_in6); + 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) { need_recompute = 1; goto wait; @@ -955,17 +1087,18 @@ send_dg(res_state statp, goto err_out; } *gotsomewhere = 1; - if (resplen < HFIXEDSZ) { + if (*thisresplenp < HFIXEDSZ) { /* * Undersized message. */ Dprint(statp->options & RES_DEBUG, (stdout, ";; undersized: %d\n", - resplen)); + *thisresplen)); *terrno = EMSGSIZE; 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. * XXX - potential security hazard could @@ -974,7 +1107,9 @@ send_dg(res_state statp, DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; old answer:\n"), - ans, (resplen > anssiz) ? anssiz : resplen); + thisansp, + (*thisresplen > *thisanssiz) + ? *thisanssiz : *thisresplen); goto wait; } if (!(statp->options & RES_INSECURE1) && @@ -987,14 +1122,16 @@ send_dg(res_state statp, DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; not our server:\n"), - ans, (resplen > anssiz) ? anssiz : resplen); + thisansp, + (*thisresplen > *thisanssiz) + ? *thisanssiz : *thisresplen); goto wait; } #ifdef RES_USE_EDNS0 if (anhp->rcode == FORMERR && (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 * FORMERR packet do not carry query section, hence * res_queriesmatch() returns 0. @@ -1002,15 +1139,23 @@ send_dg(res_state statp, DprintQ(statp->options & RES_DEBUG, (stdout, "server rejected query with EDNS0:\n"), - ans, (resplen > anssiz) ? anssiz : resplen); + thisans, + (*thisresplen > *thisanssiz) + ? *thisanssiz : *thisresplen); /* record the error */ statp->_flags |= RES_F_EDNS0ERR; goto err_out; } #endif - if (!(statp->options & RES_INSECURE2) && - !res_queriesmatch(buf, buf + buflen, - ans, ans + anssiz)) { + if (!(statp->options & RES_INSECURE2) + && (recvresp1 || !res_queriesmatch(buf, buf + buflen, + *thisansp, + *thisansp + + *thisanssizp)) + && (recvresp2 || !res_queriesmatch(buf2, buf2 + buflen2, + *thisansp, + *thisansp + + *thisanssizp))) { /* * response contains wrong query? ignore it. * XXX - potential security hazard could @@ -1019,7 +1164,9 @@ send_dg(res_state statp, DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; wrong query name:\n"), - ans, (resplen > anssiz) ? anssiz : resplen); + thisansp, + (*thisresplen > *thisanssiz) + ? *thisanssiz : *thisresplen); goto wait; } if (anhp->rcode == SERVFAIL || @@ -1027,7 +1174,9 @@ send_dg(res_state statp, anhp->rcode == REFUSED) { DprintQ(statp->options & RES_DEBUG, (stdout, "server rejected query:\n"), - ans, (resplen > anssiz) ? anssiz : resplen); + thisansp, + (*thisresplen > *thisanssiz) + ? *thisanssiz : *thisresplen); next_ns: __res_iclose(statp, false); /* 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) { DprintQ(statp->options & RES_DEBUG, (stdout, "referred query:\n"), - ans, (resplen > anssiz) ? anssiz : resplen); + thisansp, + (*thisresplen > *thisanssiz) + ? *thisanssiz : *thisresplen); goto next_ns; } if (!(statp->options & RES_IGNTC) && anhp->tc) { @@ -1050,8 +1201,18 @@ send_dg(res_state statp, (stdout, ";; truncated answer\n")); *v_circuit = 1; __res_iclose(statp, false); + // XXX if we have received one reply we could + // XXX use it and not repeat it over TCP... 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 * next nameserver ought not be tried. diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c index 9a27efd..3de83e3 100644 --- a/sysdeps/posix/getaddrinfo.c +++ b/sysdeps/posix/getaddrinfo.c @@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include #include @@ -91,14 +92,6 @@ struct gaih_servtuple 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 { @@ -202,6 +195,7 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, if (herrno == NETDB_INTERNAL) \ { \ __set_h_errno (herrno); \ + _res.options = old_res_options; \ return -EAI_SYSTEM; \ } \ 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) (const char *name, int af, struct hostent *host, char *buffer, size_t buflen, int *errnop, @@ -685,87 +683,132 @@ gaih_inet (const char *name, const struct gaih_service *service, while (!no_more) { - 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) + nss_gethostbyname4_r fct4 + = __nss_lookup_function (nip, "gethostbyname4_r"); + if (fct4 != NULL) { - if (req->ai_family == AF_INET6 - || req->ai_family == AF_UNSPEC) + int herrno; + + while (1) { - gethosts (AF_INET6, struct in6_addr); - no_inet6_data = no_data; - inet6_status = status; + 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 (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))) + + if (status == NSS_STATUS_SUCCESS) { - gethosts (AF_INET, struct in_addr); + canon = (*pat)->name; - if (req->ai_family == AF_INET) + 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; inet6_status = status; } - } - - /* 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 (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))) { - /* If we need the canonical name, get it - from the same service as the result. */ - nss_getcanonname_r cfct; - int herrno; + gethosts (AF_INET, struct in_addr); - cfct = __nss_lookup_function (nip, "getcanonname_r"); - if (cfct != NULL) + if (req->ai_family == AF_INET) { - const size_t max_fqdn_len = 256; - char *buf = alloca (max_fqdn_len); - 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; + no_inet6_data = no_data; + inet6_status = status; } } - 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; + + cfct = __nss_lookup_function (nip, + "getcanonname_r"); + if (cfct != NULL) + { + const size_t max_fqdn_len = 256; + char *buf = alloca (max_fqdn_len); + char *s; + + 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; + } + } - /* 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; + 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) -- 2.7.4