1 /* Copyright (C) 1997-2016 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/>. */
26 #include <arpa/inet.h>
27 #include <netinet/in.h>
28 #include <rpcsvc/nis.h>
29 #include <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>
50 _nss_nisplus_parse_hostent (nis_result
*result
, int af
, struct hostent
*host
,
51 char *buffer
, size_t buflen
, int *errnop
,
55 char *first_unused
= buffer
;
56 size_t room_left
= buflen
;
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)
67 char *data
= first_unused
;
69 if (room_left
< (af
!= AF_INET
|| (flags
& AI_V4MAPPED
) != 0
70 ? IN6ADDRSZ
: INADDRSZ
))
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
;
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
;
101 /* Illegal address: ignore line. */
104 first_unused
+= host
->h_length
;
105 room_left
-= host
->h_length
;
107 if (NISENTRYLEN (0, 0, result
) + 1 > room_left
)
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. */
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
)
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
144 size_t adjust
= ((__alignof__ (char *)
145 - (first_unused
- (char *) 0) % __alignof__ (char *))
146 % __alignof__ (char *));
147 if (room_left
< adjust
+ 3 * sizeof (char *))
149 first_unused
+= 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. */
162 while (*line
!= '\0')
164 /* Skip leading blanks. */
165 while (isspace (*line
))
171 if (room_left
< sizeof (char *))
174 room_left
-= sizeof (char *);
175 host
->h_aliases
[i
++] = line
;
177 while (*line
!= '\0' && *line
!= ' ')
184 host
->h_aliases
[i
] = NULL
;
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
);
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 ();
216 return NSS_STATUS_SUCCESS
;
221 _nss_nisplus_sethostent (int stayopen
)
223 enum nss_status status
= NSS_STATUS_SUCCESS
;
226 __libc_lock_lock (lock
);
230 nis_freeresult (result
);
234 if (tablename_val
== NULL
)
235 status
= _nss_create_tablename (&err
);
237 __libc_lock_unlock (lock
);
244 _nss_nisplus_endhostent (void)
246 __libc_lock_lock (lock
);
250 nis_freeresult (result
);
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
)
266 /* Get the next entry until we found a correct one. */
269 nis_result
*saved_res
;
274 if (tablename_val
== NULL
)
276 enum nss_status status
= _nss_create_tablename (errnop
);
278 if (status
!= NSS_STATUS_SUCCESS
)
282 result
= nis_first_entry (tablename_val
);
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
;
303 result
= nis_next_entry (tablename_val
, &result
->cookie
);
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
);
315 if (retval
== NSS_STATUS_TRYAGAIN
)
317 *herrnop
= NETDB_INTERNAL
;
324 if (_res
.options
& RES_USE_INET6
)
325 parse_res
= _nss_nisplus_parse_hostent (result
, AF_INET6
, host
, buffer
,
326 buflen
, errnop
, AI_V4MAPPED
);
328 parse_res
= _nss_nisplus_parse_hostent (result
, AF_INET
, host
, buffer
,
333 nis_freeresult (result
);
335 *herrnop
= NETDB_INTERNAL
;
337 return NSS_STATUS_TRYAGAIN
;
339 if (saved_res
!= NULL
)
340 nis_freeresult (saved_res
);
342 } while (!parse_res
);
344 return NSS_STATUS_SUCCESS
;
349 _nss_nisplus_gethostent_r (struct hostent
*result
, char *buffer
,
350 size_t buflen
, int *errnop
, int *herrnop
)
354 __libc_lock_lock (lock
);
356 status
= internal_nisplus_gethostent_r (result
, buffer
, buflen
, errnop
,
359 __libc_lock_unlock (lock
);
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
;
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
)
396 *herrnop
= NETDB_INTERNAL
;
397 return NSS_STATUS_NOTFOUND
;
400 char buf
[strlen (name
) + 10 + tablename_len
];
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
);
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. */
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
,
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
);
437 *herrnop
= NETDB_INTERNAL
;
438 return NSS_STATUS_TRYAGAIN
;
441 int retval
= niserr2nss (result
->status
);
442 if (__glibc_unlikely (retval
!= NSS_STATUS_SUCCESS
))
444 if (retval
== NSS_STATUS_TRYAGAIN
)
447 *herrnop
= TRY_AGAIN
;
451 __set_errno (olderr
);
452 *herrnop
= NETDB_INTERNAL
;
454 nis_freeresult (result
);
458 int parse_res
= _nss_nisplus_parse_hostent (result
, af
, host
, buffer
,
459 buflen
, errnop
, flags
);
461 nis_freeresult (result
);
464 return NSS_STATUS_SUCCESS
;
466 *herrnop
= NETDB_INTERNAL
;
470 return NSS_STATUS_TRYAGAIN
;
473 __set_errno (olderr
);
474 return NSS_STATUS_NOTFOUND
;
479 _nss_nisplus_gethostbyname2_r (const char *name
, int af
, struct hostent
*host
,
480 char *buffer
, size_t buflen
, int *errnop
,
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
,
491 ((_res
.options
& RES_USE_INET6
) ? AI_V4MAPPED
: 0));
496 _nss_nisplus_gethostbyname_r (const char *name
, struct hostent
*host
,
497 char *buffer
, size_t buflen
, int *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
,
507 if (status
== NSS_STATUS_SUCCESS
)
511 return internal_gethostbyname2_r (name
, AF_INET
, host
, buffer
,
512 buflen
, errnop
, h_errnop
, 0);
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
)
529 return NSS_STATUS_NOTFOUND
;
531 char buf
[24 + tablename_len
];
532 int retval
, parse_res
;
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
);
541 __set_errno (ENOMEM
);
542 return NSS_STATUS_TRYAGAIN
;
545 retval
= niserr2nss (result
->status
);
546 if (__glibc_unlikely (retval
!= NSS_STATUS_SUCCESS
))
548 if (retval
== NSS_STATUS_TRYAGAIN
)
551 *herrnop
= NETDB_INTERNAL
;
554 __set_errno (olderr
);
555 nis_freeresult (result
);
559 parse_res
= _nss_nisplus_parse_hostent (result
, af
, host
,
560 buffer
, buflen
, errnop
,
561 ((_res
.options
& RES_USE_INET6
)
563 nis_freeresult (result
);
566 return NSS_STATUS_SUCCESS
;
568 *herrnop
= NETDB_INTERNAL
;
572 return NSS_STATUS_TRYAGAIN
;
575 __set_errno (olderr
);
576 return NSS_STATUS_NOTFOUND
;
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
)
587 enum nss_status status
= internal_gethostbyname2_r (name
, AF_UNSPEC
, &host
,
590 if (__glibc_likely (status
== NSS_STATUS_SUCCESS
))
594 uintptr_t pad
= (-(uintptr_t) buffer
595 % __alignof__ (struct gaih_addrtuple
));
597 buflen
= buflen
> pad
? buflen
- pad
: 0;
599 if (__glibc_unlikely (buflen
< sizeof (struct gaih_addrtuple
)))
603 *herrnop
= NETDB_INTERNAL
;
604 return NSS_STATUS_TRYAGAIN
;
609 (*pat
)->name
= host
.h_name
;
610 (*pat
)->family
= host
.h_addrtype
;
612 memcpy ((*pat
)->addr
, host
.h_addr_list
[0], host
.h_length
);
614 assert (host
.h_addr_list
[1] == NULL
);