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

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

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