powerpc: Fix POWER9 implies
[glibc.git] / nss / nss_files / files-hosts.c
blob2a4a665d7ed8fc9b08f6fa852a955acacd6e0eab
1 /* Hosts file parser in nss_files module.
2 Copyright (C) 1996-2016 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 FILE *stream = NULL;
124 uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);
125 buffer += pad;
126 buflen = buflen > pad ? buflen - pad : 0;
128 /* Open file. */
129 enum nss_status status = internal_setent (&stream);
131 if (status == NSS_STATUS_SUCCESS)
133 /* XXX Is using _res to determine whether we want to convert IPv4
134 addresses to IPv6 addresses really the right thing to do? */
135 int flags = ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0);
137 while ((status = internal_getent (stream, result, buffer, buflen, errnop,
138 herrnop, af, flags))
139 == NSS_STATUS_SUCCESS)
141 LOOKUP_NAME_CASE (h_name, h_aliases)
144 if (status == NSS_STATUS_SUCCESS
145 && _res_hconf.flags & HCONF_FLAG_MULTI)
147 /* We have to get all host entries from the file. */
148 size_t tmp_buflen = MIN (buflen, 4096);
149 char tmp_buffer_stack[tmp_buflen]
150 __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));
151 char *tmp_buffer = tmp_buffer_stack;
152 struct hostent tmp_result_buf;
153 int naddrs = 1;
154 int naliases = 0;
155 char *bufferend;
156 bool tmp_buffer_malloced = false;
158 while (result->h_aliases[naliases] != NULL)
159 ++naliases;
161 bufferend = (char *) &result->h_aliases[naliases + 1];
163 again:
164 while ((status = internal_getent (stream, &tmp_result_buf, tmp_buffer,
165 tmp_buflen, errnop, herrnop, af,
166 flags))
167 == NSS_STATUS_SUCCESS)
169 int matches = 1;
170 struct hostent *old_result = result;
171 result = &tmp_result_buf;
172 /* The following piece is a bit clumsy but we want to use the
173 `LOOKUP_NAME_CASE' value. The optimizer should do its
174 job. */
177 LOOKUP_NAME_CASE (h_name, h_aliases)
178 result = old_result;
180 while ((matches = 0));
182 if (matches)
184 /* We could be very clever and try to recycle a few bytes
185 in the buffer instead of generating new arrays. But
186 we are not doing this here since it's more work than
187 it's worth. Simply let the user provide a bit bigger
188 buffer. */
189 char **new_h_addr_list;
190 char **new_h_aliases;
191 int newaliases = 0;
192 size_t newstrlen = 0;
193 int cnt;
195 /* Count the new aliases and the length of the strings. */
196 while (tmp_result_buf.h_aliases[newaliases] != NULL)
198 char *cp = tmp_result_buf.h_aliases[newaliases];
199 ++newaliases;
200 newstrlen += strlen (cp) + 1;
202 /* If the real name is different add it also to the
203 aliases. This means that there is a duplication
204 in the alias list but this is really the user's
205 problem. */
206 if (strcmp (old_result->h_name,
207 tmp_result_buf.h_name) != 0)
209 ++newaliases;
210 newstrlen += strlen (tmp_result_buf.h_name) + 1;
213 /* Make sure bufferend is aligned. */
214 assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
216 /* Now we can check whether the buffer is large enough.
217 16 is the maximal size of the IP address. */
218 if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
219 + roundup (newstrlen, sizeof (char *))
220 + (naliases + newaliases + 1) * sizeof (char *)
221 >= buffer + buflen)
223 *errnop = ERANGE;
224 *herrnop = NETDB_INTERNAL;
225 status = NSS_STATUS_TRYAGAIN;
226 goto out;
229 new_h_addr_list =
230 (char **) (bufferend
231 + roundup (newstrlen, sizeof (char *))
232 + 16);
233 new_h_aliases =
234 (char **) ((char *) new_h_addr_list
235 + (naddrs + 2) * sizeof (char *));
237 /* Copy the old data in the new arrays. */
238 for (cnt = 0; cnt < naddrs; ++cnt)
239 new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
241 for (cnt = 0; cnt < naliases; ++cnt)
242 new_h_aliases[cnt] = old_result->h_aliases[cnt];
244 /* Store the new strings. */
245 cnt = 0;
246 while (tmp_result_buf.h_aliases[cnt] != NULL)
248 new_h_aliases[naliases++] = bufferend;
249 bufferend = (__stpcpy (bufferend,
250 tmp_result_buf.h_aliases[cnt])
251 + 1);
252 ++cnt;
255 if (cnt < newaliases)
257 new_h_aliases[naliases++] = bufferend;
258 bufferend = __stpcpy (bufferend,
259 tmp_result_buf.h_name) + 1;
262 /* Final NULL pointer. */
263 new_h_aliases[naliases] = NULL;
265 /* Round up the buffer end address. */
266 bufferend += (sizeof (char *)
267 - ((bufferend - (char *) 0)
268 % sizeof (char *))) % sizeof (char *);
270 /* Now the new address. */
271 new_h_addr_list[naddrs++] =
272 memcpy (bufferend, tmp_result_buf.h_addr,
273 tmp_result_buf.h_length);
275 /* Also here a final NULL pointer. */
276 new_h_addr_list[naddrs] = NULL;
278 /* Store the new array pointers. */
279 old_result->h_aliases = new_h_aliases;
280 old_result->h_addr_list = new_h_addr_list;
282 /* Compute the new buffer end. */
283 bufferend = (char *) &new_h_aliases[naliases + 1];
284 assert (bufferend <= buffer + buflen);
286 result = old_result;
290 if (status == NSS_STATUS_TRYAGAIN)
292 size_t newsize = 2 * tmp_buflen;
293 if (tmp_buffer_malloced)
295 char *newp = realloc (tmp_buffer, newsize);
296 if (newp != NULL)
298 assert ((((uintptr_t) newp)
299 & (__alignof__ (struct hostent_data) - 1))
300 == 0);
301 tmp_buffer = newp;
302 tmp_buflen = newsize;
303 goto again;
306 else if (!__libc_use_alloca (buflen + newsize))
308 tmp_buffer = malloc (newsize);
309 if (tmp_buffer != NULL)
311 assert ((((uintptr_t) tmp_buffer)
312 & (__alignof__ (struct hostent_data) - 1))
313 == 0);
314 tmp_buffer_malloced = true;
315 tmp_buflen = newsize;
316 goto again;
319 else
321 tmp_buffer
322 = extend_alloca (tmp_buffer, tmp_buflen,
323 newsize
324 + __alignof__ (struct hostent_data));
325 tmp_buffer = (char *) (((uintptr_t) tmp_buffer
326 + __alignof__ (struct hostent_data)
327 - 1)
328 & ~(__alignof__ (struct hostent_data)
329 - 1));
330 goto again;
333 else
334 status = NSS_STATUS_SUCCESS;
335 out:
336 if (tmp_buffer_malloced)
337 free (tmp_buffer);
340 internal_endent (&stream);
343 if (canonp && status == NSS_STATUS_SUCCESS)
344 *canonp = result->h_name;
346 return status;
349 enum nss_status
350 _nss_files_gethostbyname_r (const char *name, struct hostent *result,
351 char *buffer, size_t buflen, int *errnop,
352 int *herrnop)
354 int af = ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET);
356 return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
357 errnop, herrnop, NULL, NULL);
360 enum nss_status
361 _nss_files_gethostbyname2_r (const char *name, int af, struct hostent *result,
362 char *buffer, size_t buflen, int *errnop,
363 int *herrnop)
365 return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
366 errnop, herrnop, NULL, NULL);
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 FILE *stream = NULL;
376 /* Open file. */
377 enum nss_status status = internal_setent (&stream);
379 if (status == NSS_STATUS_SUCCESS)
381 bool any = false;
382 bool got_canon = false;
383 while (1)
385 /* Align the buffer for the next record. */
386 uintptr_t pad = (-(uintptr_t) buffer
387 % __alignof__ (struct hostent_data));
388 buffer += pad;
389 buflen = buflen > pad ? buflen - pad : 0;
391 struct hostent result;
392 status = internal_getent (stream, &result, buffer, buflen, errnop,
393 herrnop, AF_UNSPEC, 0);
394 if (status != NSS_STATUS_SUCCESS)
395 break;
397 int naliases = 0;
398 if (__strcasecmp (name, result.h_name) != 0)
400 for (; result.h_aliases[naliases] != NULL; ++naliases)
401 if (! __strcasecmp (name, result.h_aliases[naliases]))
402 break;
403 if (result.h_aliases[naliases] == NULL)
404 continue;
406 /* We know this alias exist. Count it. */
407 ++naliases;
410 /* Determine how much memory has been used so far. */
411 // XXX It is not necessary to preserve the aliases array
412 while (result.h_aliases[naliases] != NULL)
413 ++naliases;
414 char *bufferend = (char *) &result.h_aliases[naliases + 1];
415 assert (buflen >= bufferend - buffer);
416 buflen -= bufferend - buffer;
417 buffer = bufferend;
419 /* We found something. */
420 any = true;
422 /* Create the record the caller expects. There is only one
423 address. */
424 assert (result.h_addr_list[1] == NULL);
425 if (*pat == NULL)
427 uintptr_t pad = (-(uintptr_t) buffer
428 % __alignof__ (struct gaih_addrtuple));
429 buffer += pad;
430 buflen = buflen > pad ? buflen - pad : 0;
432 if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
435 *errnop = ERANGE;
436 *herrnop = NETDB_INTERNAL;
437 status = NSS_STATUS_TRYAGAIN;
438 break;
441 *pat = (struct gaih_addrtuple *) buffer;
442 buffer += sizeof (struct gaih_addrtuple);
443 buflen -= sizeof (struct gaih_addrtuple);
446 (*pat)->next = NULL;
447 (*pat)->name = got_canon ? NULL : result.h_name;
448 got_canon = true;
449 (*pat)->family = result.h_addrtype;
450 memcpy ((*pat)->addr, result.h_addr_list[0], result.h_length);
451 (*pat)->scopeid = 0;
453 pat = &((*pat)->next);
455 /* If we only look for the first matching entry we are done. */
456 if ((_res_hconf.flags & HCONF_FLAG_MULTI) == 0)
457 break;
460 /* If we have to look for multiple records and found one, this
461 is a success. */
462 if (status == NSS_STATUS_NOTFOUND && any)
464 assert ((_res_hconf.flags & HCONF_FLAG_MULTI) != 0);
465 status = NSS_STATUS_SUCCESS;
468 internal_endent (&stream);
470 else if (status == NSS_STATUS_TRYAGAIN)
472 *errnop = errno;
473 *herrnop = TRY_AGAIN;
475 else
477 *errnop = errno;
478 *herrnop = NO_DATA;
481 return status;