]> git.ipfire.org Git - thirdparty/glibc.git/blame - nss/nss_files/files-hosts.c
Separate internal state between getXXent and getXXbyYY NSS calls (bug 18007)
[thirdparty/glibc.git] / nss / nss_files / files-hosts.c
CommitLineData
5f0e6fc7 1/* Hosts file parser in nss_files module.
b168057a 2 Copyright (C) 1996-2015 Free Software Foundation, Inc.
19361cb7 3 This file is part of the GNU C Library.
5f0e6fc7 4
19361cb7 5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
5f0e6fc7 9
19361cb7
UD
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
41bdb6e2 13 Lesser General Public License for more details.
5f0e6fc7 14
41bdb6e2 15 You should have received a copy of the GNU Lesser General Public
59ba27a6
PE
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
5f0e6fc7 18
a1d4a3bc 19#include <assert.h>
5f0e6fc7
RM
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"
a1d4a3bc 29#include "../resolv/res_hconf.h"
5f0e6fc7
RM
30
31
32#define ENTNAME hostent
96bda0ea 33#define DATABASE "hosts"
adc6ff7f 34#define NEED_H_ERRNO
5f0e6fc7 35
71d3bda9
UD
36#define EXTRA_ARGS , af, flags
37#define EXTRA_ARGS_DECL , int af, int flags
38
3776d592 39#define ENTDATA hostent_data
5f0e6fc7
RM
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
ffee1316
RM
50("#",
51 {
52 char *addr;
53
54 STRING_FIELD (addr, isspace, 1);
55
56 /* Parse address. */
1eb946b9
UD
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
11bf311e
UD
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 }
1eb946b9
UD
80 else if (af == AF_UNSPEC
81 && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
82 af = AF_INET6;
11bf311e
UD
83 else
84 /* Illegal address: ignore line. */
85 return 0;
86 }
ffee1316 87
b455972f
UD
88 /* We always return entries of the requested form. */
89 result->h_addrtype = af;
90 result->h_length = af == AF_INET ? INADDRSZ : IN6ADDRSZ;
91
ffee1316 92 /* Store a pointer to the address in the expected form. */
701666b7 93 entdata->h_addr_ptrs[0] = (char *) entdata->host_addr;
ffee1316
RM
94 entdata->h_addr_ptrs[1] = NULL;
95 result->h_addr_list = entdata->h_addr_ptrs;
96
ffee1316
RM
97 STRING_FIELD (result->h_name, isspace, 1);
98 })
5f0e6fc7 99
71d3bda9
UD
100#define EXTRA_ARGS_VALUE \
101 , ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET), \
102 ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
5f0e6fc7 103#include "files-XXX.c"
4a691b06 104#undef EXTRA_ARGS_VALUE
1eb946b9 105
4a691b06
UD
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)
2666d441 110DB_LOOKUP (hostbyaddr, ,,,
5f0e6fc7 111 {
addc92bd 112 if (result->h_length == (int) len
71d3bda9 113 && ! memcmp (addr, result->h_addr_list[0], len))
5f0e6fc7 114 break;
9d4d69b8 115 }, const void *addr, socklen_t len, int af)
1eb946b9
UD
116#undef EXTRA_ARGS_VALUE
117
5a4c6d53
SP
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{
b13b96ca 123 FILE *stream = NULL;
5a4c6d53
SP
124 uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);
125 buffer += pad;
126 buflen = buflen > pad ? buflen - pad : 0;
127
b13b96ca
AS
128 /* Open file. */
129 enum nss_status status = internal_setent (&stream);
5a4c6d53
SP
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
b13b96ca 137 while ((status = internal_getent (stream, result, buffer, buflen, errnop,
5a4c6d53
SP
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:
b13b96ca 164 while ((status = internal_getent (stream, &tmp_result_buf, tmp_buffer,
5a4c6d53
SP
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
b13b96ca 340 internal_endent (&stream);
5a4c6d53
SP
341 }
342
343 if (canonp && status == NSS_STATUS_SUCCESS)
344 *canonp = result->h_name;
345
5a4c6d53
SP
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}
1eb946b9
UD
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{
b13b96ca 374 FILE *stream = NULL;
1eb946b9 375
b13b96ca
AS
376 /* Open file. */
377 enum nss_status status = internal_setent (&stream);
1eb946b9
UD
378
379 if (status == NSS_STATUS_SUCCESS)
380 {
1eb946b9
UD
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;
b13b96ca 392 status = internal_getent (stream, &result, buffer, buflen, errnop,
df3aedaf 393 herrnop, AF_UNSPEC, 0);
1eb946b9
UD
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
b13b96ca 468 internal_endent (&stream);
1eb946b9 469 }
a682a1bf
UD
470 else if (status == NSS_STATUS_TRYAGAIN)
471 {
472 *errnop = errno;
473 *herrnop = TRY_AGAIN;
474 }
5c53736b
SL
475 else
476 {
477 *errnop = errno;
478 *herrnop = NO_DATA;
479 }
1eb946b9 480
1eb946b9
UD
481 return status;
482}