Bug 25819: Update to Unicode 13.0.0
[glibc.git] / nis / nss_nisplus / nisplus-hosts.c
blob1e66e4ff923755a5bc1d4292204e98c8e5dde4c2
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/>. */
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 <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 static int
46 _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
47 char *buffer, size_t buflen, int *errnop)
49 unsigned int i;
50 char *first_unused = buffer;
51 size_t room_left = buflen;
53 if (result == NULL)
54 return 0;
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)
60 return 0;
62 char *data = first_unused;
64 if (room_left < (af != AF_INET ? IN6ADDRSZ : INADDRSZ))
66 no_more_room:
67 *errnop = ERANGE;
68 return -1;
71 /* Parse address. */
72 if (af != AF_INET6
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;
84 else
85 /* Illegal address: ignore line. */
86 return 0;
88 first_unused += host->h_length;
89 room_left -= host->h_length;
91 if (NISENTRYLEN (0, 0, result) + 1 > room_left)
92 goto no_more_room;
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. */
103 if (af != AF_UNSPEC)
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)
113 goto no_more_room;
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
127 storing pointers. */
128 size_t adjust = ((__alignof__ (char *)
129 - (first_unused - (char *) 0) % __alignof__ (char *))
130 % __alignof__ (char *));
131 if (room_left < adjust + 3 * sizeof (char *))
132 goto no_more_room;
133 first_unused += adjust;
134 room_left -= 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. */
143 if (af != AF_UNSPEC)
145 i = 0;
146 while (*line != '\0')
148 /* Skip leading blanks. */
149 while (isspace (*line))
150 ++line;
152 if (*line == '\0')
153 break;
155 if (room_left < sizeof (char *))
156 goto no_more_room;
158 room_left -= sizeof (char *);
159 host->h_aliases[i++] = line;
161 while (*line != '\0' && *line != ' ')
162 ++line;
164 if (*line == ' ')
165 *line++ = '\0';
168 host->h_aliases[i] = NULL;
171 return 1;
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);
185 if (p == NULL)
187 *errnop = errno;
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 ();
197 tablename_val = p;
200 return NSS_STATUS_SUCCESS;
204 enum nss_status
205 _nss_nisplus_sethostent (int stayopen)
207 enum nss_status status = NSS_STATUS_SUCCESS;
208 int err;
210 __libc_lock_lock (lock);
212 if (result != NULL)
214 nis_freeresult (result);
215 result = NULL;
218 if (tablename_val == NULL)
219 status = _nss_create_tablename (&err);
221 __libc_lock_unlock (lock);
223 return status;
227 enum nss_status
228 _nss_nisplus_endhostent (void)
230 __libc_lock_lock (lock);
232 if (result != NULL)
234 nis_freeresult (result);
235 result = NULL;
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)
248 int parse_res;
250 /* Get the next entry until we found a correct one. */
253 nis_result *saved_res;
255 if (result == NULL)
257 saved_res = NULL;
258 if (tablename_val == NULL)
260 enum nss_status status = _nss_create_tablename (errnop);
262 if (status != NSS_STATUS_SUCCESS)
263 return status;
266 result = nis_first_entry (tablename_val);
267 if (result == NULL)
269 *errnop = errno;
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;
278 *errnop = errno;
280 return retval;
284 else
286 saved_res = result;
287 result = nis_next_entry (tablename_val, &result->cookie);
288 if (result == NULL)
290 *errnop = errno;
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);
298 result = saved_res;
299 if (retval == NSS_STATUS_TRYAGAIN)
301 *herrnop = NETDB_INTERNAL;
302 *errnop = errno;
304 return retval;
308 parse_res = _nss_nisplus_parse_hostent (result, AF_INET, host, buffer,
309 buflen, errnop);
311 if (parse_res == -1)
313 nis_freeresult (result);
314 result = saved_res;
315 *herrnop = NETDB_INTERNAL;
316 *errnop = ERANGE;
317 return NSS_STATUS_TRYAGAIN;
319 if (saved_res != NULL)
320 nis_freeresult (saved_res);
322 } while (!parse_res);
324 return NSS_STATUS_SUCCESS;
328 enum nss_status
329 _nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
330 size_t buflen, int *errnop, int *herrnop)
332 int status;
334 __libc_lock_lock (lock);
336 status = internal_nisplus_gethostent_r (result, buffer, buflen, errnop,
337 herrnop);
339 __libc_lock_unlock (lock);
341 return status;
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;
357 return status;
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,
364 int *herrnop)
366 if (tablename_val == NULL)
368 enum nss_status status = get_tablename (herrnop);
369 if (status != NSS_STATUS_SUCCESS)
370 return status;
373 if (name == NULL)
375 *errnop = EINVAL;
376 *herrnop = NETDB_INTERNAL;
377 return NSS_STATUS_NOTFOUND;
380 char buf[strlen (name) + 10 + tablename_len];
381 int olderr = errno;
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);
388 if (result != 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. */
392 char *bufptr = buf;
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,
398 "hosts_tbl") == 0
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);
414 if (result == NULL)
416 *errnop = ENOMEM;
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)
426 *errnop = errno;
427 *herrnop = TRY_AGAIN;
429 else
431 __set_errno (olderr);
432 *herrnop = NETDB_INTERNAL;
434 nis_freeresult (result);
435 return retval;
438 int parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer,
439 buflen, errnop);
441 nis_freeresult (result);
443 if (parse_res > 0)
444 return NSS_STATUS_SUCCESS;
446 *herrnop = NETDB_INTERNAL;
447 if (parse_res == -1)
449 *errnop = ERANGE;
450 return NSS_STATUS_TRYAGAIN;
453 __set_errno (olderr);
454 return NSS_STATUS_NOTFOUND;
458 enum nss_status
459 _nss_nisplus_gethostbyname2_r (const char *name, int af, struct hostent *host,
460 char *buffer, size_t buflen, int *errnop,
461 int *herrnop)
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,
470 herrnop);
474 enum nss_status
475 _nss_nisplus_gethostbyname_r (const char *name, struct hostent *host,
476 char *buffer, size_t buflen, int *errnop,
477 int *h_errnop)
479 return internal_gethostbyname2_r (name, AF_INET, host, buffer,
480 buflen, errnop, h_errnop);
484 enum nss_status
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)
493 return status;
496 if (addr == NULL)
497 return NSS_STATUS_NOTFOUND;
499 char buf[24 + tablename_len];
500 int retval, parse_res;
501 int olderr = errno;
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);
507 if (result == 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)
518 *errnop = errno;
519 *herrnop = NETDB_INTERNAL;
521 else
522 __set_errno (olderr);
523 nis_freeresult (result);
524 return retval;
527 parse_res = _nss_nisplus_parse_hostent (result, af, host,
528 buffer, buflen, errnop);
529 nis_freeresult (result);
531 if (parse_res > 0)
532 return NSS_STATUS_SUCCESS;
534 *herrnop = NETDB_INTERNAL;
535 if (parse_res == -1)
537 *errnop = ERANGE;
538 return NSS_STATUS_TRYAGAIN;
541 __set_errno (olderr);
542 return NSS_STATUS_NOTFOUND;
546 enum nss_status
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)
551 struct hostent host;
553 enum nss_status status = internal_gethostbyname2_r (name, AF_UNSPEC, &host,
554 buffer, buflen,
555 errnop, herrnop);
556 if (__glibc_likely (status == NSS_STATUS_SUCCESS))
558 if (*pat == NULL)
560 uintptr_t pad = (-(uintptr_t) buffer
561 % __alignof__ (struct gaih_addrtuple));
562 buffer += pad;
563 buflen = buflen > pad ? buflen - pad : 0;
565 if (__glibc_unlikely (buflen < sizeof (struct gaih_addrtuple)))
567 free (result);
568 *errnop = ERANGE;
569 *herrnop = NETDB_INTERNAL;
570 return NSS_STATUS_TRYAGAIN;
574 (*pat)->next = NULL;
575 (*pat)->name = host.h_name;
576 (*pat)->family = host.h_addrtype;
578 memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
579 (*pat)->scopeid = 0;
580 assert (host.h_addr_list[1] == NULL);
583 return status;