Update copyright notices with scripts/update-copyrights
[glibc.git] / nss / nss_files / files-hosts.c
blobab64eadabb7e64765f66cda173b3fd0fe42d049c
1 /* Hosts file parser in nss_files module.
2 Copyright (C) 1996-2014 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);
100 #define EXTRA_ARGS_VALUE \
101 , ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET), \
102 ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
103 #include "files-XXX.c"
104 #undef EXTRA_ARGS_VALUE
106 /* We only need to consider IPv4 mapped addresses if the input to the
107 gethostbyaddr() function is an IPv6 address. */
108 #define EXTRA_ARGS_VALUE \
109 , af, (len == IN6ADDRSZ ? AI_V4MAPPED : 0)
110 DB_LOOKUP (hostbyaddr, ,,,
112 if (result->h_length == (int) len
113 && ! memcmp (addr, result->h_addr_list[0], len))
114 break;
115 }, const void *addr, socklen_t len, int af)
116 #undef EXTRA_ARGS_VALUE
118 enum nss_status
119 _nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
120 char *buffer, size_t buflen, int *errnop,
121 int *herrnop, int32_t *ttlp, char **canonp)
123 uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);
124 buffer += pad;
125 buflen = buflen > pad ? buflen - pad : 0;
127 __libc_lock_lock (lock);
129 /* Reset file pointer to beginning or open file. */
130 enum nss_status status = internal_setent (keep_stream);
132 if (status == NSS_STATUS_SUCCESS)
134 /* XXX Is using _res to determine whether we want to convert IPv4
135 addresses to IPv6 addresses really the right thing to do? */
136 int flags = ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0);
138 /* Tell getent function that we have repositioned the file pointer. */
139 last_use = getby;
141 while ((status = internal_getent (result, buffer, buflen, errnop,
142 herrnop, af, flags))
143 == NSS_STATUS_SUCCESS)
145 LOOKUP_NAME_CASE (h_name, h_aliases)
148 if (status == NSS_STATUS_SUCCESS
149 && _res_hconf.flags & HCONF_FLAG_MULTI)
151 /* We have to get all host entries from the file. */
152 size_t tmp_buflen = MIN (buflen, 4096);
153 char tmp_buffer_stack[tmp_buflen]
154 __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));
155 char *tmp_buffer = tmp_buffer_stack;
156 struct hostent tmp_result_buf;
157 int naddrs = 1;
158 int naliases = 0;
159 char *bufferend;
160 bool tmp_buffer_malloced = false;
162 while (result->h_aliases[naliases] != NULL)
163 ++naliases;
165 bufferend = (char *) &result->h_aliases[naliases + 1];
167 again:
168 while ((status = internal_getent (&tmp_result_buf, tmp_buffer,
169 tmp_buflen, errnop, herrnop, af,
170 flags))
171 == NSS_STATUS_SUCCESS)
173 int matches = 1;
174 struct hostent *old_result = result;
175 result = &tmp_result_buf;
176 /* The following piece is a bit clumsy but we want to use the
177 `LOOKUP_NAME_CASE' value. The optimizer should do its
178 job. */
181 LOOKUP_NAME_CASE (h_name, h_aliases)
182 result = old_result;
184 while ((matches = 0));
186 if (matches)
188 /* We could be very clever and try to recycle a few bytes
189 in the buffer instead of generating new arrays. But
190 we are not doing this here since it's more work than
191 it's worth. Simply let the user provide a bit bigger
192 buffer. */
193 char **new_h_addr_list;
194 char **new_h_aliases;
195 int newaliases = 0;
196 size_t newstrlen = 0;
197 int cnt;
199 /* Count the new aliases and the length of the strings. */
200 while (tmp_result_buf.h_aliases[newaliases] != NULL)
202 char *cp = tmp_result_buf.h_aliases[newaliases];
203 ++newaliases;
204 newstrlen += strlen (cp) + 1;
206 /* If the real name is different add it also to the
207 aliases. This means that there is a duplication
208 in the alias list but this is really the user's
209 problem. */
210 if (strcmp (old_result->h_name,
211 tmp_result_buf.h_name) != 0)
213 ++newaliases;
214 newstrlen += strlen (tmp_result_buf.h_name) + 1;
217 /* Make sure bufferend is aligned. */
218 assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
220 /* Now we can check whether the buffer is large enough.
221 16 is the maximal size of the IP address. */
222 if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
223 + roundup (newstrlen, sizeof (char *))
224 + (naliases + newaliases + 1) * sizeof (char *)
225 >= buffer + buflen)
227 *errnop = ERANGE;
228 *herrnop = NETDB_INTERNAL;
229 status = NSS_STATUS_TRYAGAIN;
230 goto out;
233 new_h_addr_list =
234 (char **) (bufferend
235 + roundup (newstrlen, sizeof (char *))
236 + 16);
237 new_h_aliases =
238 (char **) ((char *) new_h_addr_list
239 + (naddrs + 2) * sizeof (char *));
241 /* Copy the old data in the new arrays. */
242 for (cnt = 0; cnt < naddrs; ++cnt)
243 new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
245 for (cnt = 0; cnt < naliases; ++cnt)
246 new_h_aliases[cnt] = old_result->h_aliases[cnt];
248 /* Store the new strings. */
249 cnt = 0;
250 while (tmp_result_buf.h_aliases[cnt] != NULL)
252 new_h_aliases[naliases++] = bufferend;
253 bufferend = (__stpcpy (bufferend,
254 tmp_result_buf.h_aliases[cnt])
255 + 1);
256 ++cnt;
259 if (cnt < newaliases)
261 new_h_aliases[naliases++] = bufferend;
262 bufferend = __stpcpy (bufferend,
263 tmp_result_buf.h_name) + 1;
266 /* Final NULL pointer. */
267 new_h_aliases[naliases] = NULL;
269 /* Round up the buffer end address. */
270 bufferend += (sizeof (char *)
271 - ((bufferend - (char *) 0)
272 % sizeof (char *))) % sizeof (char *);
274 /* Now the new address. */
275 new_h_addr_list[naddrs++] =
276 memcpy (bufferend, tmp_result_buf.h_addr,
277 tmp_result_buf.h_length);
279 /* Also here a final NULL pointer. */
280 new_h_addr_list[naddrs] = NULL;
282 /* Store the new array pointers. */
283 old_result->h_aliases = new_h_aliases;
284 old_result->h_addr_list = new_h_addr_list;
286 /* Compute the new buffer end. */
287 bufferend = (char *) &new_h_aliases[naliases + 1];
288 assert (bufferend <= buffer + buflen);
290 result = old_result;
294 if (status == NSS_STATUS_TRYAGAIN)
296 size_t newsize = 2 * tmp_buflen;
297 if (tmp_buffer_malloced)
299 char *newp = realloc (tmp_buffer, newsize);
300 if (newp != NULL)
302 assert ((((uintptr_t) newp)
303 & (__alignof__ (struct hostent_data) - 1))
304 == 0);
305 tmp_buffer = newp;
306 tmp_buflen = newsize;
307 goto again;
310 else if (!__libc_use_alloca (buflen + newsize))
312 tmp_buffer = malloc (newsize);
313 if (tmp_buffer != NULL)
315 assert ((((uintptr_t) tmp_buffer)
316 & (__alignof__ (struct hostent_data) - 1))
317 == 0);
318 tmp_buffer_malloced = true;
319 tmp_buflen = newsize;
320 goto again;
323 else
325 tmp_buffer
326 = extend_alloca (tmp_buffer, tmp_buflen,
327 newsize
328 + __alignof__ (struct hostent_data));
329 tmp_buffer = (char *) (((uintptr_t) tmp_buffer
330 + __alignof__ (struct hostent_data)
331 - 1)
332 & ~(__alignof__ (struct hostent_data)
333 - 1));
334 goto again;
337 else
338 status = NSS_STATUS_SUCCESS;
339 out:
340 if (tmp_buffer_malloced)
341 free (tmp_buffer);
344 if (! keep_stream)
345 internal_endent ();
348 if (canonp && status == NSS_STATUS_SUCCESS)
349 *canonp = result->h_name;
351 __libc_lock_unlock (lock);
353 return status;
356 enum nss_status
357 _nss_files_gethostbyname_r (const char *name, struct hostent *result,
358 char *buffer, size_t buflen, int *errnop,
359 int *herrnop)
361 int af = ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET);
363 return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
364 errnop, herrnop, NULL, NULL);
367 enum nss_status
368 _nss_files_gethostbyname2_r (const char *name, int af, struct hostent *result,
369 char *buffer, size_t buflen, int *errnop,
370 int *herrnop)
372 return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
373 errnop, herrnop, NULL, NULL);
376 enum nss_status
377 _nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
378 char *buffer, size_t buflen, int *errnop,
379 int *herrnop, int32_t *ttlp)
381 __libc_lock_lock (lock);
383 /* Reset file pointer to beginning or open file. */
384 enum nss_status status = internal_setent (keep_stream);
386 if (status == NSS_STATUS_SUCCESS)
388 /* Tell getent function that we have repositioned the file pointer. */
389 last_use = getby;
391 bool any = false;
392 bool got_canon = false;
393 while (1)
395 /* Align the buffer for the next record. */
396 uintptr_t pad = (-(uintptr_t) buffer
397 % __alignof__ (struct hostent_data));
398 buffer += pad;
399 buflen = buflen > pad ? buflen - pad : 0;
401 struct hostent result;
402 status = internal_getent (&result, buffer, buflen, errnop,
403 herrnop, AF_UNSPEC, 0);
404 if (status != NSS_STATUS_SUCCESS)
405 break;
407 int naliases = 0;
408 if (__strcasecmp (name, result.h_name) != 0)
410 for (; result.h_aliases[naliases] != NULL; ++naliases)
411 if (! __strcasecmp (name, result.h_aliases[naliases]))
412 break;
413 if (result.h_aliases[naliases] == NULL)
414 continue;
416 /* We know this alias exist. Count it. */
417 ++naliases;
420 /* Determine how much memory has been used so far. */
421 // XXX It is not necessary to preserve the aliases array
422 while (result.h_aliases[naliases] != NULL)
423 ++naliases;
424 char *bufferend = (char *) &result.h_aliases[naliases + 1];
425 assert (buflen >= bufferend - buffer);
426 buflen -= bufferend - buffer;
427 buffer = bufferend;
429 /* We found something. */
430 any = true;
432 /* Create the record the caller expects. There is only one
433 address. */
434 assert (result.h_addr_list[1] == NULL);
435 if (*pat == NULL)
437 uintptr_t pad = (-(uintptr_t) buffer
438 % __alignof__ (struct gaih_addrtuple));
439 buffer += pad;
440 buflen = buflen > pad ? buflen - pad : 0;
442 if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
445 *errnop = ERANGE;
446 *herrnop = NETDB_INTERNAL;
447 status = NSS_STATUS_TRYAGAIN;
448 break;
451 *pat = (struct gaih_addrtuple *) buffer;
452 buffer += sizeof (struct gaih_addrtuple);
453 buflen -= sizeof (struct gaih_addrtuple);
456 (*pat)->next = NULL;
457 (*pat)->name = got_canon ? NULL : result.h_name;
458 got_canon = true;
459 (*pat)->family = result.h_addrtype;
460 memcpy ((*pat)->addr, result.h_addr_list[0], result.h_length);
461 (*pat)->scopeid = 0;
463 pat = &((*pat)->next);
465 /* If we only look for the first matching entry we are done. */
466 if ((_res_hconf.flags & HCONF_FLAG_MULTI) == 0)
467 break;
470 /* If we have to look for multiple records and found one, this
471 is a success. */
472 if (status == NSS_STATUS_NOTFOUND && any)
474 assert ((_res_hconf.flags & HCONF_FLAG_MULTI) != 0);
475 status = NSS_STATUS_SUCCESS;
478 if (! keep_stream)
479 internal_endent ();
481 else if (status == NSS_STATUS_TRYAGAIN)
483 *errnop = errno;
484 *herrnop = TRY_AGAIN;
486 else
488 *errnop = errno;
489 *herrnop = NO_DATA;
492 __libc_lock_unlock (lock);
494 return status;