2.9
[glibc/nacl-glibc.git] / nss / nss_files / files-hosts.c
blob7b69d47fcbfa9021a06be71d46ffe33be0c59686
1 /* Hosts file parser in nss_files module.
2 Copyright (C) 1996-2001, 2003-2007, 2008 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, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
20 #include <assert.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <arpa/nameser.h>
24 #include <netdb.h>
25 #include <resolv.h>
28 /* Get implementation for some internal functions. */
29 #include "../resolv/mapv4v6addr.h"
30 #include "../resolv/res_hconf.h"
33 #define ENTNAME hostent
34 #define DATABASE "hosts"
35 #define NEED_H_ERRNO
37 #define EXTRA_ARGS , af, flags
38 #define EXTRA_ARGS_DECL , int af, int flags
40 #define ENTDATA hostent_data
41 struct hostent_data
43 unsigned char host_addr[16]; /* IPv4 or IPv6 address. */
44 char *h_addr_ptrs[2]; /* Points to that and null terminator. */
47 #define TRAILING_LIST_MEMBER h_aliases
48 #define TRAILING_LIST_SEPARATOR_P isspace
49 #include "files-parse.c"
50 LINE_PARSER
51 ("#",
53 char *addr;
55 STRING_FIELD (addr, isspace, 1);
57 /* Parse address. */
58 if (inet_pton (af == AF_UNSPEC ? AF_INET : af, addr, entdata->host_addr)
59 > 0)
60 af = af == AF_UNSPEC ? AF_INET : af;
61 else
63 if (af == AF_INET6 && (flags & AI_V4MAPPED) != 0
64 && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
65 map_v4v6_address ((char *) entdata->host_addr,
66 (char *) entdata->host_addr);
67 else if (af == AF_INET
68 && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
70 if (IN6_IS_ADDR_V4MAPPED (entdata->host_addr))
71 memcpy (entdata->host_addr, entdata->host_addr + 12, INADDRSZ);
72 else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
74 in_addr_t localhost = htonl (INADDR_LOOPBACK);
75 memcpy (entdata->host_addr, &localhost, sizeof (localhost));
77 else
78 /* Illegal address: ignore line. */
79 return 0;
81 else if (af == AF_UNSPEC
82 && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
83 af = AF_INET6;
84 else
85 /* Illegal address: ignore line. */
86 return 0;
89 /* We always return entries of the requested form. */
90 result->h_addrtype = af;
91 result->h_length = af == AF_INET ? INADDRSZ : IN6ADDRSZ;
93 /* Store a pointer to the address in the expected form. */
94 entdata->h_addr_ptrs[0] = (char *) entdata->host_addr;
95 entdata->h_addr_ptrs[1] = NULL;
96 result->h_addr_list = entdata->h_addr_ptrs;
98 STRING_FIELD (result->h_name, isspace, 1);
103 #define HOST_DB_LOOKUP(name, keysize, keypattern, break_if_match, proto...) \
104 enum nss_status \
105 _nss_files_get##name##_r (proto, \
106 struct STRUCTURE *result, char *buffer, \
107 size_t buflen, int *errnop H_ERRNO_PROTO) \
109 uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data); \
110 buffer += pad; \
111 buflen = buflen > pad ? buflen - pad : 0; \
113 __libc_lock_lock (lock); \
115 /* Reset file pointer to beginning or open file. */ \
116 enum nss_status status = internal_setent (keep_stream); \
118 if (status == NSS_STATUS_SUCCESS) \
120 /* Tell getent function that we have repositioned the file pointer. */ \
121 last_use = getby; \
123 while ((status = internal_getent (result, buffer, buflen, errnop \
124 H_ERRNO_ARG EXTRA_ARGS_VALUE)) \
125 == NSS_STATUS_SUCCESS) \
126 { break_if_match } \
128 if (status == NSS_STATUS_SUCCESS \
129 && _res_hconf.flags & HCONF_FLAG_MULTI) \
131 /* We have to get all host entries from the file. */ \
132 const size_t tmp_buflen = MIN (buflen, 4096); \
133 char tmp_buffer[tmp_buflen] \
134 __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));\
135 struct hostent tmp_result_buf; \
136 int naddrs = 1; \
137 int naliases = 0; \
138 char *bufferend; \
140 while (result->h_aliases[naliases] != NULL) \
141 ++naliases; \
143 bufferend = (char *) &result->h_aliases[naliases + 1]; \
145 while ((status = internal_getent (&tmp_result_buf, tmp_buffer, \
146 tmp_buflen, errnop H_ERRNO_ARG \
147 EXTRA_ARGS_VALUE)) \
148 == NSS_STATUS_SUCCESS) \
150 int matches = 1; \
151 struct hostent *old_result = result; \
152 result = &tmp_result_buf; \
153 /* The following piece is a bit clumsy but we want to use the \
154 `break_if_match' value. The optimizer should do its \
155 job. */ \
156 do \
158 break_if_match \
159 result = old_result; \
161 while ((matches = 0)); \
163 if (matches) \
165 /* We could be very clever and try to recycle a few bytes \
166 in the buffer instead of generating new arrays. But \
167 we are not doing this here since it's more work than \
168 it's worth. Simply let the user provide a bit bigger \
169 buffer. */ \
170 char **new_h_addr_list; \
171 char **new_h_aliases; \
172 int newaliases = 0; \
173 size_t newstrlen = 0; \
174 int cnt; \
176 /* Count the new aliases and the length of the strings. */ \
177 while (tmp_result_buf.h_aliases[newaliases] != NULL) \
179 char *cp = tmp_result_buf.h_aliases[newaliases]; \
180 ++newaliases; \
181 newstrlen += strlen (cp) + 1; \
183 /* If the real name is different add it also to the \
184 aliases. This means that there is a duplication \
185 in the alias list but this is really the users \
186 problem. */ \
187 if (strcmp (old_result->h_name, \
188 tmp_result_buf.h_name) != 0) \
190 ++newaliases; \
191 newstrlen += strlen (tmp_result_buf.h_name) + 1; \
194 /* Make sure bufferend is aligned. */ \
195 assert ((bufferend - (char *) 0) % sizeof (char *) == 0); \
197 /* Now we can check whether the buffer is large enough. \
198 16 is the maximal size of the IP address. */ \
199 if (bufferend + 16 + (naddrs + 2) * sizeof (char *) \
200 + roundup (newstrlen, sizeof (char *)) \
201 + (naliases + newaliases + 1) * sizeof (char *) \
202 >= buffer + buflen) \
204 *errnop = ERANGE; \
205 *herrnop = NETDB_INTERNAL; \
206 status = NSS_STATUS_TRYAGAIN; \
207 break; \
210 new_h_addr_list = \
211 (char **) (bufferend \
212 + roundup (newstrlen, sizeof (char *)) \
213 + 16); \
214 new_h_aliases = \
215 (char **) ((char *) new_h_addr_list \
216 + (naddrs + 2) * sizeof (char *)); \
218 /* Copy the old data in the new arrays. */ \
219 for (cnt = 0; cnt < naddrs; ++cnt) \
220 new_h_addr_list[cnt] = old_result->h_addr_list[cnt]; \
222 for (cnt = 0; cnt < naliases; ++cnt) \
223 new_h_aliases[cnt] = old_result->h_aliases[cnt]; \
225 /* Store the new strings. */ \
226 cnt = 0; \
227 while (tmp_result_buf.h_aliases[cnt] != NULL) \
229 new_h_aliases[naliases++] = bufferend; \
230 bufferend = (__stpcpy (bufferend, \
231 tmp_result_buf.h_aliases[cnt]) \
232 + 1); \
233 ++cnt; \
236 if (cnt < newaliases) \
238 new_h_aliases[naliases++] = bufferend; \
239 bufferend = __stpcpy (bufferend, \
240 tmp_result_buf.h_name) + 1; \
243 /* Final NULL pointer. */ \
244 new_h_aliases[naliases] = NULL; \
246 /* Round up the buffer end address. */ \
247 bufferend += (sizeof (char *) \
248 - ((bufferend - (char *) 0) \
249 % sizeof (char *))) % sizeof (char *); \
251 /* Now the new address. */ \
252 new_h_addr_list[naddrs++] = \
253 memcpy (bufferend, tmp_result_buf.h_addr, \
254 tmp_result_buf.h_length); \
256 /* Also here a final NULL pointer. */ \
257 new_h_addr_list[naddrs] = NULL; \
259 /* Store the new array pointers. */ \
260 old_result->h_aliases = new_h_aliases; \
261 old_result->h_addr_list = new_h_addr_list; \
263 /* Compute the new buffer end. */ \
264 bufferend = (char *) &new_h_aliases[naliases + 1]; \
265 assert (bufferend <= buffer + buflen); \
267 result = old_result; \
271 if (status != NSS_STATUS_TRYAGAIN) \
272 status = NSS_STATUS_SUCCESS; \
276 if (! keep_stream) \
277 internal_endent (); \
280 __libc_lock_unlock (lock); \
282 return status; \
286 #define EXTRA_ARGS_VALUE \
287 , ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET), \
288 ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
289 #include "files-XXX.c"
290 HOST_DB_LOOKUP (hostbyname, ,,
292 LOOKUP_NAME_CASE (h_name, h_aliases)
293 }, const char *name)
294 #undef EXTRA_ARGS_VALUE
297 /* XXX Is using _res to determine whether we want to convert IPv4 addresses
298 to IPv6 addresses really the right thing to do? */
299 #define EXTRA_ARGS_VALUE \
300 , af, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
301 HOST_DB_LOOKUP (hostbyname2, ,,
303 LOOKUP_NAME_CASE (h_name, h_aliases)
304 }, const char *name, int af)
305 #undef EXTRA_ARGS_VALUE
308 /* We only need to consider IPv4 mapped addresses if the input to the
309 gethostbyaddr() function is an IPv6 address. */
310 #define EXTRA_ARGS_VALUE \
311 , af, (len == IN6ADDRSZ ? AI_V4MAPPED : 0)
312 DB_LOOKUP (hostbyaddr, ,,
314 if (result->h_length == (int) len
315 && ! memcmp (addr, result->h_addr_list[0], len))
316 break;
317 }, const void *addr, socklen_t len, int af)
318 #undef EXTRA_ARGS_VALUE
321 enum nss_status
322 _nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
323 char *buffer, size_t buflen, int *errnop,
324 int *herrnop, int32_t *ttlp)
326 __libc_lock_lock (lock);
328 /* Reset file pointer to beginning or open file. */
329 enum nss_status status = internal_setent (keep_stream);
331 if (status == NSS_STATUS_SUCCESS)
333 /* Tell getent function that we have repositioned the file pointer. */
334 last_use = getby;
336 bool any = false;
337 bool got_canon = false;
338 while (1)
340 /* Align the buffer for the next record. */
341 uintptr_t pad = (-(uintptr_t) buffer
342 % __alignof__ (struct hostent_data));
343 buffer += pad;
344 buflen = buflen > pad ? buflen - pad : 0;
346 struct hostent result;
347 status = internal_getent (&result, buffer, buflen, errnop
348 H_ERRNO_ARG, AF_UNSPEC, 0);
349 if (status != NSS_STATUS_SUCCESS)
350 break;
352 int naliases = 0;
353 if (__strcasecmp (name, result.h_name) != 0)
355 for (; result.h_aliases[naliases] != NULL; ++naliases)
356 if (! __strcasecmp (name, result.h_aliases[naliases]))
357 break;
358 if (result.h_aliases[naliases] == NULL)
359 continue;
361 /* We know this alias exist. Count it. */
362 ++naliases;
365 /* Determine how much memory has been used so far. */
366 // XXX It is not necessary to preserve the aliases array
367 while (result.h_aliases[naliases] != NULL)
368 ++naliases;
369 char *bufferend = (char *) &result.h_aliases[naliases + 1];
370 assert (buflen >= bufferend - buffer);
371 buflen -= bufferend - buffer;
372 buffer = bufferend;
374 /* We found something. */
375 any = true;
377 /* Create the record the caller expects. There is only one
378 address. */
379 assert (result.h_addr_list[1] == NULL);
380 if (*pat == NULL)
382 uintptr_t pad = (-(uintptr_t) buffer
383 % __alignof__ (struct gaih_addrtuple));
384 buffer += pad;
385 buflen = buflen > pad ? buflen - pad : 0;
387 if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
390 *errnop = ERANGE;
391 *herrnop = NETDB_INTERNAL;
392 status = NSS_STATUS_TRYAGAIN;
393 break;
396 *pat = (struct gaih_addrtuple *) buffer;
397 buffer += sizeof (struct gaih_addrtuple);
398 buflen -= sizeof (struct gaih_addrtuple);
401 (*pat)->next = NULL;
402 (*pat)->name = got_canon ? NULL : result.h_name;
403 got_canon = true;
404 (*pat)->family = result.h_addrtype;
405 memcpy ((*pat)->addr, result.h_addr_list[0], result.h_length);
406 (*pat)->scopeid = 0;
408 pat = &((*pat)->next);
410 /* If we only look for the first matching entry we are done. */
411 if ((_res_hconf.flags & HCONF_FLAG_MULTI) == 0)
412 break;
415 /* If we have to look for multiple records and found one, this
416 is a success. */
417 if (status == NSS_STATUS_NOTFOUND && any)
419 assert ((_res_hconf.flags & HCONF_FLAG_MULTI) != 0);
420 status = NSS_STATUS_SUCCESS;
423 if (! keep_stream)
424 internal_endent ();
427 __libc_lock_unlock (lock);
429 return status;