]> git.ipfire.org Git - thirdparty/glibc.git/blob - nss/nss_files/files-hosts.c
ab64eadabb7e64765f66cda173b3fd0fe42d049c
[thirdparty/glibc.git] / nss / nss_files / files-hosts.c
1 /* Hosts file parser in nss_files module.
2 Copyright (C) 1996-2014 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
40 struct 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"
49 LINE_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)
110 DB_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
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)
122 {
123 uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);
124 buffer += pad;
125 buflen = buflen > pad ? buflen - pad : 0;
126
127 __libc_lock_lock (lock);
128
129 /* Reset file pointer to beginning or open file. */
130 enum nss_status status = internal_setent (keep_stream);
131
132 if (status == NSS_STATUS_SUCCESS)
133 {
134 /* XXX Is using _res to determine whether we want to convert IPv4
135 addresses to IPv6 addresses really the right thing to do? */
136 int flags = ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0);
137
138 /* Tell getent function that we have repositioned the file pointer. */
139 last_use = getby;
140
141 while ((status = internal_getent (result, buffer, buflen, errnop,
142 herrnop, af, flags))
143 == NSS_STATUS_SUCCESS)
144 {
145 LOOKUP_NAME_CASE (h_name, h_aliases)
146 }
147
148 if (status == NSS_STATUS_SUCCESS
149 && _res_hconf.flags & HCONF_FLAG_MULTI)
150 {
151 /* We have to get all host entries from the file. */
152 size_t tmp_buflen = MIN (buflen, 4096);
153 char tmp_buffer_stack[tmp_buflen]
154 __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));
155 char *tmp_buffer = tmp_buffer_stack;
156 struct hostent tmp_result_buf;
157 int naddrs = 1;
158 int naliases = 0;
159 char *bufferend;
160 bool tmp_buffer_malloced = false;
161
162 while (result->h_aliases[naliases] != NULL)
163 ++naliases;
164
165 bufferend = (char *) &result->h_aliases[naliases + 1];
166
167 again:
168 while ((status = internal_getent (&tmp_result_buf, tmp_buffer,
169 tmp_buflen, errnop, herrnop, af,
170 flags))
171 == NSS_STATUS_SUCCESS)
172 {
173 int matches = 1;
174 struct hostent *old_result = result;
175 result = &tmp_result_buf;
176 /* The following piece is a bit clumsy but we want to use the
177 `LOOKUP_NAME_CASE' value. The optimizer should do its
178 job. */
179 do
180 {
181 LOOKUP_NAME_CASE (h_name, h_aliases)
182 result = old_result;
183 }
184 while ((matches = 0));
185
186 if (matches)
187 {
188 /* We could be very clever and try to recycle a few bytes
189 in the buffer instead of generating new arrays. But
190 we are not doing this here since it's more work than
191 it's worth. Simply let the user provide a bit bigger
192 buffer. */
193 char **new_h_addr_list;
194 char **new_h_aliases;
195 int newaliases = 0;
196 size_t newstrlen = 0;
197 int cnt;
198
199 /* Count the new aliases and the length of the strings. */
200 while (tmp_result_buf.h_aliases[newaliases] != NULL)
201 {
202 char *cp = tmp_result_buf.h_aliases[newaliases];
203 ++newaliases;
204 newstrlen += strlen (cp) + 1;
205 }
206 /* If the real name is different add it also to the
207 aliases. This means that there is a duplication
208 in the alias list but this is really the user's
209 problem. */
210 if (strcmp (old_result->h_name,
211 tmp_result_buf.h_name) != 0)
212 {
213 ++newaliases;
214 newstrlen += strlen (tmp_result_buf.h_name) + 1;
215 }
216
217 /* Make sure bufferend is aligned. */
218 assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
219
220 /* Now we can check whether the buffer is large enough.
221 16 is the maximal size of the IP address. */
222 if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
223 + roundup (newstrlen, sizeof (char *))
224 + (naliases + newaliases + 1) * sizeof (char *)
225 >= buffer + buflen)
226 {
227 *errnop = ERANGE;
228 *herrnop = NETDB_INTERNAL;
229 status = NSS_STATUS_TRYAGAIN;
230 goto out;
231 }
232
233 new_h_addr_list =
234 (char **) (bufferend
235 + roundup (newstrlen, sizeof (char *))
236 + 16);
237 new_h_aliases =
238 (char **) ((char *) new_h_addr_list
239 + (naddrs + 2) * sizeof (char *));
240
241 /* Copy the old data in the new arrays. */
242 for (cnt = 0; cnt < naddrs; ++cnt)
243 new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
244
245 for (cnt = 0; cnt < naliases; ++cnt)
246 new_h_aliases[cnt] = old_result->h_aliases[cnt];
247
248 /* Store the new strings. */
249 cnt = 0;
250 while (tmp_result_buf.h_aliases[cnt] != NULL)
251 {
252 new_h_aliases[naliases++] = bufferend;
253 bufferend = (__stpcpy (bufferend,
254 tmp_result_buf.h_aliases[cnt])
255 + 1);
256 ++cnt;
257 }
258
259 if (cnt < newaliases)
260 {
261 new_h_aliases[naliases++] = bufferend;
262 bufferend = __stpcpy (bufferend,
263 tmp_result_buf.h_name) + 1;
264 }
265
266 /* Final NULL pointer. */
267 new_h_aliases[naliases] = NULL;
268
269 /* Round up the buffer end address. */
270 bufferend += (sizeof (char *)
271 - ((bufferend - (char *) 0)
272 % sizeof (char *))) % sizeof (char *);
273
274 /* Now the new address. */
275 new_h_addr_list[naddrs++] =
276 memcpy (bufferend, tmp_result_buf.h_addr,
277 tmp_result_buf.h_length);
278
279 /* Also here a final NULL pointer. */
280 new_h_addr_list[naddrs] = NULL;
281
282 /* Store the new array pointers. */
283 old_result->h_aliases = new_h_aliases;
284 old_result->h_addr_list = new_h_addr_list;
285
286 /* Compute the new buffer end. */
287 bufferend = (char *) &new_h_aliases[naliases + 1];
288 assert (bufferend <= buffer + buflen);
289
290 result = old_result;
291 }
292 }
293
294 if (status == NSS_STATUS_TRYAGAIN)
295 {
296 size_t newsize = 2 * tmp_buflen;
297 if (tmp_buffer_malloced)
298 {
299 char *newp = realloc (tmp_buffer, newsize);
300 if (newp != NULL)
301 {
302 assert ((((uintptr_t) newp)
303 & (__alignof__ (struct hostent_data) - 1))
304 == 0);
305 tmp_buffer = newp;
306 tmp_buflen = newsize;
307 goto again;
308 }
309 }
310 else if (!__libc_use_alloca (buflen + newsize))
311 {
312 tmp_buffer = malloc (newsize);
313 if (tmp_buffer != NULL)
314 {
315 assert ((((uintptr_t) tmp_buffer)
316 & (__alignof__ (struct hostent_data) - 1))
317 == 0);
318 tmp_buffer_malloced = true;
319 tmp_buflen = newsize;
320 goto again;
321 }
322 }
323 else
324 {
325 tmp_buffer
326 = extend_alloca (tmp_buffer, tmp_buflen,
327 newsize
328 + __alignof__ (struct hostent_data));
329 tmp_buffer = (char *) (((uintptr_t) tmp_buffer
330 + __alignof__ (struct hostent_data)
331 - 1)
332 & ~(__alignof__ (struct hostent_data)
333 - 1));
334 goto again;
335 }
336 }
337 else
338 status = NSS_STATUS_SUCCESS;
339 out:
340 if (tmp_buffer_malloced)
341 free (tmp_buffer);
342 }
343
344 if (! keep_stream)
345 internal_endent ();
346 }
347
348 if (canonp && status == NSS_STATUS_SUCCESS)
349 *canonp = result->h_name;
350
351 __libc_lock_unlock (lock);
352
353 return status;
354 }
355
356 enum nss_status
357 _nss_files_gethostbyname_r (const char *name, struct hostent *result,
358 char *buffer, size_t buflen, int *errnop,
359 int *herrnop)
360 {
361 int af = ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET);
362
363 return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
364 errnop, herrnop, NULL, NULL);
365 }
366
367 enum nss_status
368 _nss_files_gethostbyname2_r (const char *name, int af, struct hostent *result,
369 char *buffer, size_t buflen, int *errnop,
370 int *herrnop)
371 {
372 return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
373 errnop, herrnop, NULL, NULL);
374 }
375
376 enum nss_status
377 _nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
378 char *buffer, size_t buflen, int *errnop,
379 int *herrnop, int32_t *ttlp)
380 {
381 __libc_lock_lock (lock);
382
383 /* Reset file pointer to beginning or open file. */
384 enum nss_status status = internal_setent (keep_stream);
385
386 if (status == NSS_STATUS_SUCCESS)
387 {
388 /* Tell getent function that we have repositioned the file pointer. */
389 last_use = getby;
390
391 bool any = false;
392 bool got_canon = false;
393 while (1)
394 {
395 /* Align the buffer for the next record. */
396 uintptr_t pad = (-(uintptr_t) buffer
397 % __alignof__ (struct hostent_data));
398 buffer += pad;
399 buflen = buflen > pad ? buflen - pad : 0;
400
401 struct hostent result;
402 status = internal_getent (&result, buffer, buflen, errnop,
403 herrnop, AF_UNSPEC, 0);
404 if (status != NSS_STATUS_SUCCESS)
405 break;
406
407 int naliases = 0;
408 if (__strcasecmp (name, result.h_name) != 0)
409 {
410 for (; result.h_aliases[naliases] != NULL; ++naliases)
411 if (! __strcasecmp (name, result.h_aliases[naliases]))
412 break;
413 if (result.h_aliases[naliases] == NULL)
414 continue;
415
416 /* We know this alias exist. Count it. */
417 ++naliases;
418 }
419
420 /* Determine how much memory has been used so far. */
421 // XXX It is not necessary to preserve the aliases array
422 while (result.h_aliases[naliases] != NULL)
423 ++naliases;
424 char *bufferend = (char *) &result.h_aliases[naliases + 1];
425 assert (buflen >= bufferend - buffer);
426 buflen -= bufferend - buffer;
427 buffer = bufferend;
428
429 /* We found something. */
430 any = true;
431
432 /* Create the record the caller expects. There is only one
433 address. */
434 assert (result.h_addr_list[1] == NULL);
435 if (*pat == NULL)
436 {
437 uintptr_t pad = (-(uintptr_t) buffer
438 % __alignof__ (struct gaih_addrtuple));
439 buffer += pad;
440 buflen = buflen > pad ? buflen - pad : 0;
441
442 if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple),
443 0))
444 {
445 *errnop = ERANGE;
446 *herrnop = NETDB_INTERNAL;
447 status = NSS_STATUS_TRYAGAIN;
448 break;
449 }
450
451 *pat = (struct gaih_addrtuple *) buffer;
452 buffer += sizeof (struct gaih_addrtuple);
453 buflen -= sizeof (struct gaih_addrtuple);
454 }
455
456 (*pat)->next = NULL;
457 (*pat)->name = got_canon ? NULL : result.h_name;
458 got_canon = true;
459 (*pat)->family = result.h_addrtype;
460 memcpy ((*pat)->addr, result.h_addr_list[0], result.h_length);
461 (*pat)->scopeid = 0;
462
463 pat = &((*pat)->next);
464
465 /* If we only look for the first matching entry we are done. */
466 if ((_res_hconf.flags & HCONF_FLAG_MULTI) == 0)
467 break;
468 }
469
470 /* If we have to look for multiple records and found one, this
471 is a success. */
472 if (status == NSS_STATUS_NOTFOUND && any)
473 {
474 assert ((_res_hconf.flags & HCONF_FLAG_MULTI) != 0);
475 status = NSS_STATUS_SUCCESS;
476 }
477
478 if (! keep_stream)
479 internal_endent ();
480 }
481 else if (status == NSS_STATUS_TRYAGAIN)
482 {
483 *errnop = errno;
484 *herrnop = TRY_AGAIN;
485 }
486 else
487 {
488 *errnop = errno;
489 *herrnop = NO_DATA;
490 }
491
492 __libc_lock_unlock (lock);
493
494 return status;
495 }