Replace FSF snail mail address with URLs.
[glibc.git] / nss / nss_files / files-hosts.c
blobd7d3c5274f14da7dbe6ad9c1cb01f030f6a3224a
1 /* Hosts file parser in nss_files module.
2 Copyright (C) 1996-2001, 2003-2009, 2011 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/>. */
19 #include <assert.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <arpa/nameser.h>
23 #include <netdb.h>
24 #include <resolv.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"
34 #define NEED_H_ERRNO
36 #define EXTRA_ARGS , af, flags
37 #define EXTRA_ARGS_DECL , int af, int flags
39 #define ENTDATA hostent_data
40 struct 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"
49 LINE_PARSER
50 ("#",
52 char *addr;
54 STRING_FIELD (addr, isspace, 1);
56 /* Parse address. */
57 if (inet_pton (af == AF_UNSPEC ? AF_INET : af, addr, entdata->host_addr)
58 > 0)
59 af = af == AF_UNSPEC ? AF_INET : af;
60 else
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));
76 else
77 /* Illegal address: ignore line. */
78 return 0;
80 else if (af == AF_UNSPEC
81 && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
82 af = AF_INET6;
83 else
84 /* Illegal address: ignore line. */
85 return 0;
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...) \
103 enum nss_status \
104 _nss_files_get##name##_r (proto, \
105 struct STRUCTURE *result, char *buffer, \
106 size_t buflen, int *errnop H_ERRNO_PROTO) \
108 uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data); \
109 buffer += pad; \
110 buflen = buflen > pad ? buflen - pad : 0; \
112 __libc_lock_lock (lock); \
114 /* Reset file pointer to beginning or open file. */ \
115 enum nss_status status = internal_setent (keep_stream); \
117 if (status == NSS_STATUS_SUCCESS) \
119 /* Tell getent function that we have repositioned the file pointer. */ \
120 last_use = getby; \
122 while ((status = internal_getent (result, buffer, buflen, errnop \
123 H_ERRNO_ARG EXTRA_ARGS_VALUE)) \
124 == NSS_STATUS_SUCCESS) \
125 { break_if_match } \
127 if (status == NSS_STATUS_SUCCESS \
128 && _res_hconf.flags & HCONF_FLAG_MULTI) \
130 /* We have to get all host entries from the file. */ \
131 size_t tmp_buflen = MIN (buflen, 4096); \
132 char tmp_buffer_stack[tmp_buflen] \
133 __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));\
134 char *tmp_buffer = tmp_buffer_stack; \
135 struct hostent tmp_result_buf; \
136 int naddrs = 1; \
137 int naliases = 0; \
138 char *bufferend; \
139 bool tmp_buffer_malloced = false; \
141 while (result->h_aliases[naliases] != NULL) \
142 ++naliases; \
144 bufferend = (char *) &result->h_aliases[naliases + 1]; \
146 again: \
147 while ((status = internal_getent (&tmp_result_buf, tmp_buffer, \
148 tmp_buflen, errnop H_ERRNO_ARG \
149 EXTRA_ARGS_VALUE)) \
150 == NSS_STATUS_SUCCESS) \
152 int matches = 1; \
153 struct hostent *old_result = result; \
154 result = &tmp_result_buf; \
155 /* The following piece is a bit clumsy but we want to use the \
156 `break_if_match' value. The optimizer should do its \
157 job. */ \
158 do \
160 break_if_match \
161 result = old_result; \
163 while ((matches = 0)); \
165 if (matches) \
167 /* We could be very clever and try to recycle a few bytes \
168 in the buffer instead of generating new arrays. But \
169 we are not doing this here since it's more work than \
170 it's worth. Simply let the user provide a bit bigger \
171 buffer. */ \
172 char **new_h_addr_list; \
173 char **new_h_aliases; \
174 int newaliases = 0; \
175 size_t newstrlen = 0; \
176 int cnt; \
178 /* Count the new aliases and the length of the strings. */ \
179 while (tmp_result_buf.h_aliases[newaliases] != NULL) \
181 char *cp = tmp_result_buf.h_aliases[newaliases]; \
182 ++newaliases; \
183 newstrlen += strlen (cp) + 1; \
185 /* If the real name is different add it also to the \
186 aliases. This means that there is a duplication \
187 in the alias list but this is really the user's \
188 problem. */ \
189 if (strcmp (old_result->h_name, \
190 tmp_result_buf.h_name) != 0) \
192 ++newaliases; \
193 newstrlen += strlen (tmp_result_buf.h_name) + 1; \
196 /* Make sure bufferend is aligned. */ \
197 assert ((bufferend - (char *) 0) % sizeof (char *) == 0); \
199 /* Now we can check whether the buffer is large enough. \
200 16 is the maximal size of the IP address. */ \
201 if (bufferend + 16 + (naddrs + 2) * sizeof (char *) \
202 + roundup (newstrlen, sizeof (char *)) \
203 + (naliases + newaliases + 1) * sizeof (char *) \
204 >= buffer + buflen) \
206 *errnop = ERANGE; \
207 *herrnop = NETDB_INTERNAL; \
208 status = NSS_STATUS_TRYAGAIN; \
209 goto out; \
212 new_h_addr_list = \
213 (char **) (bufferend \
214 + roundup (newstrlen, sizeof (char *)) \
215 + 16); \
216 new_h_aliases = \
217 (char **) ((char *) new_h_addr_list \
218 + (naddrs + 2) * sizeof (char *)); \
220 /* Copy the old data in the new arrays. */ \
221 for (cnt = 0; cnt < naddrs; ++cnt) \
222 new_h_addr_list[cnt] = old_result->h_addr_list[cnt]; \
224 for (cnt = 0; cnt < naliases; ++cnt) \
225 new_h_aliases[cnt] = old_result->h_aliases[cnt]; \
227 /* Store the new strings. */ \
228 cnt = 0; \
229 while (tmp_result_buf.h_aliases[cnt] != NULL) \
231 new_h_aliases[naliases++] = bufferend; \
232 bufferend = (__stpcpy (bufferend, \
233 tmp_result_buf.h_aliases[cnt]) \
234 + 1); \
235 ++cnt; \
238 if (cnt < newaliases) \
240 new_h_aliases[naliases++] = bufferend; \
241 bufferend = __stpcpy (bufferend, \
242 tmp_result_buf.h_name) + 1; \
245 /* Final NULL pointer. */ \
246 new_h_aliases[naliases] = NULL; \
248 /* Round up the buffer end address. */ \
249 bufferend += (sizeof (char *) \
250 - ((bufferend - (char *) 0) \
251 % sizeof (char *))) % sizeof (char *); \
253 /* Now the new address. */ \
254 new_h_addr_list[naddrs++] = \
255 memcpy (bufferend, tmp_result_buf.h_addr, \
256 tmp_result_buf.h_length); \
258 /* Also here a final NULL pointer. */ \
259 new_h_addr_list[naddrs] = NULL; \
261 /* Store the new array pointers. */ \
262 old_result->h_aliases = new_h_aliases; \
263 old_result->h_addr_list = new_h_addr_list; \
265 /* Compute the new buffer end. */ \
266 bufferend = (char *) &new_h_aliases[naliases + 1]; \
267 assert (bufferend <= buffer + buflen); \
269 result = old_result; \
273 if (status == NSS_STATUS_TRYAGAIN) \
275 size_t newsize = 2 * tmp_buflen; \
276 if (tmp_buffer_malloced) \
278 char *newp = realloc (tmp_buffer, newsize); \
279 if (newp != NULL) \
281 assert ((((uintptr_t) newp) \
282 & (__alignof__ (struct hostent_data) - 1)) \
283 == 0); \
284 tmp_buffer = newp; \
285 tmp_buflen = newsize; \
286 goto again; \
289 else if (!__libc_use_alloca (buflen + newsize)) \
291 tmp_buffer = malloc (newsize); \
292 if (tmp_buffer != NULL) \
294 assert ((((uintptr_t) tmp_buffer) \
295 & (__alignof__ (struct hostent_data) - 1)) \
296 == 0); \
297 tmp_buffer_malloced = true; \
298 tmp_buflen = newsize; \
299 goto again; \
302 else \
304 tmp_buffer \
305 = extend_alloca (tmp_buffer, tmp_buflen, \
306 newsize \
307 + __alignof__ (struct hostent_data)); \
308 tmp_buffer = (char *) (((uintptr_t) tmp_buffer \
309 + __alignof__ (struct hostent_data) \
310 - 1) \
311 & ~(__alignof__ (struct hostent_data)\
312 - 1)); \
313 goto again; \
316 else \
317 status = NSS_STATUS_SUCCESS; \
318 out: \
319 if (tmp_buffer_malloced) \
320 free (tmp_buffer); \
324 if (! keep_stream) \
325 internal_endent (); \
328 __libc_lock_unlock (lock); \
330 return status; \
334 #define EXTRA_ARGS_VALUE \
335 , ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET), \
336 ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
337 #include "files-XXX.c"
338 HOST_DB_LOOKUP (hostbyname, ,,
340 LOOKUP_NAME_CASE (h_name, h_aliases)
341 }, const char *name)
342 #undef EXTRA_ARGS_VALUE
345 /* XXX Is using _res to determine whether we want to convert IPv4 addresses
346 to IPv6 addresses really the right thing to do? */
347 #define EXTRA_ARGS_VALUE \
348 , af, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
349 HOST_DB_LOOKUP (hostbyname2, ,,
351 LOOKUP_NAME_CASE (h_name, h_aliases)
352 }, const char *name, int af)
353 #undef EXTRA_ARGS_VALUE
356 /* We only need to consider IPv4 mapped addresses if the input to the
357 gethostbyaddr() function is an IPv6 address. */
358 #define EXTRA_ARGS_VALUE \
359 , af, (len == IN6ADDRSZ ? AI_V4MAPPED : 0)
360 DB_LOOKUP (hostbyaddr, ,,,
362 if (result->h_length == (int) len
363 && ! memcmp (addr, result->h_addr_list[0], len))
364 break;
365 }, const void *addr, socklen_t len, int af)
366 #undef EXTRA_ARGS_VALUE
369 enum nss_status
370 _nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
371 char *buffer, size_t buflen, int *errnop,
372 int *herrnop, int32_t *ttlp)
374 __libc_lock_lock (lock);
376 /* Reset file pointer to beginning or open file. */
377 enum nss_status status = internal_setent (keep_stream);
379 if (status == NSS_STATUS_SUCCESS)
381 /* Tell getent function that we have repositioned the file pointer. */
382 last_use = getby;
384 bool any = false;
385 bool got_canon = false;
386 while (1)
388 /* Align the buffer for the next record. */
389 uintptr_t pad = (-(uintptr_t) buffer
390 % __alignof__ (struct hostent_data));
391 buffer += pad;
392 buflen = buflen > pad ? buflen - pad : 0;
394 struct hostent result;
395 status = internal_getent (&result, buffer, buflen, errnop
396 H_ERRNO_ARG, AF_UNSPEC, 0);
397 if (status != NSS_STATUS_SUCCESS)
398 break;
400 int naliases = 0;
401 if (__strcasecmp (name, result.h_name) != 0)
403 for (; result.h_aliases[naliases] != NULL; ++naliases)
404 if (! __strcasecmp (name, result.h_aliases[naliases]))
405 break;
406 if (result.h_aliases[naliases] == NULL)
407 continue;
409 /* We know this alias exist. Count it. */
410 ++naliases;
413 /* Determine how much memory has been used so far. */
414 // XXX It is not necessary to preserve the aliases array
415 while (result.h_aliases[naliases] != NULL)
416 ++naliases;
417 char *bufferend = (char *) &result.h_aliases[naliases + 1];
418 assert (buflen >= bufferend - buffer);
419 buflen -= bufferend - buffer;
420 buffer = bufferend;
422 /* We found something. */
423 any = true;
425 /* Create the record the caller expects. There is only one
426 address. */
427 assert (result.h_addr_list[1] == NULL);
428 if (*pat == NULL)
430 uintptr_t pad = (-(uintptr_t) buffer
431 % __alignof__ (struct gaih_addrtuple));
432 buffer += pad;
433 buflen = buflen > pad ? buflen - pad : 0;
435 if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
438 *errnop = ERANGE;
439 *herrnop = NETDB_INTERNAL;
440 status = NSS_STATUS_TRYAGAIN;
441 break;
444 *pat = (struct gaih_addrtuple *) buffer;
445 buffer += sizeof (struct gaih_addrtuple);
446 buflen -= sizeof (struct gaih_addrtuple);
449 (*pat)->next = NULL;
450 (*pat)->name = got_canon ? NULL : result.h_name;
451 got_canon = true;
452 (*pat)->family = result.h_addrtype;
453 memcpy ((*pat)->addr, result.h_addr_list[0], result.h_length);
454 (*pat)->scopeid = 0;
456 pat = &((*pat)->next);
458 /* If we only look for the first matching entry we are done. */
459 if ((_res_hconf.flags & HCONF_FLAG_MULTI) == 0)
460 break;
463 /* If we have to look for multiple records and found one, this
464 is a success. */
465 if (status == NSS_STATUS_NOTFOUND && any)
467 assert ((_res_hconf.flags & HCONF_FLAG_MULTI) != 0);
468 status = NSS_STATUS_SUCCESS;
471 if (! keep_stream)
472 internal_endent ();
474 else if (status == NSS_STATUS_TRYAGAIN)
476 *errnop = errno;
477 *herrnop = TRY_AGAIN;
479 else
481 *errnop = errno;
482 *herrnop = NO_DATA;
485 __libc_lock_unlock (lock);
487 return status;