1 /* Copyright (C) 1997-2003, 2005, 2006, 2008, 2009 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
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>
51 _nss_nisplus_parse_hostent (nis_result
*result
, int af
, struct hostent
*host
,
52 char *buffer
, size_t buflen
, int *errnop
,
56 char *first_unused
= buffer
;
57 size_t room_left
= buflen
;
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)
68 char *data
= first_unused
;
70 if (room_left
< (af
!= AF_INET
|| (flags
& AI_V4MAPPED
) != 0
71 ? IN6ADDRSZ
: INADDRSZ
))
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
;
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
;
102 /* Illegal address: ignore line. */
105 first_unused
+= host
->h_length
;
106 room_left
-= host
->h_length
;
108 if (NISENTRYLEN (0, 0, result
) + 1 > room_left
)
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. */
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
)
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
145 size_t adjust
= ((__alignof__ (char *)
146 - (first_unused
- (char *) 0) % __alignof__ (char *))
147 % __alignof__ (char *));
148 if (room_left
< adjust
+ 3 * sizeof (char *))
150 first_unused
+= 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. */
163 while (*line
!= '\0')
165 /* Skip leading blanks. */
166 while (isspace (*line
))
172 if (room_left
< sizeof (char *))
175 room_left
-= sizeof (char *);
176 host
->h_aliases
[i
++] = line
;
178 while (*line
!= '\0' && *line
!= ' ')
185 host
->h_aliases
[i
] = NULL
;
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
);
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 ();
217 return NSS_STATUS_SUCCESS
;
222 _nss_nisplus_sethostent (int stayopen
)
224 enum nss_status status
= NSS_STATUS_SUCCESS
;
227 __libc_lock_lock (lock
);
231 nis_freeresult (result
);
235 if (tablename_val
== NULL
)
236 status
= _nss_create_tablename (&err
);
238 __libc_lock_unlock (lock
);
245 _nss_nisplus_endhostent (void)
247 __libc_lock_lock (lock
);
251 nis_freeresult (result
);
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
)
267 /* Get the next entry until we found a correct one. */
270 nis_result
*saved_res
;
275 if (tablename_val
== NULL
)
277 enum nss_status status
= _nss_create_tablename (errnop
);
279 if (status
!= NSS_STATUS_SUCCESS
)
283 result
= nis_first_entry (tablename_val
);
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
;
304 result
= nis_next_entry (tablename_val
, &result
->cookie
);
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
);
316 if (retval
== NSS_STATUS_TRYAGAIN
)
318 *herrnop
= NETDB_INTERNAL
;
325 if (_res
.options
& RES_USE_INET6
)
326 parse_res
= _nss_nisplus_parse_hostent (result
, AF_INET6
, host
, buffer
,
327 buflen
, errnop
, AI_V4MAPPED
);
329 parse_res
= _nss_nisplus_parse_hostent (result
, AF_INET
, host
, buffer
,
334 nis_freeresult (result
);
336 *herrnop
= NETDB_INTERNAL
;
338 return NSS_STATUS_TRYAGAIN
;
340 if (saved_res
!= NULL
)
341 nis_freeresult (saved_res
);
343 } while (!parse_res
);
345 return NSS_STATUS_SUCCESS
;
350 _nss_nisplus_gethostent_r (struct hostent
*result
, char *buffer
,
351 size_t buflen
, int *errnop
, int *herrnop
)
355 __libc_lock_lock (lock
);
357 status
= internal_nisplus_gethostent_r (result
, buffer
, buflen
, errnop
,
360 __libc_lock_unlock (lock
);
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
;
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
)
397 *herrnop
= NETDB_INTERNAL
;
398 return NSS_STATUS_NOTFOUND
;
401 char buf
[strlen (name
) + 10 + tablename_len
];
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
);
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. */
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
,
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
);
438 *herrnop
= NETDB_INTERNAL
;
439 return NSS_STATUS_TRYAGAIN
;
442 int retval
= niserr2nss (result
->status
);
443 if (__builtin_expect (retval
!= NSS_STATUS_SUCCESS
, 0))
445 if (retval
== NSS_STATUS_TRYAGAIN
)
448 *herrnop
= TRY_AGAIN
;
452 __set_errno (olderr
);
453 *herrnop
= NETDB_INTERNAL
;
455 nis_freeresult (result
);
459 int parse_res
= _nss_nisplus_parse_hostent (result
, af
, host
, buffer
,
460 buflen
, errnop
, flags
);
462 nis_freeresult (result
);
465 return NSS_STATUS_SUCCESS
;
467 *herrnop
= NETDB_INTERNAL
;
471 return NSS_STATUS_TRYAGAIN
;
474 __set_errno (olderr
);
475 return NSS_STATUS_NOTFOUND
;
480 _nss_nisplus_gethostbyname2_r (const char *name
, int af
, struct hostent
*host
,
481 char *buffer
, size_t buflen
, int *errnop
,
484 if (af
!= AF_INET
&& af
!= AF_INET6
)
486 *herrnop
= HOST_NOT_FOUND
;
487 return NSS_STATUS_NOTFOUND
;
490 return internal_gethostbyname2_r (name
, af
, host
, buffer
, buflen
, errnop
,
492 ((_res
.options
& RES_USE_INET6
) ? AI_V4MAPPED
: 0));
497 _nss_nisplus_gethostbyname_r (const char *name
, struct hostent
*host
,
498 char *buffer
, size_t buflen
, int *errnop
,
501 if (_res
.options
& RES_USE_INET6
)
503 enum nss_status status
;
505 status
= internal_gethostbyname2_r (name
, AF_INET6
, host
, buffer
,
506 buflen
, errnop
, h_errnop
,
508 if (status
== NSS_STATUS_SUCCESS
)
512 return internal_gethostbyname2_r (name
, AF_INET
, host
, buffer
,
513 buflen
, errnop
, h_errnop
, 0);
518 _nss_nisplus_gethostbyaddr_r (const void *addr
, socklen_t addrlen
, int af
,
519 struct hostent
*host
, char *buffer
,
520 size_t buflen
, int *errnop
, int *herrnop
)
522 if (tablename_val
== NULL
)
524 enum nss_status status
= get_tablename (herrnop
);
525 if (status
!= NSS_STATUS_SUCCESS
)
530 return NSS_STATUS_NOTFOUND
;
532 char buf
[24 + tablename_len
];
533 int retval
, parse_res
;
536 snprintf (buf
, sizeof (buf
), "[addr=%s],%s",
537 inet_ntoa (*(const struct in_addr
*) addr
), tablename_val
);
538 nis_result
*result
= nis_list (buf
, FOLLOW_PATH
| FOLLOW_LINKS
, NULL
, NULL
);
542 __set_errno (ENOMEM
);
543 return NSS_STATUS_TRYAGAIN
;
546 retval
= niserr2nss (result
->status
);
547 if (__builtin_expect (retval
!= NSS_STATUS_SUCCESS
, 0))
549 if (retval
== NSS_STATUS_TRYAGAIN
)
552 *herrnop
= NETDB_INTERNAL
;
555 __set_errno (olderr
);
556 nis_freeresult (result
);
560 parse_res
= _nss_nisplus_parse_hostent (result
, af
, host
,
561 buffer
, buflen
, errnop
,
562 ((_res
.options
& RES_USE_INET6
)
564 nis_freeresult (result
);
567 return NSS_STATUS_SUCCESS
;
569 *herrnop
= NETDB_INTERNAL
;
573 return NSS_STATUS_TRYAGAIN
;
576 __set_errno (olderr
);
577 return NSS_STATUS_NOTFOUND
;
582 _nss_nisplus_gethostbyname4_r (const char *name
, struct gaih_addrtuple
**pat
,
583 char *buffer
, size_t buflen
, int *errnop
,
584 int *herrnop
, int32_t *ttlp
)
588 enum nss_status status
= internal_gethostbyname2_r (name
, AF_UNSPEC
, &host
,
591 if (__builtin_expect (status
== NSS_STATUS_SUCCESS
, 1))
595 uintptr_t pad
= (-(uintptr_t) buffer
596 % __alignof__ (struct gaih_addrtuple
));
598 buflen
= buflen
> pad
? buflen
- pad
: 0;
600 if (__builtin_expect (buflen
< sizeof (struct gaih_addrtuple
), 0))
604 *herrnop
= NETDB_INTERNAL
;
605 return NSS_STATUS_TRYAGAIN
;
610 (*pat
)->name
= host
.h_name
;
611 (*pat
)->family
= host
.h_addrtype
;
613 memcpy ((*pat
)->addr
, host
.h_addr_list
[0], host
.h_length
);
615 assert (host
.h_addr_list
[1] == NULL
);