1 /* Hosts file parser in nss_files module.
2 Copyright (C) 1996-2013 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <arpa/nameser.h>
27 /* Get implementation for some internal functions. */
28 #include "../resolv/mapv4v6addr.h"
29 #include "../resolv/res_hconf.h"
32 #define ENTNAME hostent
33 #define DATABASE "hosts"
36 #define EXTRA_ARGS , af, flags
37 #define EXTRA_ARGS_DECL , int af, int flags
39 #define ENTDATA hostent_data
42 unsigned char host_addr
[16]; /* IPv4 or IPv6 address. */
43 char *h_addr_ptrs
[2]; /* Points to that and null terminator. */
46 #define TRAILING_LIST_MEMBER h_aliases
47 #define TRAILING_LIST_SEPARATOR_P isspace
48 #include "files-parse.c"
54 STRING_FIELD (addr
, isspace
, 1);
57 if (inet_pton (af
== AF_UNSPEC
? AF_INET
: af
, addr
, entdata
->host_addr
)
59 af
= af
== AF_UNSPEC
? AF_INET
: af
;
62 if (af
== AF_INET6
&& (flags
& AI_V4MAPPED
) != 0
63 && inet_pton (AF_INET
, addr
, entdata
->host_addr
) > 0)
64 map_v4v6_address ((char *) entdata
->host_addr
,
65 (char *) entdata
->host_addr
);
66 else if (af
== AF_INET
67 && inet_pton (AF_INET6
, addr
, entdata
->host_addr
) > 0)
69 if (IN6_IS_ADDR_V4MAPPED (entdata
->host_addr
))
70 memcpy (entdata
->host_addr
, entdata
->host_addr
+ 12, INADDRSZ
);
71 else if (IN6_IS_ADDR_LOOPBACK (entdata
->host_addr
))
73 in_addr_t localhost
= htonl (INADDR_LOOPBACK
);
74 memcpy (entdata
->host_addr
, &localhost
, sizeof (localhost
));
77 /* Illegal address: ignore line. */
80 else if (af
== AF_UNSPEC
81 && inet_pton (AF_INET6
, addr
, entdata
->host_addr
) > 0)
84 /* Illegal address: ignore line. */
88 /* We always return entries of the requested form. */
89 result
->h_addrtype
= af
;
90 result
->h_length
= af
== AF_INET
? INADDRSZ
: IN6ADDRSZ
;
92 /* Store a pointer to the address in the expected form. */
93 entdata
->h_addr_ptrs
[0] = (char *) entdata
->host_addr
;
94 entdata
->h_addr_ptrs
[1] = NULL
;
95 result
->h_addr_list
= entdata
->h_addr_ptrs
;
97 STRING_FIELD (result
->h_name
, isspace
, 1);
102 #define HOST_DB_LOOKUP(name, keysize, keypattern, break_if_match, proto...) \
104 _nss_files_get##name##_r (proto, \
105 struct STRUCTURE *result, char *buffer, \
106 size_t buflen, int *errnop H_ERRNO_PROTO) \
108 FILE *stream = NULL; \
109 uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data); \
111 buflen = buflen > pad ? buflen - pad : 0; \
114 enum nss_status status = internal_setent (&stream); \
116 if (status == NSS_STATUS_SUCCESS) \
118 while ((status = internal_getent (stream, result, buffer, buflen, \
119 errnop H_ERRNO_ARG EXTRA_ARGS_VALUE)) \
120 == NSS_STATUS_SUCCESS) \
123 if (status == NSS_STATUS_SUCCESS \
124 && _res_hconf.flags & HCONF_FLAG_MULTI) \
126 /* We have to get all host entries from the file. */ \
127 size_t tmp_buflen = MIN (buflen, 4096); \
128 char tmp_buffer_stack[tmp_buflen] \
129 __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));\
130 char *tmp_buffer = tmp_buffer_stack; \
131 struct hostent tmp_result_buf; \
135 bool tmp_buffer_malloced = false; \
137 while (result->h_aliases[naliases] != NULL) \
140 bufferend = (char *) &result->h_aliases[naliases + 1]; \
143 while ((status = internal_getent (stream, &tmp_result_buf, \
144 tmp_buffer, tmp_buflen, errnop \
145 H_ERRNO_ARG EXTRA_ARGS_VALUE)) \
146 == NSS_STATUS_SUCCESS) \
149 struct hostent *old_result = result; \
150 result = &tmp_result_buf; \
151 /* The following piece is a bit clumsy but we want to use the \
152 `break_if_match' value. The optimizer should do its \
157 result = old_result; \
159 while ((matches = 0)); \
163 /* We could be very clever and try to recycle a few bytes \
164 in the buffer instead of generating new arrays. But \
165 we are not doing this here since it's more work than \
166 it's worth. Simply let the user provide a bit bigger \
168 char **new_h_addr_list; \
169 char **new_h_aliases; \
170 int newaliases = 0; \
171 size_t newstrlen = 0; \
174 /* Count the new aliases and the length of the strings. */ \
175 while (tmp_result_buf.h_aliases[newaliases] != NULL) \
177 char *cp = tmp_result_buf.h_aliases[newaliases]; \
179 newstrlen += strlen (cp) + 1; \
181 /* If the real name is different add it also to the \
182 aliases. This means that there is a duplication \
183 in the alias list but this is really the user's \
185 if (strcmp (old_result->h_name, \
186 tmp_result_buf.h_name) != 0) \
189 newstrlen += strlen (tmp_result_buf.h_name) + 1; \
192 /* Make sure bufferend is aligned. */ \
193 assert ((bufferend - (char *) 0) % sizeof (char *) == 0); \
195 /* Now we can check whether the buffer is large enough. \
196 16 is the maximal size of the IP address. */ \
197 if (bufferend + 16 + (naddrs + 2) * sizeof (char *) \
198 + roundup (newstrlen, sizeof (char *)) \
199 + (naliases + newaliases + 1) * sizeof (char *) \
200 >= buffer + buflen) \
203 *herrnop = NETDB_INTERNAL; \
204 status = NSS_STATUS_TRYAGAIN; \
209 (char **) (bufferend \
210 + roundup (newstrlen, sizeof (char *)) \
213 (char **) ((char *) new_h_addr_list \
214 + (naddrs + 2) * sizeof (char *)); \
216 /* Copy the old data in the new arrays. */ \
217 for (cnt = 0; cnt < naddrs; ++cnt) \
218 new_h_addr_list[cnt] = old_result->h_addr_list[cnt]; \
220 for (cnt = 0; cnt < naliases; ++cnt) \
221 new_h_aliases[cnt] = old_result->h_aliases[cnt]; \
223 /* Store the new strings. */ \
225 while (tmp_result_buf.h_aliases[cnt] != NULL) \
227 new_h_aliases[naliases++] = bufferend; \
228 bufferend = (__stpcpy (bufferend, \
229 tmp_result_buf.h_aliases[cnt]) \
234 if (cnt < newaliases) \
236 new_h_aliases[naliases++] = bufferend; \
237 bufferend = __stpcpy (bufferend, \
238 tmp_result_buf.h_name) + 1; \
241 /* Final NULL pointer. */ \
242 new_h_aliases[naliases] = NULL; \
244 /* Round up the buffer end address. */ \
245 bufferend += (sizeof (char *) \
246 - ((bufferend - (char *) 0) \
247 % sizeof (char *))) % sizeof (char *); \
249 /* Now the new address. */ \
250 new_h_addr_list[naddrs++] = \
251 memcpy (bufferend, tmp_result_buf.h_addr, \
252 tmp_result_buf.h_length); \
254 /* Also here a final NULL pointer. */ \
255 new_h_addr_list[naddrs] = NULL; \
257 /* Store the new array pointers. */ \
258 old_result->h_aliases = new_h_aliases; \
259 old_result->h_addr_list = new_h_addr_list; \
261 /* Compute the new buffer end. */ \
262 bufferend = (char *) &new_h_aliases[naliases + 1]; \
263 assert (bufferend <= buffer + buflen); \
265 result = old_result; \
269 if (status == NSS_STATUS_TRYAGAIN) \
271 size_t newsize = 2 * tmp_buflen; \
272 if (tmp_buffer_malloced) \
274 char *newp = realloc (tmp_buffer, newsize); \
277 assert ((((uintptr_t) newp) \
278 & (__alignof__ (struct hostent_data) - 1)) \
281 tmp_buflen = newsize; \
285 else if (!__libc_use_alloca (buflen + newsize)) \
287 tmp_buffer = malloc (newsize); \
288 if (tmp_buffer != NULL) \
290 assert ((((uintptr_t) tmp_buffer) \
291 & (__alignof__ (struct hostent_data) - 1)) \
293 tmp_buffer_malloced = true; \
294 tmp_buflen = newsize; \
301 = extend_alloca (tmp_buffer, tmp_buflen, \
303 + __alignof__ (struct hostent_data)); \
304 tmp_buffer = (char *) (((uintptr_t) tmp_buffer \
305 + __alignof__ (struct hostent_data) \
307 & ~(__alignof__ (struct hostent_data)\
313 status = NSS_STATUS_SUCCESS; \
315 if (tmp_buffer_malloced) \
320 internal_endent (&stream); \
327 #define EXTRA_ARGS_VALUE \
328 , ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET), \
329 ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
330 #include "files-XXX.c"
331 HOST_DB_LOOKUP (hostbyname
, ,,
333 LOOKUP_NAME_CASE (h_name
, h_aliases
)
335 #undef EXTRA_ARGS_VALUE
338 /* XXX Is using _res to determine whether we want to convert IPv4 addresses
339 to IPv6 addresses really the right thing to do? */
340 #define EXTRA_ARGS_VALUE \
341 , af, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
342 HOST_DB_LOOKUP (hostbyname2
, ,,
344 LOOKUP_NAME_CASE (h_name
, h_aliases
)
345 }, const char *name
, int af
)
346 #undef EXTRA_ARGS_VALUE
349 /* We only need to consider IPv4 mapped addresses if the input to the
350 gethostbyaddr() function is an IPv6 address. */
351 #define EXTRA_ARGS_VALUE \
352 , af, (len == IN6ADDRSZ ? AI_V4MAPPED : 0)
353 DB_LOOKUP (hostbyaddr
, ,,,
355 if (result
->h_length
== (int) len
356 && ! memcmp (addr
, result
->h_addr_list
[0], len
))
358 }, const void *addr
, socklen_t len
, int af
)
359 #undef EXTRA_ARGS_VALUE
362 _nss_files_gethostbyname4_r (const char *name
, struct gaih_addrtuple
**pat
,
363 char *buffer
, size_t buflen
, int *errnop
,
364 int *herrnop
, int32_t *ttlp
)
369 enum nss_status status
= internal_setent (&stream
);
371 if (status
== NSS_STATUS_SUCCESS
)
374 bool got_canon
= false;
377 /* Align the buffer for the next record. */
378 uintptr_t pad
= (-(uintptr_t) buffer
379 % __alignof__ (struct hostent_data
));
381 buflen
= buflen
> pad
? buflen
- pad
: 0;
383 struct hostent result
;
384 status
= internal_getent (stream
, &result
, buffer
, buflen
, errnop
385 H_ERRNO_ARG
, AF_UNSPEC
, 0);
386 if (status
!= NSS_STATUS_SUCCESS
)
390 if (__strcasecmp (name
, result
.h_name
) != 0)
392 for (; result
.h_aliases
[naliases
] != NULL
; ++naliases
)
393 if (! __strcasecmp (name
, result
.h_aliases
[naliases
]))
395 if (result
.h_aliases
[naliases
] == NULL
)
398 /* We know this alias exist. Count it. */
402 /* Determine how much memory has been used so far. */
403 // XXX It is not necessary to preserve the aliases array
404 while (result
.h_aliases
[naliases
] != NULL
)
406 char *bufferend
= (char *) &result
.h_aliases
[naliases
+ 1];
407 assert (buflen
>= bufferend
- buffer
);
408 buflen
-= bufferend
- buffer
;
411 /* We found something. */
414 /* Create the record the caller expects. There is only one
416 assert (result
.h_addr_list
[1] == NULL
);
419 uintptr_t pad
= (-(uintptr_t) buffer
420 % __alignof__ (struct gaih_addrtuple
));
422 buflen
= buflen
> pad
? buflen
- pad
: 0;
424 if (__builtin_expect (buflen
< sizeof (struct gaih_addrtuple
),
428 *herrnop
= NETDB_INTERNAL
;
429 status
= NSS_STATUS_TRYAGAIN
;
433 *pat
= (struct gaih_addrtuple
*) buffer
;
434 buffer
+= sizeof (struct gaih_addrtuple
);
435 buflen
-= sizeof (struct gaih_addrtuple
);
439 (*pat
)->name
= got_canon
? NULL
: result
.h_name
;
441 (*pat
)->family
= result
.h_addrtype
;
442 memcpy ((*pat
)->addr
, result
.h_addr_list
[0], result
.h_length
);
445 pat
= &((*pat
)->next
);
447 /* If we only look for the first matching entry we are done. */
448 if ((_res_hconf
.flags
& HCONF_FLAG_MULTI
) == 0)
452 /* If we have to look for multiple records and found one, this
454 if (status
== NSS_STATUS_NOTFOUND
&& any
)
456 assert ((_res_hconf
.flags
& HCONF_FLAG_MULTI
) != 0);
457 status
= NSS_STATUS_SUCCESS
;
460 internal_endent (&stream
);
462 else if (status
== NSS_STATUS_TRYAGAIN
)
465 *herrnop
= TRY_AGAIN
;