1 /* Hosts file parser in nss_files module.
2 Copyright (C) 1996-2022 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 <https://www.gnu.org/licenses/>. */
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <arpa/nameser.h>
24 #include <resolv/resolv-internal.h>
25 #include <scratch_buffer.h>
26 #include <alloc_buffer.h>
29 /* Get implementation for some internal functions. */
30 #include "../resolv/res_hconf.h"
33 #define ENTNAME hostent
34 #define DATABASE "hosts"
37 #define EXTRA_ARGS , af
38 #define EXTRA_ARGS_DECL , int af
40 #define ENTDATA 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"
55 STRING_FIELD (addr
, isspace
, 1);
58 if (__inet_pton (af
== AF_UNSPEC
? AF_INET
: af
, addr
, entdata
->host_addr
)
60 af
= af
== AF_UNSPEC
? AF_INET
: af
;
64 && __inet_pton (AF_INET6
, addr
, entdata
->host_addr
) > 0)
66 if (IN6_IS_ADDR_V4MAPPED (entdata
->host_addr
))
67 memcpy (entdata
->host_addr
, entdata
->host_addr
+ 12, INADDRSZ
);
68 else if (IN6_IS_ADDR_LOOPBACK (entdata
->host_addr
))
70 in_addr_t localhost
= htonl (INADDR_LOOPBACK
);
71 memcpy (entdata
->host_addr
, &localhost
, sizeof (localhost
));
74 /* Illegal address: ignore line. */
77 else if (af
== AF_UNSPEC
78 && __inet_pton (AF_INET6
, addr
, entdata
->host_addr
) > 0)
81 /* Illegal address: ignore line. */
85 /* We always return entries of the requested form. */
86 result
->h_addrtype
= af
;
87 result
->h_length
= af
== AF_INET
? INADDRSZ
: IN6ADDRSZ
;
89 /* Store a pointer to the address in the expected form. */
90 entdata
->h_addr_ptrs
[0] = (char *) entdata
->host_addr
;
91 entdata
->h_addr_ptrs
[1] = NULL
;
92 result
->h_addr_list
= entdata
->h_addr_ptrs
;
94 STRING_FIELD (result
->h_name
, isspace
, 1);
97 #define EXTRA_ARGS_VALUE , AF_INET
98 #include "files-XXX.c"
99 #undef EXTRA_ARGS_VALUE
101 /* We only need to consider IPv4 mapped addresses if the input to the
102 gethostbyaddr() function is an IPv6 address. */
103 #define EXTRA_ARGS_VALUE , af
104 DB_LOOKUP (hostbyaddr
, ,,,
106 if (result
->h_length
== (int) len
107 && ! memcmp (addr
, result
->h_addr_list
[0], len
))
109 }, const void *addr
, socklen_t len
, int af
)
110 #undef EXTRA_ARGS_VALUE
112 /* Type of the address and alias arrays. */
113 #define DYNARRAY_STRUCT array
114 #define DYNARRAY_ELEMENT char *
115 #define DYNARRAY_PREFIX array_
116 #include <malloc/dynarray-skeleton.c>
118 static enum nss_status
119 gethostbyname3_multi (FILE * stream
, const char *name
, int af
,
120 struct hostent
*result
, char *buffer
, size_t buflen
,
121 int *errnop
, int *herrnop
)
123 assert (af
== AF_INET
|| af
== AF_INET6
);
125 /* We have to get all host entries from the file. */
126 struct scratch_buffer tmp_buffer
;
127 scratch_buffer_init (&tmp_buffer
);
128 struct hostent tmp_result_buf
;
129 struct array addresses
;
130 array_init (&addresses
);
131 struct array aliases
;
132 array_init (&aliases
);
133 enum nss_status status
;
135 /* Preserve the addresses and aliases encountered so far. */
136 for (size_t i
= 0; result
->h_addr_list
[i
] != NULL
; ++i
)
137 array_add (&addresses
, result
->h_addr_list
[i
]);
138 for (size_t i
= 0; result
->h_aliases
[i
] != NULL
; ++i
)
139 array_add (&aliases
, result
->h_aliases
[i
]);
141 /* The output buffer re-uses now-unused space at the end of the
142 buffer, starting with the aliases array. It comes last in the
143 data produced by internal_getent. (The alias names themselves
144 are still located in the line read in internal_getent, which is
145 stored at the beginning of the buffer.) */
146 struct alloc_buffer outbuf
;
148 char *bufferend
= (char *) result
->h_aliases
;
149 outbuf
= alloc_buffer_create (bufferend
, buffer
+ buflen
- bufferend
);
154 status
= internal_getent (stream
, &tmp_result_buf
, tmp_buffer
.data
,
155 tmp_buffer
.length
, errnop
, herrnop
, af
);
156 /* Enlarge the buffer if necessary. */
157 if (status
== NSS_STATUS_TRYAGAIN
&& *herrnop
== NETDB_INTERNAL
158 && *errnop
== ERANGE
)
160 if (!scratch_buffer_grow (&tmp_buffer
))
163 /* *herrnop and status already have the right value. */
166 /* Loop around and retry with a larger buffer. */
168 else if (status
== NSS_STATUS_SUCCESS
)
170 /* A line was read. Check that it matches the search
174 struct hostent
*old_result
= result
;
175 result
= &tmp_result_buf
;
176 /* The following piece is a bit clumsy but we want to use
177 the `LOOKUP_NAME_CASE' value. The optimizer should do
181 LOOKUP_NAME_CASE (h_name
, h_aliases
)
184 while ((matches
= 0));
186 /* If the line matches, we need to copy the addresses and
187 aliases, so that we can reuse tmp_buffer for the next
191 /* Record the addresses. */
192 for (size_t i
= 0; tmp_result_buf
.h_addr_list
[i
] != NULL
; ++i
)
194 /* Allocate the target space in the output buffer,
195 depending on the address family. */
199 assert (tmp_result_buf
.h_length
== 4);
200 target
= alloc_buffer_alloc (&outbuf
, struct in_addr
);
202 else if (af
== AF_INET6
)
204 assert (tmp_result_buf
.h_length
== 16);
205 target
= alloc_buffer_alloc (&outbuf
, struct in6_addr
);
208 __builtin_unreachable ();
212 /* Request a larger output buffer. */
214 *herrnop
= NETDB_INTERNAL
;
215 status
= NSS_STATUS_TRYAGAIN
;
218 memcpy (target
, tmp_result_buf
.h_addr_list
[i
],
219 tmp_result_buf
.h_length
);
220 array_add (&addresses
, target
);
223 /* Record the aliases. */
224 for (size_t i
= 0; tmp_result_buf
.h_aliases
[i
] != NULL
; ++i
)
226 char *alias
= tmp_result_buf
.h_aliases
[i
];
228 alloc_buffer_copy_string (&outbuf
, alias
));
231 /* If the real name is different add, it also to the
232 aliases. This means that there is a duplication in
233 the alias list but this is really the user's
236 char *new_name
= tmp_result_buf
.h_name
;
237 if (strcmp (old_result
->h_name
, new_name
) != 0)
239 alloc_buffer_copy_string (&outbuf
, new_name
));
242 /* Report memory allocation failures during the
243 expansion of the temporary arrays. */
244 if (array_has_failed (&addresses
) || array_has_failed (&aliases
))
247 *herrnop
= NETDB_INTERNAL
;
248 status
= NSS_STATUS_UNAVAIL
;
252 /* Request a larger output buffer if we ran out of room. */
253 if (alloc_buffer_has_failed (&outbuf
))
256 *herrnop
= NETDB_INTERNAL
;
257 status
= NSS_STATUS_TRYAGAIN
;
262 } /* If match was found. */
264 /* If no match is found, loop around and fetch another
267 } /* status == NSS_STATUS_SUCCESS. */
269 /* internal_getent returned an error. */
273 /* Propagate the NSS_STATUS_TRYAGAIN error to the caller. It means
274 that we may not have loaded the complete result.
275 NSS_STATUS_NOTFOUND, however, means that we reached the end of
276 the file successfully. */
277 if (status
!= NSS_STATUS_TRYAGAIN
)
278 status
= NSS_STATUS_SUCCESS
;
280 if (status
== NSS_STATUS_SUCCESS
)
282 /* Copy the address and alias arrays into the output buffer and
283 add NULL terminators. The pointed-to elements were directly
284 written into the output buffer above and do not need to be
286 size_t addresses_count
= array_size (&addresses
);
287 size_t aliases_count
= array_size (&aliases
);
288 char **out_addresses
= alloc_buffer_alloc_array
289 (&outbuf
, char *, addresses_count
+ 1);
290 char **out_aliases
= alloc_buffer_alloc_array
291 (&outbuf
, char *, aliases_count
+ 1);
292 if (out_addresses
== NULL
|| out_aliases
== NULL
)
294 /* The output buffer is not large enough. */
296 *herrnop
= NETDB_INTERNAL
;
297 status
= NSS_STATUS_TRYAGAIN
;
298 /* Fall through to function exit. */
302 /* Everything is allocated in place. Make the copies and
303 adjust the array pointers. */
304 memcpy (out_addresses
, array_begin (&addresses
),
305 addresses_count
* sizeof (char *));
306 out_addresses
[addresses_count
] = NULL
;
307 memcpy (out_aliases
, array_begin (&aliases
),
308 aliases_count
* sizeof (char *));
309 out_aliases
[aliases_count
] = NULL
;
311 result
->h_addr_list
= out_addresses
;
312 result
->h_aliases
= out_aliases
;
314 status
= NSS_STATUS_SUCCESS
;
318 scratch_buffer_free (&tmp_buffer
);
319 array_free (&addresses
);
320 array_free (&aliases
);
325 _nss_files_gethostbyname3_r (const char *name
, int af
, struct hostent
*result
,
326 char *buffer
, size_t buflen
, int *errnop
,
327 int *herrnop
, int32_t *ttlp
, char **canonp
)
330 uintptr_t pad
= -(uintptr_t) buffer
% __alignof__ (struct hostent_data
);
332 buflen
= buflen
> pad
? buflen
- pad
: 0;
335 enum nss_status status
= internal_setent (&stream
);
337 if (status
== NSS_STATUS_SUCCESS
)
339 while ((status
= internal_getent (stream
, result
, buffer
, buflen
, errnop
,
341 == NSS_STATUS_SUCCESS
)
343 LOOKUP_NAME_CASE (h_name
, h_aliases
)
346 if (status
== NSS_STATUS_SUCCESS
347 && _res_hconf
.flags
& HCONF_FLAG_MULTI
)
348 status
= gethostbyname3_multi
349 (stream
, name
, af
, result
, buffer
, buflen
, errnop
, herrnop
);
354 if (canonp
&& status
== NSS_STATUS_SUCCESS
)
355 *canonp
= result
->h_name
;
359 libc_hidden_def (_nss_files_gethostbyname3_r
)
362 _nss_files_gethostbyname_r (const char *name
, struct hostent
*result
,
363 char *buffer
, size_t buflen
, int *errnop
,
366 return _nss_files_gethostbyname3_r (name
, AF_INET
, result
, buffer
, buflen
,
367 errnop
, herrnop
, NULL
, NULL
);
369 libc_hidden_def (_nss_files_gethostbyname_r
)
372 _nss_files_gethostbyname2_r (const char *name
, int af
, struct hostent
*result
,
373 char *buffer
, size_t buflen
, int *errnop
,
376 return _nss_files_gethostbyname3_r (name
, af
, result
, buffer
, buflen
,
377 errnop
, herrnop
, NULL
, NULL
);
379 libc_hidden_def (_nss_files_gethostbyname2_r
)
382 _nss_files_gethostbyname4_r (const char *name
, struct gaih_addrtuple
**pat
,
383 char *buffer
, size_t buflen
, int *errnop
,
384 int *herrnop
, int32_t *ttlp
)
389 enum nss_status status
= internal_setent (&stream
);
391 if (status
== NSS_STATUS_SUCCESS
)
394 bool got_canon
= false;
397 /* Align the buffer for the next record. */
398 uintptr_t pad
= (-(uintptr_t) buffer
399 % __alignof__ (struct hostent_data
));
401 buflen
= buflen
> pad
? buflen
- pad
: 0;
403 struct hostent result
;
404 status
= internal_getent (stream
, &result
, buffer
, buflen
, errnop
,
406 if (status
!= NSS_STATUS_SUCCESS
)
410 if (__strcasecmp (name
, result
.h_name
) != 0)
412 for (; result
.h_aliases
[naliases
] != NULL
; ++naliases
)
413 if (! __strcasecmp (name
, result
.h_aliases
[naliases
]))
415 if (result
.h_aliases
[naliases
] == NULL
)
418 /* We know this alias exist. Count it. */
422 /* Determine how much memory has been used so far. */
423 // XXX It is not necessary to preserve the aliases array
424 while (result
.h_aliases
[naliases
] != NULL
)
426 char *bufferend
= (char *) &result
.h_aliases
[naliases
+ 1];
427 assert (buflen
>= bufferend
- buffer
);
428 buflen
-= bufferend
- buffer
;
431 /* We found something. */
434 /* Create the record the caller expects. There is only one
436 assert (result
.h_addr_list
[1] == NULL
);
439 uintptr_t pad
= (-(uintptr_t) buffer
440 % __alignof__ (struct gaih_addrtuple
));
442 buflen
= buflen
> pad
? buflen
- pad
: 0;
444 if (__builtin_expect (buflen
< sizeof (struct gaih_addrtuple
),
448 *herrnop
= NETDB_INTERNAL
;
449 status
= NSS_STATUS_TRYAGAIN
;
453 *pat
= (struct gaih_addrtuple
*) buffer
;
454 buffer
+= sizeof (struct gaih_addrtuple
);
455 buflen
-= sizeof (struct gaih_addrtuple
);
459 (*pat
)->name
= got_canon
? NULL
: result
.h_name
;
461 (*pat
)->family
= result
.h_addrtype
;
462 memcpy ((*pat
)->addr
, result
.h_addr_list
[0], result
.h_length
);
465 pat
= &((*pat
)->next
);
467 /* If we only look for the first matching entry we are done. */
468 if ((_res_hconf
.flags
& HCONF_FLAG_MULTI
) == 0)
472 /* If we have to look for multiple records and found one, this
474 if (status
== NSS_STATUS_NOTFOUND
&& any
)
476 assert ((_res_hconf
.flags
& HCONF_FLAG_MULTI
) != 0);
477 status
= NSS_STATUS_SUCCESS
;
482 else if (status
== NSS_STATUS_TRYAGAIN
)
485 *herrnop
= TRY_AGAIN
;
495 libc_hidden_def (_nss_files_gethostbyname4_r
)