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.
4
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.
9
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.
14
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/>. */
18
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>
25
26
27/* Get implementation for some internal functions. */
28#include "../resolv/mapv4v6addr.h"
29#include "../resolv/res_hconf.h"
30
31
32#define ENTNAME hostent
33#define DATABASE "hosts"
34#define NEED_H_ERRNO
35
36#define EXTRA_ARGS , af, flags
37#define EXTRA_ARGS_DECL , int af, int flags
38
39#define ENTDATA hostent_data
40struct hostent_data
41 {
42 unsigned char host_addr[16]; /* IPv4 or IPv6 address. */
43 char *h_addr_ptrs[2]; /* Points to that and null terminator. */
44 };
45
46#define TRAILING_LIST_MEMBER h_aliases
47#define TRAILING_LIST_SEPARATOR_P isspace
48#include "files-parse.c"
49LINE_PARSER
50("#",
51 {
52 char *addr;
53
54 STRING_FIELD (addr, isspace, 1);
55
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
61 {
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)
68 {
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))
72 {
73 in_addr_t localhost = htonl (INADDR_LOOPBACK);
74 memcpy (entdata->host_addr, &localhost, sizeof (localhost));
75 }
76 else
77 /* Illegal address: ignore line. */
78 return 0;
79 }
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;
86 }
87
88 /* We always return entries of the requested form. */
89 result->h_addrtype = af;
90 result->h_length = af == AF_INET ? INADDRSZ : IN6ADDRSZ;
91
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;
96
97 STRING_FIELD (result->h_name, isspace, 1);
98 })
99
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
105
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)
110DB_LOOKUP (hostbyaddr, ,,,
111 {
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
117
118enum 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)
122{
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;
127
128 /* Open file. */
129 enum nss_status status = internal_setent (&stream);
130
131 if (status == NSS_STATUS_SUCCESS)
132 {
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);
136
137 while ((status = internal_getent (stream, result, buffer, buflen, errnop,
138 herrnop, af, flags))
139 == NSS_STATUS_SUCCESS)
140 {
141 LOOKUP_NAME_CASE (h_name, h_aliases)
142 }
143
144 if (status == NSS_STATUS_SUCCESS
145 && _res_hconf.flags & HCONF_FLAG_MULTI)
146 {
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;
157
158 while (result->h_aliases[naliases] != NULL)
159 ++naliases;
160
161 bufferend = (char *) &result->h_aliases[naliases + 1];
162
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)
168 {
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. */
175 do
176 {
177 LOOKUP_NAME_CASE (h_name, h_aliases)
178 result = old_result;
179 }
180 while ((matches = 0));
181
182 if (matches)
183 {
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;
194
195 /* Count the new aliases and the length of the strings. */
196 while (tmp_result_buf.h_aliases[newaliases] != NULL)
197 {
198 char *cp = tmp_result_buf.h_aliases[newaliases];
199 ++newaliases;
200 newstrlen += strlen (cp) + 1;
201 }
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)
208 {
209 ++newaliases;
210 newstrlen += strlen (tmp_result_buf.h_name) + 1;
211 }
212
213 /* Make sure bufferend is aligned. */
214 assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
215
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)
222 {
223 *errnop = ERANGE;
224 *herrnop = NETDB_INTERNAL;
225 status = NSS_STATUS_TRYAGAIN;
226 goto out;
227 }
228
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 *));
236
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];
240
241 for (cnt = 0; cnt < naliases; ++cnt)
242 new_h_aliases[cnt] = old_result->h_aliases[cnt];
243
244 /* Store the new strings. */
245 cnt = 0;
246 while (tmp_result_buf.h_aliases[cnt] != NULL)
247 {
248 new_h_aliases[naliases++] = bufferend;
249 bufferend = (__stpcpy (bufferend,
250 tmp_result_buf.h_aliases[cnt])
251 + 1);
252 ++cnt;
253 }
254
255 if (cnt < newaliases)
256 {
257 new_h_aliases[naliases++] = bufferend;
258 bufferend = __stpcpy (bufferend,
259 tmp_result_buf.h_name) + 1;
260 }
261
262 /* Final NULL pointer. */
263 new_h_aliases[naliases] = NULL;
264
265 /* Round up the buffer end address. */
266 bufferend += (sizeof (char *)
267 - ((bufferend - (char *) 0)
268 % sizeof (char *))) % sizeof (char *);
269
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);
274
275 /* Also here a final NULL pointer. */
276 new_h_addr_list[naddrs] = NULL;
277
278 /* Store the new array pointers. */
279 old_result->h_aliases = new_h_aliases;
280 old_result->h_addr_list = new_h_addr_list;
281
282 /* Compute the new buffer end. */
283 bufferend = (char *) &new_h_aliases[naliases + 1];
284 assert (bufferend <= buffer + buflen);
285
286 result = old_result;
287 }
288 }
289
290 if (status == NSS_STATUS_TRYAGAIN)
291 {
292 size_t newsize = 2 * tmp_buflen;
293 if (tmp_buffer_malloced)
294 {
295 char *newp = realloc (tmp_buffer, newsize);
296 if (newp != NULL)
297 {
298 assert ((((uintptr_t) newp)
299 & (__alignof__ (struct hostent_data) - 1))
300 == 0);
301 tmp_buffer = newp;
302 tmp_buflen = newsize;
303 goto again;
304 }
305 }
306 else if (!__libc_use_alloca (buflen + newsize))
307 {
308 tmp_buffer = malloc (newsize);
309 if (tmp_buffer != NULL)
310 {
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;
317 }
318 }
319 else
320 {
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;
331 }
332 }
333 else
334 status = NSS_STATUS_SUCCESS;
335 out:
336 if (tmp_buffer_malloced)
337 free (tmp_buffer);
338 }
339
340 internal_endent (&stream);
341 }
342
343 if (canonp && status == NSS_STATUS_SUCCESS)
344 *canonp = result->h_name;
345
346 return status;
347}
348
349enum nss_status
350_nss_files_gethostbyname_r (const char *name, struct hostent *result,
351 char *buffer, size_t buflen, int *errnop,
352 int *herrnop)
353{
354 int af = ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET);
355
356 return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
357 errnop, herrnop, NULL, NULL);
358}
359
360enum 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)
364{
365 return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
366 errnop, herrnop, NULL, NULL);
367}
368
369enum 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)
373{
374 FILE *stream = NULL;
375
376 /* Open file. */
377 enum nss_status status = internal_setent (&stream);
378
379 if (status == NSS_STATUS_SUCCESS)
380 {
381 bool any = false;
382 bool got_canon = false;
383 while (1)
384 {
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;
390
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;
396
397 int naliases = 0;
398 if (__strcasecmp (name, result.h_name) != 0)
399 {
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;
405
406 /* We know this alias exist. Count it. */
407 ++naliases;
408 }
409
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;
418
419 /* We found something. */
420 any = true;
421
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)
426 {
427 uintptr_t pad = (-(uintptr_t) buffer
428 % __alignof__ (struct gaih_addrtuple));
429 buffer += pad;
430 buflen = buflen > pad ? buflen - pad : 0;
431
432 if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
433 0))
434 {
435 *errnop = ERANGE;
436 *herrnop = NETDB_INTERNAL;
437 status = NSS_STATUS_TRYAGAIN;
438 break;
439 }
440
441 *pat = (struct gaih_addrtuple *) buffer;
442 buffer += sizeof (struct gaih_addrtuple);
443 buflen -= sizeof (struct gaih_addrtuple);
444 }
445
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;
452
453 pat = &((*pat)->next);
454
455 /* If we only look for the first matching entry we are done. */
456 if ((_res_hconf.flags & HCONF_FLAG_MULTI) == 0)
457 break;
458 }
459
460 /* If we have to look for multiple records and found one, this
461 is a success. */
462 if (status == NSS_STATUS_NOTFOUND && any)
463 {
464 assert ((_res_hconf.flags & HCONF_FLAG_MULTI) != 0);
465 status = NSS_STATUS_SUCCESS;
466 }
467
468 internal_endent (&stream);
469 }
470 else if (status == NSS_STATUS_TRYAGAIN)
471 {
472 *errnop = errno;
473 *herrnop = TRY_AGAIN;
474 }
475 else
476 {
477 *errnop = errno;
478 *herrnop = NO_DATA;
479 }
480
481 return status;
482}
483