Update copyright notices with scripts/update-copyrights
[glibc.git] / nis / nss_nisplus / nisplus-hosts.c
blob62440f2459b3dca75ea8f410e16fc627af39e18b
1 /* Copyright (C) 1997-2014 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, see
17 <http://www.gnu.org/licenses/>. */
19 #include <assert.h>
20 #include <atomic.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <netdb.h>
24 #include <nss.h>
25 #include <string.h>
26 #include <arpa/inet.h>
27 #include <netinet/in.h>
28 #include <rpcsvc/nis.h>
29 #include <bits/libc-lock.h>
31 #include "nss-nisplus.h"
33 __libc_lock_define_initialized (static, lock)
35 static nis_result *result;
36 static nis_name tablename_val;
37 static u_long tablename_len;
39 #define NISENTRYVAL(idx, col, res) \
40 (NIS_RES_OBJECT (res)[idx].EN_data.en_cols.en_cols_val[col].ec_value.ec_value_val)
42 #define NISENTRYLEN(idx, col, res) \
43 (NIS_RES_OBJECT (res)[idx].EN_data.en_cols.en_cols_val[col].ec_value.ec_value_len)
45 /* Get implementation for some internal functions. */
46 #include <resolv/mapv4v6addr.h>
49 static int
50 _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
51 char *buffer, size_t buflen, int *errnop,
52 int flags)
54 unsigned int i;
55 char *first_unused = buffer;
56 size_t room_left = buflen;
58 if (result == NULL)
59 return 0;
61 if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS)
62 || __type_of (NIS_RES_OBJECT (result)) != NIS_ENTRY_OBJ
63 || strcmp (NIS_RES_OBJECT (result)[0].EN_data.en_type, "hosts_tbl") != 0
64 || NIS_RES_OBJECT (result)[0].EN_data.en_cols.en_cols_len < 4)
65 return 0;
67 char *data = first_unused;
69 if (room_left < (af != AF_INET || (flags & AI_V4MAPPED) != 0
70 ? IN6ADDRSZ : INADDRSZ))
72 no_more_room:
73 *errnop = ERANGE;
74 return -1;
77 /* Parse address. */
78 if (af != AF_INET6
79 && inet_pton (AF_INET, NISENTRYVAL (0, 2, result), data) > 0)
81 assert ((flags & AI_V4MAPPED) == 0 || af != AF_UNSPEC);
82 if (flags & AI_V4MAPPED)
84 map_v4v6_address (data, data);
85 host->h_addrtype = AF_INET6;
86 host->h_length = IN6ADDRSZ;
88 else
90 host->h_addrtype = AF_INET;
91 host->h_length = INADDRSZ;
94 else if (af != AF_INET
95 && inet_pton (AF_INET6, NISENTRYVAL (0, 2, result), data) > 0)
97 host->h_addrtype = AF_INET6;
98 host->h_length = IN6ADDRSZ;
100 else
101 /* Illegal address: ignore line. */
102 return 0;
104 first_unused += host->h_length;
105 room_left -= host->h_length;
107 if (NISENTRYLEN (0, 0, result) + 1 > room_left)
108 goto no_more_room;
110 host->h_name = first_unused;
111 first_unused = __stpncpy (first_unused, NISENTRYVAL (0, 0, result),
112 NISENTRYLEN (0, 0, result));
113 *first_unused++ = '\0';
115 room_left -= NISENTRYLEN (0, 0, result) + 1;
116 char *line = first_unused;
118 /* When this is a call to gethostbyname4_r we do not need the aliases. */
119 if (af != AF_UNSPEC)
121 /* XXX Rewrite at some point to allocate the array first and then
122 copy the strings. It is wasteful to first concatenate the strings
123 to just split them again later. */
124 for (i = 0; i < NIS_RES_NUMOBJ (result); ++i)
126 if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0)
128 if (NISENTRYLEN (i, 1, result) + 2 > room_left)
129 goto no_more_room;
131 *first_unused++ = ' ';
132 first_unused = __stpncpy (first_unused,
133 NISENTRYVAL (i, 1, result),
134 NISENTRYLEN (i, 1, result));
135 *first_unused = '\0';
136 room_left -= NISENTRYLEN (i, 1, result) + 1;
139 *first_unused++ = '\0';
142 /* Adjust the pointer so it is aligned for
143 storing pointers. */
144 size_t adjust = ((__alignof__ (char *)
145 - (first_unused - (char *) 0) % __alignof__ (char *))
146 % __alignof__ (char *));
147 if (room_left < adjust + 3 * sizeof (char *))
148 goto no_more_room;
149 first_unused += adjust;
150 room_left -= adjust;
151 host->h_addr_list = (char **) first_unused;
153 room_left -= 3 * sizeof (char *);
154 host->h_addr_list[0] = data;
155 host->h_addr_list[1] = NULL;
156 host->h_aliases = &host->h_addr_list[2];
158 /* When this is a call to gethostbyname4_r we do not need the aliases. */
159 if (af != AF_UNSPEC)
161 i = 0;
162 while (*line != '\0')
164 /* Skip leading blanks. */
165 while (isspace (*line))
166 ++line;
168 if (*line == '\0')
169 break;
171 if (room_left < sizeof (char *))
172 goto no_more_room;
174 room_left -= sizeof (char *);
175 host->h_aliases[i++] = line;
177 while (*line != '\0' && *line != ' ')
178 ++line;
180 if (*line == ' ')
181 *line++ = '\0';
184 host->h_aliases[i] = NULL;
187 return 1;
191 static enum nss_status
192 _nss_create_tablename (int *errnop)
194 if (tablename_val == NULL)
196 const char *local_dir = nis_local_directory ();
197 size_t local_dir_len = strlen (local_dir);
198 static const char prefix[] = "hosts.org_dir.";
200 char *p = malloc (sizeof (prefix) + local_dir_len);
201 if (p == NULL)
203 *errnop = errno;
204 return NSS_STATUS_TRYAGAIN;
207 memcpy (__stpcpy (p, prefix), local_dir, local_dir_len + 1);
209 tablename_len = sizeof (prefix) - 1 + local_dir_len;
211 atomic_write_barrier ();
213 tablename_val = p;
216 return NSS_STATUS_SUCCESS;
220 enum nss_status
221 _nss_nisplus_sethostent (int stayopen)
223 enum nss_status status = NSS_STATUS_SUCCESS;
224 int err;
226 __libc_lock_lock (lock);
228 if (result != NULL)
230 nis_freeresult (result);
231 result = NULL;
234 if (tablename_val == NULL)
235 status = _nss_create_tablename (&err);
237 __libc_lock_unlock (lock);
239 return status;
243 enum nss_status
244 _nss_nisplus_endhostent (void)
246 __libc_lock_lock (lock);
248 if (result != NULL)
250 nis_freeresult (result);
251 result = NULL;
254 __libc_lock_unlock (lock);
256 return NSS_STATUS_SUCCESS;
260 static enum nss_status
261 internal_nisplus_gethostent_r (struct hostent *host, char *buffer,
262 size_t buflen, int *errnop, int *herrnop)
264 int parse_res;
266 /* Get the next entry until we found a correct one. */
269 nis_result *saved_res;
271 if (result == NULL)
273 saved_res = NULL;
274 if (tablename_val == NULL)
276 enum nss_status status = _nss_create_tablename (errnop);
278 if (status != NSS_STATUS_SUCCESS)
279 return status;
282 result = nis_first_entry (tablename_val);
283 if (result == NULL)
285 *errnop = errno;
286 return NSS_STATUS_TRYAGAIN;
288 if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
290 enum nss_status retval = niserr2nss (result->status);
291 if (retval == NSS_STATUS_TRYAGAIN)
293 *herrnop = NETDB_INTERNAL;
294 *errnop = errno;
296 return retval;
300 else
302 saved_res = result;
303 result = nis_next_entry (tablename_val, &result->cookie);
304 if (result == NULL)
306 *errnop = errno;
307 return NSS_STATUS_TRYAGAIN;
309 if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
311 enum nss_status retval= niserr2nss (result->status);
313 nis_freeresult (result);
314 result = saved_res;
315 if (retval == NSS_STATUS_TRYAGAIN)
317 *herrnop = NETDB_INTERNAL;
318 *errnop = errno;
320 return retval;
324 if (_res.options & RES_USE_INET6)
325 parse_res = _nss_nisplus_parse_hostent (result, AF_INET6, host, buffer,
326 buflen, errnop, AI_V4MAPPED);
327 else
328 parse_res = _nss_nisplus_parse_hostent (result, AF_INET, host, buffer,
329 buflen, errnop, 0);
331 if (parse_res == -1)
333 nis_freeresult (result);
334 result = saved_res;
335 *herrnop = NETDB_INTERNAL;
336 *errnop = ERANGE;
337 return NSS_STATUS_TRYAGAIN;
339 if (saved_res != NULL)
340 nis_freeresult (saved_res);
342 } while (!parse_res);
344 return NSS_STATUS_SUCCESS;
348 enum nss_status
349 _nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
350 size_t buflen, int *errnop, int *herrnop)
352 int status;
354 __libc_lock_lock (lock);
356 status = internal_nisplus_gethostent_r (result, buffer, buflen, errnop,
357 herrnop);
359 __libc_lock_unlock (lock);
361 return status;
365 static enum nss_status
366 get_tablename (int *herrnop)
368 __libc_lock_lock (lock);
370 enum nss_status status = _nss_create_tablename (herrnop);
372 __libc_lock_unlock (lock);
374 if (status != NSS_STATUS_SUCCESS)
375 *herrnop = NETDB_INTERNAL;
377 return status;
381 static enum nss_status
382 internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
383 char *buffer, size_t buflen, int *errnop,
384 int *herrnop, int flags)
386 if (tablename_val == NULL)
388 enum nss_status status = get_tablename (herrnop);
389 if (status != NSS_STATUS_SUCCESS)
390 return status;
393 if (name == NULL)
395 *errnop = EINVAL;
396 *herrnop = NETDB_INTERNAL;
397 return NSS_STATUS_NOTFOUND;
400 char buf[strlen (name) + 10 + tablename_len];
401 int olderr = errno;
403 /* Search at first in the alias list, and use the correct name
404 for the next search. */
405 snprintf (buf, sizeof (buf), "[name=%s],%s", name, tablename_val);
406 nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
408 if (result != NULL)
410 /* If we did not find it, try it as original name. But if the
411 database is correct, we should find it in the first case, too. */
412 char *bufptr = buf;
413 size_t buflen = sizeof (buf);
415 if ((result->status == NIS_SUCCESS || result->status == NIS_S_SUCCESS)
416 && __type_of (result->objects.objects_val) == NIS_ENTRY_OBJ
417 && strcmp (result->objects.objects_val->EN_data.en_type,
418 "hosts_tbl") == 0
419 && result->objects.objects_val->EN_data.en_cols.en_cols_len >= 3)
421 /* We need to allocate a new buffer since there is no
422 guarantee the returned alias name has a length limit. */
423 name = NISENTRYVAL(0, 0, result);
424 size_t buflen = strlen (name) + 10 + tablename_len;
425 bufptr = alloca (buflen);
428 snprintf (bufptr, buflen, "[cname=%s],%s", name, tablename_val);
430 nis_freeresult (result);
431 result = nis_list (bufptr, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
434 if (result == NULL)
436 *errnop = ENOMEM;
437 *herrnop = NETDB_INTERNAL;
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 = TRY_AGAIN;
449 else
451 __set_errno (olderr);
452 *herrnop = NETDB_INTERNAL;
454 nis_freeresult (result);
455 return retval;
458 int parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer,
459 buflen, errnop, flags);
461 nis_freeresult (result);
463 if (parse_res > 0)
464 return NSS_STATUS_SUCCESS;
466 *herrnop = NETDB_INTERNAL;
467 if (parse_res == -1)
469 *errnop = ERANGE;
470 return NSS_STATUS_TRYAGAIN;
473 __set_errno (olderr);
474 return NSS_STATUS_NOTFOUND;
478 enum nss_status
479 _nss_nisplus_gethostbyname2_r (const char *name, int af, struct hostent *host,
480 char *buffer, size_t buflen, int *errnop,
481 int *herrnop)
483 if (af != AF_INET && af != AF_INET6)
485 *herrnop = HOST_NOT_FOUND;
486 return NSS_STATUS_NOTFOUND;
489 return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
490 herrnop,
491 ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
495 enum nss_status
496 _nss_nisplus_gethostbyname_r (const char *name, struct hostent *host,
497 char *buffer, size_t buflen, int *errnop,
498 int *h_errnop)
500 if (_res.options & RES_USE_INET6)
502 enum nss_status status;
504 status = internal_gethostbyname2_r (name, AF_INET6, host, buffer,
505 buflen, errnop, h_errnop,
506 AI_V4MAPPED);
507 if (status == NSS_STATUS_SUCCESS)
508 return status;
511 return internal_gethostbyname2_r (name, AF_INET, host, buffer,
512 buflen, errnop, h_errnop, 0);
516 enum nss_status
517 _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
518 struct hostent *host, char *buffer,
519 size_t buflen, int *errnop, int *herrnop)
521 if (tablename_val == NULL)
523 enum nss_status status = get_tablename (herrnop);
524 if (status != NSS_STATUS_SUCCESS)
525 return status;
528 if (addr == NULL)
529 return NSS_STATUS_NOTFOUND;
531 char buf[24 + tablename_len];
532 int retval, parse_res;
533 int olderr = errno;
535 snprintf (buf, sizeof (buf), "[addr=%s],%s",
536 inet_ntoa (*(const struct in_addr *) addr), tablename_val);
537 nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
539 if (result == NULL)
541 __set_errno (ENOMEM);
542 return NSS_STATUS_TRYAGAIN;
545 retval = niserr2nss (result->status);
546 if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0))
548 if (retval == NSS_STATUS_TRYAGAIN)
550 *errnop = errno;
551 *herrnop = NETDB_INTERNAL;
553 else
554 __set_errno (olderr);
555 nis_freeresult (result);
556 return retval;
559 parse_res = _nss_nisplus_parse_hostent (result, af, host,
560 buffer, buflen, errnop,
561 ((_res.options & RES_USE_INET6)
562 ? AI_V4MAPPED : 0));
563 nis_freeresult (result);
565 if (parse_res > 0)
566 return NSS_STATUS_SUCCESS;
568 *herrnop = NETDB_INTERNAL;
569 if (parse_res == -1)
571 *errnop = ERANGE;
572 return NSS_STATUS_TRYAGAIN;
575 __set_errno (olderr);
576 return NSS_STATUS_NOTFOUND;
580 enum nss_status
581 _nss_nisplus_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
582 char *buffer, size_t buflen, int *errnop,
583 int *herrnop, int32_t *ttlp)
585 struct hostent host;
587 enum nss_status status = internal_gethostbyname2_r (name, AF_UNSPEC, &host,
588 buffer, buflen,
589 errnop, herrnop, 0);
590 if (__builtin_expect (status == NSS_STATUS_SUCCESS, 1))
592 if (*pat == NULL)
594 uintptr_t pad = (-(uintptr_t) buffer
595 % __alignof__ (struct gaih_addrtuple));
596 buffer += pad;
597 buflen = buflen > pad ? buflen - pad : 0;
599 if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple), 0))
601 free (result);
602 *errnop = ERANGE;
603 *herrnop = NETDB_INTERNAL;
604 return NSS_STATUS_TRYAGAIN;
608 (*pat)->next = NULL;
609 (*pat)->name = host.h_name;
610 (*pat)->family = host.h_addrtype;
612 memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
613 (*pat)->scopeid = 0;
614 assert (host.h_addr_list[1] == NULL);
617 return status;