1 /* Copyright (C) 1997-2020 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 <https://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)
46 _nss_nisplus_parse_hostent (nis_result
*result
, int af
, struct hostent
*host
,
47 char *buffer
, size_t buflen
, int *errnop
)
50 char *first_unused
= buffer
;
51 size_t room_left
= buflen
;
56 if ((result
->status
!= NIS_SUCCESS
&& result
->status
!= NIS_S_SUCCESS
)
57 || __type_of (NIS_RES_OBJECT (result
)) != NIS_ENTRY_OBJ
58 || strcmp (NIS_RES_OBJECT (result
)[0].EN_data
.en_type
, "hosts_tbl") != 0
59 || NIS_RES_OBJECT (result
)[0].EN_data
.en_cols
.en_cols_len
< 4)
62 char *data
= first_unused
;
64 if (room_left
< (af
!= AF_INET
? IN6ADDRSZ
: INADDRSZ
))
73 && inet_pton (AF_INET
, NISENTRYVAL (0, 2, result
), data
) > 0)
75 host
->h_addrtype
= AF_INET
;
76 host
->h_length
= INADDRSZ
;
78 else if (af
!= AF_INET
79 && inet_pton (AF_INET6
, NISENTRYVAL (0, 2, result
), data
) > 0)
81 host
->h_addrtype
= AF_INET6
;
82 host
->h_length
= IN6ADDRSZ
;
85 /* Illegal address: ignore line. */
88 first_unused
+= host
->h_length
;
89 room_left
-= host
->h_length
;
91 if (NISENTRYLEN (0, 0, result
) + 1 > room_left
)
94 host
->h_name
= first_unused
;
95 first_unused
= __stpncpy (first_unused
, NISENTRYVAL (0, 0, result
),
96 NISENTRYLEN (0, 0, result
));
97 *first_unused
++ = '\0';
99 room_left
-= NISENTRYLEN (0, 0, result
) + 1;
100 char *line
= first_unused
;
102 /* When this is a call to gethostbyname4_r we do not need the aliases. */
105 /* XXX Rewrite at some point to allocate the array first and then
106 copy the strings. It is wasteful to first concatenate the strings
107 to just split them again later. */
108 for (i
= 0; i
< NIS_RES_NUMOBJ (result
); ++i
)
110 if (strcmp (NISENTRYVAL (i
, 1, result
), host
->h_name
) != 0)
112 if (NISENTRYLEN (i
, 1, result
) + 2 > room_left
)
115 *first_unused
++ = ' ';
116 first_unused
= __stpncpy (first_unused
,
117 NISENTRYVAL (i
, 1, result
),
118 NISENTRYLEN (i
, 1, result
));
119 *first_unused
= '\0';
120 room_left
-= NISENTRYLEN (i
, 1, result
) + 1;
123 *first_unused
++ = '\0';
126 /* Adjust the pointer so it is aligned for
128 size_t adjust
= ((__alignof__ (char *)
129 - (first_unused
- (char *) 0) % __alignof__ (char *))
130 % __alignof__ (char *));
131 if (room_left
< adjust
+ 3 * sizeof (char *))
133 first_unused
+= adjust
;
135 host
->h_addr_list
= (char **) first_unused
;
137 room_left
-= 3 * sizeof (char *);
138 host
->h_addr_list
[0] = data
;
139 host
->h_addr_list
[1] = NULL
;
140 host
->h_aliases
= &host
->h_addr_list
[2];
142 /* When this is a call to gethostbyname4_r we do not need the aliases. */
146 while (*line
!= '\0')
148 /* Skip leading blanks. */
149 while (isspace (*line
))
155 if (room_left
< sizeof (char *))
158 room_left
-= sizeof (char *);
159 host
->h_aliases
[i
++] = line
;
161 while (*line
!= '\0' && *line
!= ' ')
168 host
->h_aliases
[i
] = NULL
;
175 static enum nss_status
176 _nss_create_tablename (int *errnop
)
178 if (tablename_val
== NULL
)
180 const char *local_dir
= nis_local_directory ();
181 size_t local_dir_len
= strlen (local_dir
);
182 static const char prefix
[] = "hosts.org_dir.";
184 char *p
= malloc (sizeof (prefix
) + local_dir_len
);
188 return NSS_STATUS_TRYAGAIN
;
191 memcpy (__stpcpy (p
, prefix
), local_dir
, local_dir_len
+ 1);
193 tablename_len
= sizeof (prefix
) - 1 + local_dir_len
;
195 atomic_write_barrier ();
200 return NSS_STATUS_SUCCESS
;
205 _nss_nisplus_sethostent (int stayopen
)
207 enum nss_status status
= NSS_STATUS_SUCCESS
;
210 __libc_lock_lock (lock
);
214 nis_freeresult (result
);
218 if (tablename_val
== NULL
)
219 status
= _nss_create_tablename (&err
);
221 __libc_lock_unlock (lock
);
228 _nss_nisplus_endhostent (void)
230 __libc_lock_lock (lock
);
234 nis_freeresult (result
);
238 __libc_lock_unlock (lock
);
240 return NSS_STATUS_SUCCESS
;
244 static enum nss_status
245 internal_nisplus_gethostent_r (struct hostent
*host
, char *buffer
,
246 size_t buflen
, int *errnop
, int *herrnop
)
250 /* Get the next entry until we found a correct one. */
253 nis_result
*saved_res
;
258 if (tablename_val
== NULL
)
260 enum nss_status status
= _nss_create_tablename (errnop
);
262 if (status
!= NSS_STATUS_SUCCESS
)
266 result
= nis_first_entry (tablename_val
);
270 return NSS_STATUS_TRYAGAIN
;
272 if (niserr2nss (result
->status
) != NSS_STATUS_SUCCESS
)
274 enum nss_status retval
= niserr2nss (result
->status
);
275 if (retval
== NSS_STATUS_TRYAGAIN
)
277 *herrnop
= NETDB_INTERNAL
;
287 result
= nis_next_entry (tablename_val
, &result
->cookie
);
291 return NSS_STATUS_TRYAGAIN
;
293 if (niserr2nss (result
->status
) != NSS_STATUS_SUCCESS
)
295 enum nss_status retval
= niserr2nss (result
->status
);
297 nis_freeresult (result
);
299 if (retval
== NSS_STATUS_TRYAGAIN
)
301 *herrnop
= NETDB_INTERNAL
;
308 parse_res
= _nss_nisplus_parse_hostent (result
, AF_INET
, host
, buffer
,
313 nis_freeresult (result
);
315 *herrnop
= NETDB_INTERNAL
;
317 return NSS_STATUS_TRYAGAIN
;
319 if (saved_res
!= NULL
)
320 nis_freeresult (saved_res
);
322 } while (!parse_res
);
324 return NSS_STATUS_SUCCESS
;
329 _nss_nisplus_gethostent_r (struct hostent
*result
, char *buffer
,
330 size_t buflen
, int *errnop
, int *herrnop
)
334 __libc_lock_lock (lock
);
336 status
= internal_nisplus_gethostent_r (result
, buffer
, buflen
, errnop
,
339 __libc_lock_unlock (lock
);
345 static enum nss_status
346 get_tablename (int *herrnop
)
348 __libc_lock_lock (lock
);
350 enum nss_status status
= _nss_create_tablename (herrnop
);
352 __libc_lock_unlock (lock
);
354 if (status
!= NSS_STATUS_SUCCESS
)
355 *herrnop
= NETDB_INTERNAL
;
361 static enum nss_status
362 internal_gethostbyname2_r (const char *name
, int af
, struct hostent
*host
,
363 char *buffer
, size_t buflen
, int *errnop
,
366 if (tablename_val
== NULL
)
368 enum nss_status status
= get_tablename (herrnop
);
369 if (status
!= NSS_STATUS_SUCCESS
)
376 *herrnop
= NETDB_INTERNAL
;
377 return NSS_STATUS_NOTFOUND
;
380 char buf
[strlen (name
) + 10 + tablename_len
];
383 /* Search at first in the alias list, and use the correct name
384 for the next search. */
385 snprintf (buf
, sizeof (buf
), "[name=%s],%s", name
, tablename_val
);
386 nis_result
*result
= nis_list (buf
, FOLLOW_PATH
| FOLLOW_LINKS
, NULL
, NULL
);
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. */
393 size_t buflen
= sizeof (buf
);
395 if ((result
->status
== NIS_SUCCESS
|| result
->status
== NIS_S_SUCCESS
)
396 && __type_of (result
->objects
.objects_val
) == NIS_ENTRY_OBJ
397 && strcmp (result
->objects
.objects_val
->EN_data
.en_type
,
399 && result
->objects
.objects_val
->EN_data
.en_cols
.en_cols_len
>= 3)
401 /* We need to allocate a new buffer since there is no
402 guarantee the returned alias name has a length limit. */
403 name
= NISENTRYVAL(0, 0, result
);
404 size_t buflen
= strlen (name
) + 10 + tablename_len
;
405 bufptr
= alloca (buflen
);
408 snprintf (bufptr
, buflen
, "[cname=%s],%s", name
, tablename_val
);
410 nis_freeresult (result
);
411 result
= nis_list (bufptr
, FOLLOW_PATH
| FOLLOW_LINKS
, NULL
, NULL
);
417 *herrnop
= NETDB_INTERNAL
;
418 return NSS_STATUS_TRYAGAIN
;
421 int retval
= niserr2nss (result
->status
);
422 if (__glibc_unlikely (retval
!= NSS_STATUS_SUCCESS
))
424 if (retval
== NSS_STATUS_TRYAGAIN
)
427 *herrnop
= TRY_AGAIN
;
431 __set_errno (olderr
);
432 *herrnop
= NETDB_INTERNAL
;
434 nis_freeresult (result
);
438 int parse_res
= _nss_nisplus_parse_hostent (result
, af
, host
, buffer
,
441 nis_freeresult (result
);
444 return NSS_STATUS_SUCCESS
;
446 *herrnop
= NETDB_INTERNAL
;
450 return NSS_STATUS_TRYAGAIN
;
453 __set_errno (olderr
);
454 return NSS_STATUS_NOTFOUND
;
459 _nss_nisplus_gethostbyname2_r (const char *name
, int af
, struct hostent
*host
,
460 char *buffer
, size_t buflen
, int *errnop
,
463 if (af
!= AF_INET
&& af
!= AF_INET6
)
465 *herrnop
= HOST_NOT_FOUND
;
466 return NSS_STATUS_NOTFOUND
;
469 return internal_gethostbyname2_r (name
, af
, host
, buffer
, buflen
, errnop
,
475 _nss_nisplus_gethostbyname_r (const char *name
, struct hostent
*host
,
476 char *buffer
, size_t buflen
, int *errnop
,
479 return internal_gethostbyname2_r (name
, AF_INET
, host
, buffer
,
480 buflen
, errnop
, h_errnop
);
485 _nss_nisplus_gethostbyaddr_r (const void *addr
, socklen_t addrlen
, int af
,
486 struct hostent
*host
, char *buffer
,
487 size_t buflen
, int *errnop
, int *herrnop
)
489 if (tablename_val
== NULL
)
491 enum nss_status status
= get_tablename (herrnop
);
492 if (status
!= NSS_STATUS_SUCCESS
)
497 return NSS_STATUS_NOTFOUND
;
499 char buf
[24 + tablename_len
];
500 int retval
, parse_res
;
503 snprintf (buf
, sizeof (buf
), "[addr=%s],%s",
504 inet_ntoa (*(const struct in_addr
*) addr
), tablename_val
);
505 nis_result
*result
= nis_list (buf
, FOLLOW_PATH
| FOLLOW_LINKS
, NULL
, NULL
);
509 __set_errno (ENOMEM
);
510 return NSS_STATUS_TRYAGAIN
;
513 retval
= niserr2nss (result
->status
);
514 if (__glibc_unlikely (retval
!= NSS_STATUS_SUCCESS
))
516 if (retval
== NSS_STATUS_TRYAGAIN
)
519 *herrnop
= NETDB_INTERNAL
;
522 __set_errno (olderr
);
523 nis_freeresult (result
);
527 parse_res
= _nss_nisplus_parse_hostent (result
, af
, host
,
528 buffer
, buflen
, errnop
);
529 nis_freeresult (result
);
532 return NSS_STATUS_SUCCESS
;
534 *herrnop
= NETDB_INTERNAL
;
538 return NSS_STATUS_TRYAGAIN
;
541 __set_errno (olderr
);
542 return NSS_STATUS_NOTFOUND
;
547 _nss_nisplus_gethostbyname4_r (const char *name
, struct gaih_addrtuple
**pat
,
548 char *buffer
, size_t buflen
, int *errnop
,
549 int *herrnop
, int32_t *ttlp
)
553 enum nss_status status
= internal_gethostbyname2_r (name
, AF_UNSPEC
, &host
,
556 if (__glibc_likely (status
== NSS_STATUS_SUCCESS
))
560 uintptr_t pad
= (-(uintptr_t) buffer
561 % __alignof__ (struct gaih_addrtuple
));
563 buflen
= buflen
> pad
? buflen
- pad
: 0;
565 if (__glibc_unlikely (buflen
< sizeof (struct gaih_addrtuple
)))
569 *herrnop
= NETDB_INTERNAL
;
570 return NSS_STATUS_TRYAGAIN
;
575 (*pat
)->name
= host
.h_name
;
576 (*pat
)->family
= host
.h_addrtype
;
578 memcpy ((*pat
)->addr
, host
.h_addr_list
[0], host
.h_length
);
580 assert (host
.h_addr_list
[1] == NULL
);