Updated to fedora-glibc-20070331T1609
[glibc.git] / nis / nss_nisplus / nisplus-hosts.c
blobf5f0ac96dac3680266811905bea0549cb5ea9cf3
1 /* Copyright (C) 1997-2002, 2003, 2005, 2006 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 <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_INET6 || (flags & AI_V4MAPPED) != 0
70 ? IN6ADDRSZ : INADDRSZ))
72 no_more_room:
73 *errnop = ERANGE;
74 return -1;
77 /* Parse address. */
78 if (af == AF_INET && inet_pton (af, NISENTRYVAL (0, 2, result), data) > 0)
80 if (flags & AI_V4MAPPED)
82 map_v4v6_address (data, data);
83 host->h_addrtype = AF_INET6;
84 host->h_length = IN6ADDRSZ;
86 else
88 host->h_addrtype = AF_INET;
89 host->h_length = INADDRSZ;
92 else if (af == AF_INET6
93 && inet_pton (AF_INET6, NISENTRYVAL (0, 2, result), data) > 0)
95 host->h_addrtype = AF_INET6;
96 host->h_length = IN6ADDRSZ;
98 else
99 /* Illegal address: ignore line. */
100 return 0;
102 first_unused += host->h_length;
103 room_left -= host->h_length;
105 if (NISENTRYLEN (0, 0, result) + 1 > room_left)
106 goto no_more_room;
108 host->h_name = first_unused;
109 first_unused = __stpncpy (first_unused, NISENTRYVAL (0, 0, result),
110 NISENTRYLEN (0, 0, result));
111 *first_unused++ = '\0';
112 room_left -= NISENTRYLEN (0, 0, result) + 1;
114 /* XXX Rewrite at some point to allocate the array first and then
115 copy the strings. It wasteful to first concatenate the strings
116 to just split them again later. */
117 char *line = first_unused;
118 for (i = 0; i < NIS_RES_NUMOBJ (result); ++i)
120 if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0)
122 if (NISENTRYLEN (i, 1, result) + 2 > room_left)
123 goto no_more_room;
125 *first_unused++ = ' ';
126 first_unused = __stpncpy (first_unused, NISENTRYVAL (i, 1, result),
127 NISENTRYLEN (i, 1, result));
128 *first_unused = '\0';
129 room_left -= NISENTRYLEN (i, 1, result) + 1;
132 *first_unused++ = '\0';
134 /* Adjust the pointer so it is aligned for
135 storing pointers. */
136 size_t adjust = ((__alignof__ (char *)
137 - (first_unused - (char *) 0) % __alignof__ (char *))
138 % __alignof__ (char *));
139 if (room_left < adjust + 3 * sizeof (char *))
140 goto no_more_room;
141 first_unused += adjust;
142 room_left -= adjust;
143 host->h_addr_list = (char **) first_unused;
145 room_left -= 3 * sizeof (char *);
146 host->h_addr_list[0] = data;
147 host->h_addr_list[1] = NULL;
148 host->h_aliases = &host->h_addr_list[2];
150 i = 0;
151 while (*line != '\0')
153 /* Skip leading blanks. */
154 while (isspace (*line))
155 ++line;
157 if (*line == '\0')
158 break;
160 if (room_left < sizeof (char *))
161 goto no_more_room;
163 room_left -= sizeof (char *);
164 host->h_aliases[i++] = line;
166 while (*line != '\0' && *line != ' ')
167 ++line;
169 if (*line == ' ')
170 *line++ = '\0';
173 host->h_aliases[i] = NULL;
175 return 1;
179 static enum nss_status
180 _nss_create_tablename (int *errnop)
182 if (tablename_val == NULL)
184 const char *local_dir = nis_local_directory ();
185 size_t local_dir_len = strlen (local_dir);
186 static const char prefix[] = "hosts.org_dir.";
188 char *p = malloc (sizeof (prefix) + local_dir_len);
189 if (p == NULL)
191 *errnop = errno;
192 return NSS_STATUS_TRYAGAIN;
195 memcpy (__stpcpy (p, prefix), local_dir, local_dir_len + 1);
197 tablename_len = sizeof (prefix) - 1 + local_dir_len;
199 atomic_write_barrier ();
201 tablename_val = p;
204 return NSS_STATUS_SUCCESS;
207 enum nss_status
208 _nss_nisplus_sethostent (int stayopen)
210 enum nss_status status = NSS_STATUS_SUCCESS;
211 int err;
213 __libc_lock_lock (lock);
215 if (result != NULL)
217 nis_freeresult (result);
218 result = NULL;
221 if (tablename_val == NULL)
222 status = _nss_create_tablename (&err);
224 __libc_lock_unlock (lock);
226 return status;
229 enum nss_status
230 _nss_nisplus_endhostent (void)
232 __libc_lock_lock (lock);
234 if (result != NULL)
236 nis_freeresult (result);
237 result = NULL;
240 __libc_lock_unlock (lock);
242 return NSS_STATUS_SUCCESS;
245 static enum nss_status
246 internal_nisplus_gethostent_r (struct hostent *host, char *buffer,
247 size_t buflen, int *errnop, int *herrnop)
249 int parse_res;
251 /* Get the next entry until we found a correct one. */
254 nis_result *saved_res;
256 if (result == NULL)
258 saved_res = NULL;
259 if (tablename_val == NULL)
261 enum nss_status status = _nss_create_tablename (errnop);
263 if (status != NSS_STATUS_SUCCESS)
264 return status;
267 result = nis_first_entry (tablename_val);
268 if (result == NULL)
270 *errnop = errno;
271 return NSS_STATUS_TRYAGAIN;
273 if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
275 enum nss_status retval = niserr2nss (result->status);
276 if (retval == NSS_STATUS_TRYAGAIN)
278 *herrnop = NETDB_INTERNAL;
279 *errnop = errno;
281 return retval;
285 else
287 saved_res = result;
288 result = nis_next_entry (tablename_val, &result->cookie);
289 if (result == NULL)
291 *errnop = errno;
292 return NSS_STATUS_TRYAGAIN;
294 if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
296 enum nss_status retval= niserr2nss (result->status);
298 nis_freeresult (result);
299 result = saved_res;
300 if (retval == NSS_STATUS_TRYAGAIN)
302 *herrnop = NETDB_INTERNAL;
303 *errnop = errno;
305 return retval;
309 if (_res.options & RES_USE_INET6)
310 parse_res = _nss_nisplus_parse_hostent (result, AF_INET6, host, buffer,
311 buflen, errnop, AI_V4MAPPED);
312 else
313 parse_res = _nss_nisplus_parse_hostent (result, AF_INET, host, buffer,
314 buflen, errnop, 0);
316 if (parse_res == -1)
318 nis_freeresult (result);
319 result = saved_res;
320 *herrnop = NETDB_INTERNAL;
321 *errnop = ERANGE;
322 return NSS_STATUS_TRYAGAIN;
324 if (saved_res != NULL)
325 nis_freeresult (saved_res);
327 } while (!parse_res);
329 return NSS_STATUS_SUCCESS;
332 enum nss_status
333 _nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
334 size_t buflen, int *errnop, int *herrnop)
336 int status;
338 __libc_lock_lock (lock);
340 status = internal_nisplus_gethostent_r (result, buffer, buflen, errnop,
341 herrnop);
343 __libc_lock_unlock (lock);
345 return status;
348 static enum nss_status
349 internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
350 char *buffer, size_t buflen, int *errnop,
351 int *herrnop, int flags)
353 int parse_res, retval;
355 if (tablename_val == NULL)
357 __libc_lock_lock (lock);
359 enum nss_status status = _nss_create_tablename (errnop);
361 __libc_lock_unlock (lock);
363 if (status != NSS_STATUS_SUCCESS)
365 *herrnop = NETDB_INTERNAL;
366 return NSS_STATUS_UNAVAIL;
370 if (name == NULL)
372 *errnop = EINVAL;
373 *herrnop = NETDB_INTERNAL;
374 return NSS_STATUS_NOTFOUND;
377 nis_result *result;
378 char buf[strlen (name) + 10 + tablename_len];
379 int olderr = errno;
381 /* Search at first in the alias list, and use the correct name
382 for the next search. */
383 snprintf (buf, sizeof (buf), "[name=%s],%s", name, tablename_val);
384 result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
386 if (result != NULL)
388 char *bufptr = buf;
390 /* If we did not find it, try it as original name. But if the
391 database is correct, we should find it in the first case, too. */
392 if ((result->status != NIS_SUCCESS
393 && result->status != NIS_S_SUCCESS)
394 || __type_of (result->objects.objects_val) != NIS_ENTRY_OBJ
395 || strcmp (result->objects.objects_val->EN_data.en_type,
396 "hosts_tbl") != 0
397 || result->objects.objects_val->EN_data.en_cols.en_cols_len < 3)
398 snprintf (buf, sizeof (buf), "[cname=%s],%s", name, tablename_val);
399 else
401 /* We need to allocate a new buffer since there is no
402 guarantee the returned name has a length limit. */
403 const char *entryval = NISENTRYVAL(0, 0, result);
404 size_t buflen = strlen (entryval) + 10 + tablename_len;
405 bufptr = alloca (buflen);
406 snprintf (bufptr, buflen, "[cname=%s],%s",
407 entryval, tablename_val);
410 nis_freeresult (result);
411 result = nis_list (bufptr, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
414 if (result == NULL)
416 *errnop = ENOMEM;
417 return NSS_STATUS_TRYAGAIN;
420 retval = niserr2nss (result->status);
421 if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0))
423 if (retval == NSS_STATUS_TRYAGAIN)
425 *errnop = errno;
426 *herrnop = NETDB_INTERNAL;
428 else
429 __set_errno (olderr);
430 nis_freeresult (result);
431 return retval;
434 parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer,
435 buflen, errnop, flags);
437 nis_freeresult (result);
439 if (parse_res > 0)
440 return NSS_STATUS_SUCCESS;
442 *herrnop = NETDB_INTERNAL;
443 if (parse_res == -1)
445 *errnop = ERANGE;
446 return NSS_STATUS_TRYAGAIN;
449 __set_errno (olderr);
450 return NSS_STATUS_NOTFOUND;
453 enum nss_status
454 _nss_nisplus_gethostbyname2_r (const char *name, int af, struct hostent *host,
455 char *buffer, size_t buflen, int *errnop,
456 int *herrnop)
458 return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
459 herrnop,
460 ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
463 enum nss_status
464 _nss_nisplus_gethostbyname_r (const char *name, struct hostent *host,
465 char *buffer, size_t buflen, int *errnop,
466 int *h_errnop)
468 if (_res.options & RES_USE_INET6)
470 enum nss_status status;
472 status = internal_gethostbyname2_r (name, AF_INET6, host, buffer,
473 buflen, errnop, h_errnop,
474 AI_V4MAPPED);
475 if (status == NSS_STATUS_SUCCESS)
476 return status;
479 return internal_gethostbyname2_r (name, AF_INET, host, buffer,
480 buflen, errnop, h_errnop, 0);
483 enum nss_status
484 _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
485 struct hostent *host, char *buffer,
486 size_t buflen, int *errnop, int *herrnop)
488 if (tablename_val == NULL)
490 __libc_lock_lock (lock);
492 enum nss_status status = _nss_create_tablename (errnop);
494 __libc_lock_unlock (lock);
496 if (status != NSS_STATUS_SUCCESS)
497 return status;
500 if (addr == NULL)
501 return NSS_STATUS_NOTFOUND;
503 char buf[24 + tablename_len];
504 int retval, parse_res;
505 int olderr = errno;
507 snprintf (buf, sizeof (buf), "[addr=%s],%s",
508 inet_ntoa (*(const struct in_addr *) addr), tablename_val);
509 nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
511 if (result == NULL)
513 __set_errno (ENOMEM);
514 return NSS_STATUS_TRYAGAIN;
517 retval = niserr2nss (result->status);
518 if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0))
520 if (retval == NSS_STATUS_TRYAGAIN)
522 *errnop = errno;
523 *herrnop = NETDB_INTERNAL;
525 else
526 __set_errno (olderr);
527 nis_freeresult (result);
528 return retval;
531 parse_res = _nss_nisplus_parse_hostent (result, af, host,
532 buffer, buflen, errnop,
533 ((_res.options & RES_USE_INET6)
534 ? AI_V4MAPPED : 0));
535 nis_freeresult (result);
537 if (parse_res > 0)
538 return NSS_STATUS_SUCCESS;
540 *herrnop = NETDB_INTERNAL;
541 if (parse_res == -1)
543 *errnop = ERANGE;
544 return NSS_STATUS_TRYAGAIN;
547 __set_errno (olderr);
548 return NSS_STATUS_NOTFOUND;