]> git.ipfire.org Git - thirdparty/glibc.git/blob - nis/nss_nisplus/nisplus-hosts.c
Prefer https to http for gnu.org and fsf.org URLs
[thirdparty/glibc.git] / nis / nss_nisplus / nisplus-hosts.c
1 /* Copyright (C) 1997-2019 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997.
4
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.
9
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.
14
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/>. */
18
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>
30
31 #include "nss-nisplus.h"
32
33 __libc_lock_define_initialized (static, lock)
34
35 static nis_result *result;
36 static nis_name tablename_val;
37 static u_long tablename_len;
38
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)
41
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)
44
45 static int
46 _nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
47 char *buffer, size_t buflen, int *errnop)
48 {
49 unsigned int i;
50 char *first_unused = buffer;
51 size_t room_left = buflen;
52
53 if (result == NULL)
54 return 0;
55
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;
61
62 char *data = first_unused;
63
64 if (room_left < (af != AF_INET ? IN6ADDRSZ : INADDRSZ))
65 {
66 no_more_room:
67 *errnop = ERANGE;
68 return -1;
69 }
70
71 /* Parse address. */
72 if (af != AF_INET6
73 && inet_pton (AF_INET, NISENTRYVAL (0, 2, result), data) > 0)
74 {
75 host->h_addrtype = AF_INET;
76 host->h_length = INADDRSZ;
77 }
78 else if (af != AF_INET
79 && inet_pton (AF_INET6, NISENTRYVAL (0, 2, result), data) > 0)
80 {
81 host->h_addrtype = AF_INET6;
82 host->h_length = IN6ADDRSZ;
83 }
84 else
85 /* Illegal address: ignore line. */
86 return 0;
87
88 first_unused += host->h_length;
89 room_left -= host->h_length;
90
91 if (NISENTRYLEN (0, 0, result) + 1 > room_left)
92 goto no_more_room;
93
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';
98
99 room_left -= NISENTRYLEN (0, 0, result) + 1;
100 char *line = first_unused;
101
102 /* When this is a call to gethostbyname4_r we do not need the aliases. */
103 if (af != AF_UNSPEC)
104 {
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)
109 {
110 if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0)
111 {
112 if (NISENTRYLEN (i, 1, result) + 2 > room_left)
113 goto no_more_room;
114
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;
121 }
122 }
123 *first_unused++ = '\0';
124 }
125
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;
136
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];
141
142 /* When this is a call to gethostbyname4_r we do not need the aliases. */
143 if (af != AF_UNSPEC)
144 {
145 i = 0;
146 while (*line != '\0')
147 {
148 /* Skip leading blanks. */
149 while (isspace (*line))
150 ++line;
151
152 if (*line == '\0')
153 break;
154
155 if (room_left < sizeof (char *))
156 goto no_more_room;
157
158 room_left -= sizeof (char *);
159 host->h_aliases[i++] = line;
160
161 while (*line != '\0' && *line != ' ')
162 ++line;
163
164 if (*line == ' ')
165 *line++ = '\0';
166 }
167
168 host->h_aliases[i] = NULL;
169 }
170
171 return 1;
172 }
173
174
175 static enum nss_status
176 _nss_create_tablename (int *errnop)
177 {
178 if (tablename_val == NULL)
179 {
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.";
183
184 char *p = malloc (sizeof (prefix) + local_dir_len);
185 if (p == NULL)
186 {
187 *errnop = errno;
188 return NSS_STATUS_TRYAGAIN;
189 }
190
191 memcpy (__stpcpy (p, prefix), local_dir, local_dir_len + 1);
192
193 tablename_len = sizeof (prefix) - 1 + local_dir_len;
194
195 atomic_write_barrier ();
196
197 tablename_val = p;
198 }
199
200 return NSS_STATUS_SUCCESS;
201 }
202
203
204 enum nss_status
205 _nss_nisplus_sethostent (int stayopen)
206 {
207 enum nss_status status = NSS_STATUS_SUCCESS;
208 int err;
209
210 __libc_lock_lock (lock);
211
212 if (result != NULL)
213 {
214 nis_freeresult (result);
215 result = NULL;
216 }
217
218 if (tablename_val == NULL)
219 status = _nss_create_tablename (&err);
220
221 __libc_lock_unlock (lock);
222
223 return status;
224 }
225
226
227 enum nss_status
228 _nss_nisplus_endhostent (void)
229 {
230 __libc_lock_lock (lock);
231
232 if (result != NULL)
233 {
234 nis_freeresult (result);
235 result = NULL;
236 }
237
238 __libc_lock_unlock (lock);
239
240 return NSS_STATUS_SUCCESS;
241 }
242
243
244 static enum nss_status
245 internal_nisplus_gethostent_r (struct hostent *host, char *buffer,
246 size_t buflen, int *errnop, int *herrnop)
247 {
248 int parse_res;
249
250 /* Get the next entry until we found a correct one. */
251 do
252 {
253 nis_result *saved_res;
254
255 if (result == NULL)
256 {
257 saved_res = NULL;
258 if (tablename_val == NULL)
259 {
260 enum nss_status status = _nss_create_tablename (errnop);
261
262 if (status != NSS_STATUS_SUCCESS)
263 return status;
264 }
265
266 result = nis_first_entry (tablename_val);
267 if (result == NULL)
268 {
269 *errnop = errno;
270 return NSS_STATUS_TRYAGAIN;
271 }
272 if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
273 {
274 enum nss_status retval = niserr2nss (result->status);
275 if (retval == NSS_STATUS_TRYAGAIN)
276 {
277 *herrnop = NETDB_INTERNAL;
278 *errnop = errno;
279 }
280 return retval;
281 }
282
283 }
284 else
285 {
286 saved_res = result;
287 result = nis_next_entry (tablename_val, &result->cookie);
288 if (result == NULL)
289 {
290 *errnop = errno;
291 return NSS_STATUS_TRYAGAIN;
292 }
293 if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
294 {
295 enum nss_status retval= niserr2nss (result->status);
296
297 nis_freeresult (result);
298 result = saved_res;
299 if (retval == NSS_STATUS_TRYAGAIN)
300 {
301 *herrnop = NETDB_INTERNAL;
302 *errnop = errno;
303 }
304 return retval;
305 }
306 }
307
308 parse_res = _nss_nisplus_parse_hostent (result, AF_INET, host, buffer,
309 buflen, errnop);
310
311 if (parse_res == -1)
312 {
313 nis_freeresult (result);
314 result = saved_res;
315 *herrnop = NETDB_INTERNAL;
316 *errnop = ERANGE;
317 return NSS_STATUS_TRYAGAIN;
318 }
319 if (saved_res != NULL)
320 nis_freeresult (saved_res);
321
322 } while (!parse_res);
323
324 return NSS_STATUS_SUCCESS;
325 }
326
327
328 enum nss_status
329 _nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
330 size_t buflen, int *errnop, int *herrnop)
331 {
332 int status;
333
334 __libc_lock_lock (lock);
335
336 status = internal_nisplus_gethostent_r (result, buffer, buflen, errnop,
337 herrnop);
338
339 __libc_lock_unlock (lock);
340
341 return status;
342 }
343
344
345 static enum nss_status
346 get_tablename (int *herrnop)
347 {
348 __libc_lock_lock (lock);
349
350 enum nss_status status = _nss_create_tablename (herrnop);
351
352 __libc_lock_unlock (lock);
353
354 if (status != NSS_STATUS_SUCCESS)
355 *herrnop = NETDB_INTERNAL;
356
357 return status;
358 }
359
360
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)
365 {
366 if (tablename_val == NULL)
367 {
368 enum nss_status status = get_tablename (herrnop);
369 if (status != NSS_STATUS_SUCCESS)
370 return status;
371 }
372
373 if (name == NULL)
374 {
375 *errnop = EINVAL;
376 *herrnop = NETDB_INTERNAL;
377 return NSS_STATUS_NOTFOUND;
378 }
379
380 char buf[strlen (name) + 10 + tablename_len];
381 int olderr = errno;
382
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);
387
388 if (result != NULL)
389 {
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);
394
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)
400 {
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);
406 }
407
408 snprintf (bufptr, buflen, "[cname=%s],%s", name, tablename_val);
409
410 nis_freeresult (result);
411 result = nis_list (bufptr, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
412 }
413
414 if (result == NULL)
415 {
416 *errnop = ENOMEM;
417 *herrnop = NETDB_INTERNAL;
418 return NSS_STATUS_TRYAGAIN;
419 }
420
421 int retval = niserr2nss (result->status);
422 if (__glibc_unlikely (retval != NSS_STATUS_SUCCESS))
423 {
424 if (retval == NSS_STATUS_TRYAGAIN)
425 {
426 *errnop = errno;
427 *herrnop = TRY_AGAIN;
428 }
429 else
430 {
431 __set_errno (olderr);
432 *herrnop = NETDB_INTERNAL;
433 }
434 nis_freeresult (result);
435 return retval;
436 }
437
438 int parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer,
439 buflen, errnop);
440
441 nis_freeresult (result);
442
443 if (parse_res > 0)
444 return NSS_STATUS_SUCCESS;
445
446 *herrnop = NETDB_INTERNAL;
447 if (parse_res == -1)
448 {
449 *errnop = ERANGE;
450 return NSS_STATUS_TRYAGAIN;
451 }
452
453 __set_errno (olderr);
454 return NSS_STATUS_NOTFOUND;
455 }
456
457
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)
462 {
463 if (af != AF_INET && af != AF_INET6)
464 {
465 *herrnop = HOST_NOT_FOUND;
466 return NSS_STATUS_NOTFOUND;
467 }
468
469 return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
470 herrnop);
471 }
472
473
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)
478 {
479 return internal_gethostbyname2_r (name, AF_INET, host, buffer,
480 buflen, errnop, h_errnop);
481 }
482
483
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)
488 {
489 if (tablename_val == NULL)
490 {
491 enum nss_status status = get_tablename (herrnop);
492 if (status != NSS_STATUS_SUCCESS)
493 return status;
494 }
495
496 if (addr == NULL)
497 return NSS_STATUS_NOTFOUND;
498
499 char buf[24 + tablename_len];
500 int retval, parse_res;
501 int olderr = errno;
502
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);
506
507 if (result == NULL)
508 {
509 __set_errno (ENOMEM);
510 return NSS_STATUS_TRYAGAIN;
511 }
512
513 retval = niserr2nss (result->status);
514 if (__glibc_unlikely (retval != NSS_STATUS_SUCCESS))
515 {
516 if (retval == NSS_STATUS_TRYAGAIN)
517 {
518 *errnop = errno;
519 *herrnop = NETDB_INTERNAL;
520 }
521 else
522 __set_errno (olderr);
523 nis_freeresult (result);
524 return retval;
525 }
526
527 parse_res = _nss_nisplus_parse_hostent (result, af, host,
528 buffer, buflen, errnop);
529 nis_freeresult (result);
530
531 if (parse_res > 0)
532 return NSS_STATUS_SUCCESS;
533
534 *herrnop = NETDB_INTERNAL;
535 if (parse_res == -1)
536 {
537 *errnop = ERANGE;
538 return NSS_STATUS_TRYAGAIN;
539 }
540
541 __set_errno (olderr);
542 return NSS_STATUS_NOTFOUND;
543 }
544
545
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)
550 {
551 struct hostent host;
552
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))
557 {
558 if (*pat == NULL)
559 {
560 uintptr_t pad = (-(uintptr_t) buffer
561 % __alignof__ (struct gaih_addrtuple));
562 buffer += pad;
563 buflen = buflen > pad ? buflen - pad : 0;
564
565 if (__glibc_unlikely (buflen < sizeof (struct gaih_addrtuple)))
566 {
567 free (result);
568 *errnop = ERANGE;
569 *herrnop = NETDB_INTERNAL;
570 return NSS_STATUS_TRYAGAIN;
571 }
572 }
573
574 (*pat)->next = NULL;
575 (*pat)->name = host.h_name;
576 (*pat)->family = host.h_addrtype;
577
578 memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
579 (*pat)->scopeid = 0;
580 assert (host.h_addr_list[1] == NULL);
581 }
582
583 return status;
584 }