2.9
[glibc/nacl-glibc.git] / nis / nss_nisplus / nisplus-hosts.c
blob37d44773fc96dd54c700c628445cf925a28c4bdf
1 /* Copyright (C) 1997-2003, 2005, 2006, 2008 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997.
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 <atomic.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <netdb.h>
25 #include <nss.h>
26 #include <string.h>
27 #include <arpa/inet.h>
28 #include <netinet/in.h>
29 #include <rpcsvc/nis.h>
30 #include <bits/libc-lock.h>
32 #include "nss-nisplus.h"
34 __libc_lock_define_initialized (static, lock)
36 static nis_result *result;
37 static nis_name tablename_val;
38 static u_long tablename_len;
40 #define NISENTRYVAL(idx, col, res) \
41 (NIS_RES_OBJECT (res)[idx].EN_data.en_cols.en_cols_val[col].ec_value.ec_value_val)
43 #define NISENTRYLEN(idx, col, res) \
44 (NIS_RES_OBJECT (res)[idx].EN_data.en_cols.en_cols_val[col].ec_value.ec_value_len)
46 /* Get implementation for some internal functions. */
47 #include <resolv/mapv4v6addr.h>
50 static int
51 _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
52 char *buffer, size_t buflen, int *errnop,
53 int flags)
55 unsigned int i;
56 char *first_unused = buffer;
57 size_t room_left = buflen;
59 if (result == NULL)
60 return 0;
62 if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS)
63 || __type_of (NIS_RES_OBJECT (result)) != NIS_ENTRY_OBJ
64 || strcmp (NIS_RES_OBJECT (result)[0].EN_data.en_type, "hosts_tbl") != 0
65 || NIS_RES_OBJECT (result)[0].EN_data.en_cols.en_cols_len < 4)
66 return 0;
68 char *data = first_unused;
70 if (room_left < (af != AF_INET || (flags & AI_V4MAPPED) != 0
71 ? IN6ADDRSZ : INADDRSZ))
73 no_more_room:
74 *errnop = ERANGE;
75 return -1;
78 /* Parse address. */
79 if (af != AF_INET6
80 && inet_pton (AF_INET, NISENTRYVAL (0, 2, result), data) > 0)
82 assert ((flags & AI_V4MAPPED) == 0 || af != AF_UNSPEC);
83 if (flags & AI_V4MAPPED)
85 map_v4v6_address (data, data);
86 host->h_addrtype = AF_INET6;
87 host->h_length = IN6ADDRSZ;
89 else
91 host->h_addrtype = AF_INET;
92 host->h_length = INADDRSZ;
95 else if (af != AF_INET
96 && inet_pton (AF_INET6, NISENTRYVAL (0, 2, result), data) > 0)
98 host->h_addrtype = AF_INET6;
99 host->h_length = IN6ADDRSZ;
101 else
102 /* Illegal address: ignore line. */
103 return 0;
105 first_unused += host->h_length;
106 room_left -= host->h_length;
108 if (NISENTRYLEN (0, 0, result) + 1 > room_left)
109 goto no_more_room;
111 host->h_name = first_unused;
112 first_unused = __stpncpy (first_unused, NISENTRYVAL (0, 0, result),
113 NISENTRYLEN (0, 0, result));
114 *first_unused++ = '\0';
116 room_left -= NISENTRYLEN (0, 0, result) + 1;
117 char *line = first_unused;
119 /* When this is a call to gethostbyname4_r we do not need the aliases. */
120 if (af != AF_UNSPEC)
122 /* XXX Rewrite at some point to allocate the array first and then
123 copy the strings. It is wasteful to first concatenate the strings
124 to just split them again later. */
125 for (i = 0; i < NIS_RES_NUMOBJ (result); ++i)
127 if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0)
129 if (NISENTRYLEN (i, 1, result) + 2 > room_left)
130 goto no_more_room;
132 *first_unused++ = ' ';
133 first_unused = __stpncpy (first_unused,
134 NISENTRYVAL (i, 1, result),
135 NISENTRYLEN (i, 1, result));
136 *first_unused = '\0';
137 room_left -= NISENTRYLEN (i, 1, result) + 1;
140 *first_unused++ = '\0';
143 /* Adjust the pointer so it is aligned for
144 storing pointers. */
145 size_t adjust = ((__alignof__ (char *)
146 - (first_unused - (char *) 0) % __alignof__ (char *))
147 % __alignof__ (char *));
148 if (room_left < adjust + 3 * sizeof (char *))
149 goto no_more_room;
150 first_unused += adjust;
151 room_left -= adjust;
152 host->h_addr_list = (char **) first_unused;
154 room_left -= 3 * sizeof (char *);
155 host->h_addr_list[0] = data;
156 host->h_addr_list[1] = NULL;
157 host->h_aliases = &host->h_addr_list[2];
159 /* When this is a call to gethostbyname4_r we do not need the aliases. */
160 if (af != AF_UNSPEC)
162 i = 0;
163 while (*line != '\0')
165 /* Skip leading blanks. */
166 while (isspace (*line))
167 ++line;
169 if (*line == '\0')
170 break;
172 if (room_left < sizeof (char *))
173 goto no_more_room;
175 room_left -= sizeof (char *);
176 host->h_aliases[i++] = line;
178 while (*line != '\0' && *line != ' ')
179 ++line;
181 if (*line == ' ')
182 *line++ = '\0';
185 host->h_aliases[i] = NULL;
188 return 1;
192 static enum nss_status
193 _nss_create_tablename (int *errnop)
195 if (tablename_val == NULL)
197 const char *local_dir = nis_local_directory ();
198 size_t local_dir_len = strlen (local_dir);
199 static const char prefix[] = "hosts.org_dir.";
201 char *p = malloc (sizeof (prefix) + local_dir_len);
202 if (p == NULL)
204 *errnop = errno;
205 return NSS_STATUS_TRYAGAIN;
208 memcpy (__stpcpy (p, prefix), local_dir, local_dir_len + 1);
210 tablename_len = sizeof (prefix) - 1 + local_dir_len;
212 atomic_write_barrier ();
214 tablename_val = p;
217 return NSS_STATUS_SUCCESS;
221 enum nss_status
222 _nss_nisplus_sethostent (int stayopen)
224 enum nss_status status = NSS_STATUS_SUCCESS;
225 int err;
227 __libc_lock_lock (lock);
229 if (result != NULL)
231 nis_freeresult (result);
232 result = NULL;
235 if (tablename_val == NULL)
236 status = _nss_create_tablename (&err);
238 __libc_lock_unlock (lock);
240 return status;
244 enum nss_status
245 _nss_nisplus_endhostent (void)
247 __libc_lock_lock (lock);
249 if (result != NULL)
251 nis_freeresult (result);
252 result = NULL;
255 __libc_lock_unlock (lock);
257 return NSS_STATUS_SUCCESS;
261 static enum nss_status
262 internal_nisplus_gethostent_r (struct hostent *host, char *buffer,
263 size_t buflen, int *errnop, int *herrnop)
265 int parse_res;
267 /* Get the next entry until we found a correct one. */
270 nis_result *saved_res;
272 if (result == NULL)
274 saved_res = NULL;
275 if (tablename_val == NULL)
277 enum nss_status status = _nss_create_tablename (errnop);
279 if (status != NSS_STATUS_SUCCESS)
280 return status;
283 result = nis_first_entry (tablename_val);
284 if (result == NULL)
286 *errnop = errno;
287 return NSS_STATUS_TRYAGAIN;
289 if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
291 enum nss_status retval = niserr2nss (result->status);
292 if (retval == NSS_STATUS_TRYAGAIN)
294 *herrnop = NETDB_INTERNAL;
295 *errnop = errno;
297 return retval;
301 else
303 saved_res = result;
304 result = nis_next_entry (tablename_val, &result->cookie);
305 if (result == NULL)
307 *errnop = errno;
308 return NSS_STATUS_TRYAGAIN;
310 if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
312 enum nss_status retval= niserr2nss (result->status);
314 nis_freeresult (result);
315 result = saved_res;
316 if (retval == NSS_STATUS_TRYAGAIN)
318 *herrnop = NETDB_INTERNAL;
319 *errnop = errno;
321 return retval;
325 if (_res.options & RES_USE_INET6)
326 parse_res = _nss_nisplus_parse_hostent (result, AF_INET6, host, buffer,
327 buflen, errnop, AI_V4MAPPED);
328 else
329 parse_res = _nss_nisplus_parse_hostent (result, AF_INET, host, buffer,
330 buflen, errnop, 0);
332 if (parse_res == -1)
334 nis_freeresult (result);
335 result = saved_res;
336 *herrnop = NETDB_INTERNAL;
337 *errnop = ERANGE;
338 return NSS_STATUS_TRYAGAIN;
340 if (saved_res != NULL)
341 nis_freeresult (saved_res);
343 } while (!parse_res);
345 return NSS_STATUS_SUCCESS;
349 enum nss_status
350 _nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
351 size_t buflen, int *errnop, int *herrnop)
353 int status;
355 __libc_lock_lock (lock);
357 status = internal_nisplus_gethostent_r (result, buffer, buflen, errnop,
358 herrnop);
360 __libc_lock_unlock (lock);
362 return status;
366 static enum nss_status
367 get_tablename (int *herrnop)
369 __libc_lock_lock (lock);
371 enum nss_status status = _nss_create_tablename (herrnop);
373 __libc_lock_unlock (lock);
375 if (status != NSS_STATUS_SUCCESS)
376 *herrnop = NETDB_INTERNAL;
378 return status;
382 static enum nss_status
383 internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
384 char *buffer, size_t buflen, int *errnop,
385 int *herrnop, int flags)
387 if (tablename_val == NULL)
389 enum nss_status status = get_tablename (herrnop);
390 if (status != NSS_STATUS_SUCCESS)
391 return status;
394 if (name == NULL)
396 *errnop = EINVAL;
397 *herrnop = NETDB_INTERNAL;
398 return NSS_STATUS_NOTFOUND;
401 char buf[strlen (name) + 10 + tablename_len];
402 int olderr = errno;
404 /* Search at first in the alias list, and use the correct name
405 for the next search. */
406 snprintf (buf, sizeof (buf), "[name=%s],%s", name, tablename_val);
407 nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
409 if (result != NULL)
411 /* If we did not find it, try it as original name. But if the
412 database is correct, we should find it in the first case, too. */
413 char *bufptr = buf;
414 size_t buflen = sizeof (buf);
416 if ((result->status == NIS_SUCCESS || result->status == NIS_S_SUCCESS)
417 && __type_of (result->objects.objects_val) == NIS_ENTRY_OBJ
418 && strcmp (result->objects.objects_val->EN_data.en_type,
419 "hosts_tbl") == 0
420 && result->objects.objects_val->EN_data.en_cols.en_cols_len >= 3)
422 /* We need to allocate a new buffer since there is no
423 guarantee the returned alias name has a length limit. */
424 name = NISENTRYVAL(0, 0, result);
425 size_t buflen = strlen (name) + 10 + tablename_len;
426 bufptr = alloca (buflen);
429 snprintf (bufptr, buflen, "[cname=%s],%s", name, tablename_val);
431 nis_freeresult (result);
432 result = nis_list (bufptr, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
435 if (result == NULL)
437 *errnop = ENOMEM;
438 return NSS_STATUS_TRYAGAIN;
441 int retval = niserr2nss (result->status);
442 if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0))
444 if (retval == NSS_STATUS_TRYAGAIN)
446 *errnop = errno;
447 *herrnop = NETDB_INTERNAL;
449 else
450 __set_errno (olderr);
451 nis_freeresult (result);
452 return retval;
455 int parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer,
456 buflen, errnop, flags);
458 nis_freeresult (result);
460 if (parse_res > 0)
461 return NSS_STATUS_SUCCESS;
463 *herrnop = NETDB_INTERNAL;
464 if (parse_res == -1)
466 *errnop = ERANGE;
467 return NSS_STATUS_TRYAGAIN;
470 __set_errno (olderr);
471 return NSS_STATUS_NOTFOUND;
475 enum nss_status
476 _nss_nisplus_gethostbyname2_r (const char *name, int af, struct hostent *host,
477 char *buffer, size_t buflen, int *errnop,
478 int *herrnop)
480 if (af != AF_INET && af != AF_INET6)
482 *herrnop = HOST_NOT_FOUND;
483 return NSS_STATUS_NOTFOUND;
486 return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
487 herrnop,
488 ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
492 enum nss_status
493 _nss_nisplus_gethostbyname_r (const char *name, struct hostent *host,
494 char *buffer, size_t buflen, int *errnop,
495 int *h_errnop)
497 if (_res.options & RES_USE_INET6)
499 enum nss_status status;
501 status = internal_gethostbyname2_r (name, AF_INET6, host, buffer,
502 buflen, errnop, h_errnop,
503 AI_V4MAPPED);
504 if (status == NSS_STATUS_SUCCESS)
505 return status;
508 return internal_gethostbyname2_r (name, AF_INET, host, buffer,
509 buflen, errnop, h_errnop, 0);
513 enum nss_status
514 _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
515 struct hostent *host, char *buffer,
516 size_t buflen, int *errnop, int *herrnop)
518 if (tablename_val == NULL)
520 enum nss_status status = get_tablename (herrnop);
521 if (status != NSS_STATUS_SUCCESS)
522 return status;
525 if (addr == NULL)
526 return NSS_STATUS_NOTFOUND;
528 char buf[24 + tablename_len];
529 int retval, parse_res;
530 int olderr = errno;
532 snprintf (buf, sizeof (buf), "[addr=%s],%s",
533 inet_ntoa (*(const struct in_addr *) addr), tablename_val);
534 nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
536 if (result == NULL)
538 __set_errno (ENOMEM);
539 return NSS_STATUS_TRYAGAIN;
542 retval = niserr2nss (result->status);
543 if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0))
545 if (retval == NSS_STATUS_TRYAGAIN)
547 *errnop = errno;
548 *herrnop = NETDB_INTERNAL;
550 else
551 __set_errno (olderr);
552 nis_freeresult (result);
553 return retval;
556 parse_res = _nss_nisplus_parse_hostent (result, af, host,
557 buffer, buflen, errnop,
558 ((_res.options & RES_USE_INET6)
559 ? AI_V4MAPPED : 0));
560 nis_freeresult (result);
562 if (parse_res > 0)
563 return NSS_STATUS_SUCCESS;
565 *herrnop = NETDB_INTERNAL;
566 if (parse_res == -1)
568 *errnop = ERANGE;
569 return NSS_STATUS_TRYAGAIN;
572 __set_errno (olderr);
573 return NSS_STATUS_NOTFOUND;
577 enum nss_status
578 _nss_nisplus_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
579 char *buffer, size_t buflen, int *errnop,
580 int *herrnop, int32_t *ttlp)
582 struct hostent host;
584 enum nss_status status = internal_gethostbyname2_r (name, AF_UNSPEC, &host,
585 buffer, buflen,
586 errnop, herrnop, 0);
587 if (__builtin_expect (status == NSS_STATUS_SUCCESS, 1))
589 if (*pat == NULL)
591 uintptr_t pad = (-(uintptr_t) buffer
592 % __alignof__ (struct gaih_addrtuple));
593 buffer += pad;
594 buflen = buflen > pad ? buflen - pad : 0;
596 if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple), 0))
598 free (result);
599 *errnop = ERANGE;
600 *herrnop = NETDB_INTERNAL;
601 return NSS_STATUS_TRYAGAIN;
605 (*pat)->next = NULL;
606 (*pat)->name = host.h_name;
607 (*pat)->family = host.h_addrtype;
609 memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
610 (*pat)->scopeid = 0;
611 assert (host.h_addr_list[1] == NULL);
614 return status;