Get canonical name in getaddrinfo from hosts file for AF_INET (fixes 16077)

AF_INET lookup in hosts file uses _nss_files_gethostbyname2_r, which
is not capable of returning a canonical name if it has found one.
This change adds _nss_files_gethostbyname3_r, which wraps around
_nss_files_gethostbyname2_r and then returns result.h_name as the
canonical name.
This commit is contained in:
Siddhesh Poyarekar 2013-11-28 17:18:12 +05:30
parent 897b98ba4d
commit 5a4c6d53f5
4 changed files with 272 additions and 252 deletions

View File

@ -1,3 +1,15 @@
2013-11-28 Siddhesh Poyarekar <siddhesh@redhat.com>
[BZ #16077]
* nss/Versions (libnss_files): Add
_nss_files_gethostbyname3_r.
* nss/nss_files/files-hosts.c (_nss_files_gethostbyname3_r):
New function.
(HOST_DB_LOOKUP): Remove macro.
(_nss_files_gethostbyname_r): Implement function without the
HOST_DB_LOOKUP macro.
(_nss_files_gethostbyname2_r): Likewise.
2013-11-28 Ondřej Bílka <neleai@seznam.cz>
* malloc/malloc.c (__libc_calloc): Make memset a tail call.

4
NEWS
View File

@ -18,8 +18,8 @@ Version 2.19
15856, 15857, 15859, 15867, 15886, 15887, 15890, 15892, 15893, 15895,
15897, 15905, 15909, 15917, 15919, 15921, 15923, 15939, 15948, 15963,
15966, 15985, 15988, 15997, 16032, 16034, 16036, 16037, 16041, 16055,
16071, 16072, 16074, 16078, 16103, 16112, 16143, 16144, 16146, 16150,
16151, 16153, 16167, 16172, 16245.
16071, 16072, 16074, 16077, 16078, 16103, 16112, 16143, 16144, 16146,
16150, 16151, 16153, 16167, 16172, 16245.
* The public headers no longer use __unused nor __block. This change is to
support compiling programs that are derived from BSD sources and use

View File

@ -40,6 +40,7 @@ libnss_files {
_nss_files_endhostent;
_nss_files_gethostbyaddr_r;
_nss_files_gethostbyname2_r;
_nss_files_gethostbyname3_r;
_nss_files_gethostbyname4_r;
_nss_files_gethostbyname_r;
_nss_files_gethostent_r;

View File

@ -97,262 +97,12 @@ LINE_PARSER
STRING_FIELD (result->h_name, isspace, 1);
})
#define HOST_DB_LOOKUP(name, keysize, keypattern, break_if_match, proto...) \
enum nss_status \
_nss_files_get##name##_r (proto, \
struct STRUCTURE *result, char *buffer, \
size_t buflen, int *errnop H_ERRNO_PROTO) \
{ \
uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data); \
buffer += pad; \
buflen = buflen > pad ? buflen - pad : 0; \
\
__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; \
\
while ((status = internal_getent (result, buffer, buflen, errnop \
H_ERRNO_ARG EXTRA_ARGS_VALUE)) \
== NSS_STATUS_SUCCESS) \
{ break_if_match } \
\
if (status == NSS_STATUS_SUCCESS \
&& _res_hconf.flags & HCONF_FLAG_MULTI) \
{ \
/* We have to get all host entries from the file. */ \
size_t tmp_buflen = MIN (buflen, 4096); \
char tmp_buffer_stack[tmp_buflen] \
__attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));\
char *tmp_buffer = tmp_buffer_stack; \
struct hostent tmp_result_buf; \
int naddrs = 1; \
int naliases = 0; \
char *bufferend; \
bool tmp_buffer_malloced = false; \
\
while (result->h_aliases[naliases] != NULL) \
++naliases; \
\
bufferend = (char *) &result->h_aliases[naliases + 1]; \
\
again: \
while ((status = internal_getent (&tmp_result_buf, tmp_buffer, \
tmp_buflen, errnop H_ERRNO_ARG \
EXTRA_ARGS_VALUE)) \
== NSS_STATUS_SUCCESS) \
{ \
int matches = 1; \
struct hostent *old_result = result; \
result = &tmp_result_buf; \
/* The following piece is a bit clumsy but we want to use the \
`break_if_match' value. The optimizer should do its \
job. */ \
do \
{ \
break_if_match \
result = old_result; \
} \
while ((matches = 0)); \
\
if (matches) \
{ \
/* We could be very clever and try to recycle a few bytes \
in the buffer instead of generating new arrays. But \
we are not doing this here since it's more work than \
it's worth. Simply let the user provide a bit bigger \
buffer. */ \
char **new_h_addr_list; \
char **new_h_aliases; \
int newaliases = 0; \
size_t newstrlen = 0; \
int cnt; \
\
/* Count the new aliases and the length of the strings. */ \
while (tmp_result_buf.h_aliases[newaliases] != NULL) \
{ \
char *cp = tmp_result_buf.h_aliases[newaliases]; \
++newaliases; \
newstrlen += strlen (cp) + 1; \
} \
/* If the real name is different add it also to the \
aliases. This means that there is a duplication \
in the alias list but this is really the user's \
problem. */ \
if (strcmp (old_result->h_name, \
tmp_result_buf.h_name) != 0) \
{ \
++newaliases; \
newstrlen += strlen (tmp_result_buf.h_name) + 1; \
} \
\
/* Make sure bufferend is aligned. */ \
assert ((bufferend - (char *) 0) % sizeof (char *) == 0); \
\
/* Now we can check whether the buffer is large enough. \
16 is the maximal size of the IP address. */ \
if (bufferend + 16 + (naddrs + 2) * sizeof (char *) \
+ roundup (newstrlen, sizeof (char *)) \
+ (naliases + newaliases + 1) * sizeof (char *) \
>= buffer + buflen) \
{ \
*errnop = ERANGE; \
*herrnop = NETDB_INTERNAL; \
status = NSS_STATUS_TRYAGAIN; \
goto out; \
} \
\
new_h_addr_list = \
(char **) (bufferend \
+ roundup (newstrlen, sizeof (char *)) \
+ 16); \
new_h_aliases = \
(char **) ((char *) new_h_addr_list \
+ (naddrs + 2) * sizeof (char *)); \
\
/* Copy the old data in the new arrays. */ \
for (cnt = 0; cnt < naddrs; ++cnt) \
new_h_addr_list[cnt] = old_result->h_addr_list[cnt]; \
\
for (cnt = 0; cnt < naliases; ++cnt) \
new_h_aliases[cnt] = old_result->h_aliases[cnt]; \
\
/* Store the new strings. */ \
cnt = 0; \
while (tmp_result_buf.h_aliases[cnt] != NULL) \
{ \
new_h_aliases[naliases++] = bufferend; \
bufferend = (__stpcpy (bufferend, \
tmp_result_buf.h_aliases[cnt]) \
+ 1); \
++cnt; \
} \
\
if (cnt < newaliases) \
{ \
new_h_aliases[naliases++] = bufferend; \
bufferend = __stpcpy (bufferend, \
tmp_result_buf.h_name) + 1; \
} \
\
/* Final NULL pointer. */ \
new_h_aliases[naliases] = NULL; \
\
/* Round up the buffer end address. */ \
bufferend += (sizeof (char *) \
- ((bufferend - (char *) 0) \
% sizeof (char *))) % sizeof (char *); \
\
/* Now the new address. */ \
new_h_addr_list[naddrs++] = \
memcpy (bufferend, tmp_result_buf.h_addr, \
tmp_result_buf.h_length); \
\
/* Also here a final NULL pointer. */ \
new_h_addr_list[naddrs] = NULL; \
\
/* Store the new array pointers. */ \
old_result->h_aliases = new_h_aliases; \
old_result->h_addr_list = new_h_addr_list; \
\
/* Compute the new buffer end. */ \
bufferend = (char *) &new_h_aliases[naliases + 1]; \
assert (bufferend <= buffer + buflen); \
\
result = old_result; \
} \
} \
\
if (status == NSS_STATUS_TRYAGAIN) \
{ \
size_t newsize = 2 * tmp_buflen; \
if (tmp_buffer_malloced) \
{ \
char *newp = realloc (tmp_buffer, newsize); \
if (newp != NULL) \
{ \
assert ((((uintptr_t) newp) \
& (__alignof__ (struct hostent_data) - 1)) \
== 0); \
tmp_buffer = newp; \
tmp_buflen = newsize; \
goto again; \
} \
} \
else if (!__libc_use_alloca (buflen + newsize)) \
{ \
tmp_buffer = malloc (newsize); \
if (tmp_buffer != NULL) \
{ \
assert ((((uintptr_t) tmp_buffer) \
& (__alignof__ (struct hostent_data) - 1)) \
== 0); \
tmp_buffer_malloced = true; \
tmp_buflen = newsize; \
goto again; \
} \
} \
else \
{ \
tmp_buffer \
= extend_alloca (tmp_buffer, tmp_buflen, \
newsize \
+ __alignof__ (struct hostent_data)); \
tmp_buffer = (char *) (((uintptr_t) tmp_buffer \
+ __alignof__ (struct hostent_data) \
- 1) \
& ~(__alignof__ (struct hostent_data)\
- 1)); \
goto again; \
} \
} \
else \
status = NSS_STATUS_SUCCESS; \
out: \
if (tmp_buffer_malloced) \
free (tmp_buffer); \
} \
\
\
if (! keep_stream) \
internal_endent (); \
} \
\
__libc_lock_unlock (lock); \
\
return status; \
}
#define EXTRA_ARGS_VALUE \
, ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET), \
((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
#include "files-XXX.c"
HOST_DB_LOOKUP (hostbyname, ,,
{
LOOKUP_NAME_CASE (h_name, h_aliases)
}, const char *name)
#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 \
, af, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
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 \
@ -365,6 +115,263 @@ DB_LOOKUP (hostbyaddr, ,,,
}, const void *addr, socklen_t len, int af)
#undef EXTRA_ARGS_VALUE
enum nss_status
_nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
char *buffer, size_t buflen, int *errnop,
int *herrnop, int32_t *ttlp, char **canonp)
{
uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);
buffer += pad;
buflen = buflen > pad ? buflen - pad : 0;
__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)
{
/* XXX Is using _res to determine whether we want to convert IPv4
addresses to IPv6 addresses really the right thing to do? */
int flags = ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0);
/* Tell getent function that we have repositioned the file pointer. */
last_use = getby;
while ((status = internal_getent (result, buffer, buflen, errnop,
herrnop, af, flags))
== NSS_STATUS_SUCCESS)
{
LOOKUP_NAME_CASE (h_name, h_aliases)
}
if (status == NSS_STATUS_SUCCESS
&& _res_hconf.flags & HCONF_FLAG_MULTI)
{
/* We have to get all host entries from the file. */
size_t tmp_buflen = MIN (buflen, 4096);
char tmp_buffer_stack[tmp_buflen]
__attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));
char *tmp_buffer = tmp_buffer_stack;
struct hostent tmp_result_buf;
int naddrs = 1;
int naliases = 0;
char *bufferend;
bool tmp_buffer_malloced = false;
while (result->h_aliases[naliases] != NULL)
++naliases;
bufferend = (char *) &result->h_aliases[naliases + 1];
again:
while ((status = internal_getent (&tmp_result_buf, tmp_buffer,
tmp_buflen, errnop, herrnop, af,
flags))
== NSS_STATUS_SUCCESS)
{
int matches = 1;
struct hostent *old_result = result;
result = &tmp_result_buf;
/* The following piece is a bit clumsy but we want to use the
`LOOKUP_NAME_CASE' value. The optimizer should do its
job. */
do
{
LOOKUP_NAME_CASE (h_name, h_aliases)
result = old_result;
}
while ((matches = 0));
if (matches)
{
/* We could be very clever and try to recycle a few bytes
in the buffer instead of generating new arrays. But
we are not doing this here since it's more work than
it's worth. Simply let the user provide a bit bigger
buffer. */
char **new_h_addr_list;
char **new_h_aliases;
int newaliases = 0;
size_t newstrlen = 0;
int cnt;
/* Count the new aliases and the length of the strings. */
while (tmp_result_buf.h_aliases[newaliases] != NULL)
{
char *cp = tmp_result_buf.h_aliases[newaliases];
++newaliases;
newstrlen += strlen (cp) + 1;
}
/* If the real name is different add it also to the
aliases. This means that there is a duplication
in the alias list but this is really the user's
problem. */
if (strcmp (old_result->h_name,
tmp_result_buf.h_name) != 0)
{
++newaliases;
newstrlen += strlen (tmp_result_buf.h_name) + 1;
}
/* Make sure bufferend is aligned. */
assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
/* Now we can check whether the buffer is large enough.
16 is the maximal size of the IP address. */
if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
+ roundup (newstrlen, sizeof (char *))
+ (naliases + newaliases + 1) * sizeof (char *)
>= buffer + buflen)
{
*errnop = ERANGE;
*herrnop = NETDB_INTERNAL;
status = NSS_STATUS_TRYAGAIN;
goto out;
}
new_h_addr_list =
(char **) (bufferend
+ roundup (newstrlen, sizeof (char *))
+ 16);
new_h_aliases =
(char **) ((char *) new_h_addr_list
+ (naddrs + 2) * sizeof (char *));
/* Copy the old data in the new arrays. */
for (cnt = 0; cnt < naddrs; ++cnt)
new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
for (cnt = 0; cnt < naliases; ++cnt)
new_h_aliases[cnt] = old_result->h_aliases[cnt];
/* Store the new strings. */
cnt = 0;
while (tmp_result_buf.h_aliases[cnt] != NULL)
{
new_h_aliases[naliases++] = bufferend;
bufferend = (__stpcpy (bufferend,
tmp_result_buf.h_aliases[cnt])
+ 1);
++cnt;
}
if (cnt < newaliases)
{
new_h_aliases[naliases++] = bufferend;
bufferend = __stpcpy (bufferend,
tmp_result_buf.h_name) + 1;
}
/* Final NULL pointer. */
new_h_aliases[naliases] = NULL;
/* Round up the buffer end address. */
bufferend += (sizeof (char *)
- ((bufferend - (char *) 0)
% sizeof (char *))) % sizeof (char *);
/* Now the new address. */
new_h_addr_list[naddrs++] =
memcpy (bufferend, tmp_result_buf.h_addr,
tmp_result_buf.h_length);
/* Also here a final NULL pointer. */
new_h_addr_list[naddrs] = NULL;
/* Store the new array pointers. */
old_result->h_aliases = new_h_aliases;
old_result->h_addr_list = new_h_addr_list;
/* Compute the new buffer end. */
bufferend = (char *) &new_h_aliases[naliases + 1];
assert (bufferend <= buffer + buflen);
result = old_result;
}
}
if (status == NSS_STATUS_TRYAGAIN)
{
size_t newsize = 2 * tmp_buflen;
if (tmp_buffer_malloced)
{
char *newp = realloc (tmp_buffer, newsize);
if (newp != NULL)
{
assert ((((uintptr_t) newp)
& (__alignof__ (struct hostent_data) - 1))
== 0);
tmp_buffer = newp;
tmp_buflen = newsize;
goto again;
}
}
else if (!__libc_use_alloca (buflen + newsize))
{
tmp_buffer = malloc (newsize);
if (tmp_buffer != NULL)
{
assert ((((uintptr_t) tmp_buffer)
& (__alignof__ (struct hostent_data) - 1))
== 0);
tmp_buffer_malloced = true;
tmp_buflen = newsize;
goto again;
}
}
else
{
tmp_buffer
= extend_alloca (tmp_buffer, tmp_buflen,
newsize
+ __alignof__ (struct hostent_data));
tmp_buffer = (char *) (((uintptr_t) tmp_buffer
+ __alignof__ (struct hostent_data)
- 1)
& ~(__alignof__ (struct hostent_data)
- 1));
goto again;
}
}
else
status = NSS_STATUS_SUCCESS;
out:
if (tmp_buffer_malloced)
free (tmp_buffer);
}
if (! keep_stream)
internal_endent ();
}
if (canonp && status == NSS_STATUS_SUCCESS)
*canonp = result->h_name;
__libc_lock_unlock (lock);
return status;
}
enum nss_status
_nss_files_gethostbyname_r (const char *name, struct hostent *result,
char *buffer, size_t buflen, int *errnop,
int *herrnop)
{
int af = ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET);
return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
errnop, herrnop, NULL, NULL);
}
enum nss_status
_nss_files_gethostbyname2_r (const char *name, int af, struct hostent *result,
char *buffer, size_t buflen, int *errnop,
int *herrnop)
{
return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
errnop, herrnop, NULL, NULL);
}
enum nss_status
_nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,