glibc/nss/nss_files/files-hosts.c

314 lines
11 KiB
C

/* Hosts file parser in nss_files module.
Copyright (C) 1996-2001, 2003, 2004, 2006, 2007
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
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <assert.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netdb.h>
#include <resolv.h>
/* Get implementation for some internal functions. */
#include "../resolv/mapv4v6addr.h"
#include "../resolv/res_hconf.h"
#define ENTNAME hostent
#define DATABASE "hosts"
#define NEED_H_ERRNO
#define EXTRA_ARGS , af, flags
#define EXTRA_ARGS_DECL , int af, int flags
#define ENTDATA hostent_data
struct hostent_data
{
unsigned char host_addr[16]; /* IPv4 or IPv6 address. */
char *h_addr_ptrs[2]; /* Points to that and null terminator. */
};
#define TRAILING_LIST_MEMBER h_aliases
#define TRAILING_LIST_SEPARATOR_P isspace
#include "files-parse.c"
LINE_PARSER
("#",
{
char *addr;
STRING_FIELD (addr, isspace, 1);
/* Parse address. */
if (inet_pton (af, addr, entdata->host_addr) <= 0)
{
if (af == AF_INET6 && (flags & AI_V4MAPPED) != 0
&& inet_pton (AF_INET, addr, entdata->host_addr) > 0)
map_v4v6_address ((char *) entdata->host_addr,
(char *) entdata->host_addr);
else if (af == AF_INET
&& inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
{
if (IN6_IS_ADDR_V4MAPPED (entdata->host_addr))
memcpy (entdata->host_addr, entdata->host_addr + 12, INADDRSZ);
else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
{
in_addr_t localhost = htonl (INADDR_LOOPBACK);
memcpy (entdata->host_addr, &localhost, sizeof (localhost));
}
else
/* Illegal address: ignore line. */
return 0;
}
else
/* Illegal address: ignore line. */
return 0;
}
/* We always return entries of the requested form. */
result->h_addrtype = af;
result->h_length = af == AF_INET ? INADDRSZ : IN6ADDRSZ;
/* Store a pointer to the address in the expected form. */
entdata->h_addr_ptrs[0] = (char *) entdata->host_addr;
entdata->h_addr_ptrs[1] = NULL;
result->h_addr_list = entdata->h_addr_ptrs;
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) \
{ \
enum nss_status status; \
\
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. */ \
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. */ \
const size_t tmp_buflen = MIN (buflen, 4096); \
char tmp_buffer[tmp_buflen] \
__attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));\
struct hostent tmp_result_buf; \
int naddrs = 1; \
int naliases = 0; \
char *bufferend; \
\
while (result->h_aliases[naliases] != NULL) \
++naliases; \
\
bufferend = (char *) &result->h_aliases[naliases + 1]; \
\
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 users \
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; \
break; \
} \
\
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) \
status = NSS_STATUS_SUCCESS; \
} \
\
\
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 \
, af, (len == IN6ADDRSZ ? AI_V4MAPPED : 0)
DB_LOOKUP (hostbyaddr, ,,
{
if (result->h_length == (int) len
&& ! memcmp (addr, result->h_addr_list[0], len))
break;
}, const void *addr, socklen_t len, int af)