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.
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 <http://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
35static nis_result *result;
36static nis_name tablename_val;
37static 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/* Get implementation for some internal functions. */
46#include <resolv/mapv4v6addr.h>
47
48
49static int
50_nss_nisplus_parse_hostent (nis_result *result, int af, struct hostent *host,
51 char *buffer, size_t buflen, int *errnop,
52 int flags)
53{
54 unsigned int i;
55 char *first_unused = buffer;
56 size_t room_left = buflen;
57
58 if (result == NULL)
59 return 0;
60
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)
65 return 0;
66
67 char *data = first_unused;
68
69 if (room_left < (af != AF_INET || (flags & AI_V4MAPPED) != 0
70 ? IN6ADDRSZ : INADDRSZ))
71 {
72 no_more_room:
73 *errnop = ERANGE;
74 return -1;
75 }
76
77 /* Parse address. */
78 if (af != AF_INET6
79 && inet_pton (AF_INET, NISENTRYVAL (0, 2, result), data) > 0)
80 {
81 assert ((flags & AI_V4MAPPED) == 0 || af != AF_UNSPEC);
82 if (flags & AI_V4MAPPED)
83 {
84 map_v4v6_address (data, data);
85 host->h_addrtype = AF_INET6;
86 host->h_length = IN6ADDRSZ;
87 }
88 else
89 {
90 host->h_addrtype = AF_INET;
91 host->h_length = INADDRSZ;
92 }
93 }
94 else if (af != AF_INET
95 && inet_pton (AF_INET6, NISENTRYVAL (0, 2, result), data) > 0)
96 {
97 host->h_addrtype = AF_INET6;
98 host->h_length = IN6ADDRSZ;
99 }
100 else
101 /* Illegal address: ignore line. */
102 return 0;
103
104 first_unused += host->h_length;
105 room_left -= host->h_length;
106
107 if (NISENTRYLEN (0, 0, result) + 1 > room_left)
108 goto no_more_room;
109
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';
114
115 room_left -= NISENTRYLEN (0, 0, result) + 1;
116 char *line = first_unused;
117
118 /* When this is a call to gethostbyname4_r we do not need the aliases. */
119 if (af != AF_UNSPEC)
120 {
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)
125 {
126 if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0)
127 {
128 if (NISENTRYLEN (i, 1, result) + 2 > room_left)
129 goto no_more_room;
130
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;
137 }
138 }
139 *first_unused++ = '\0';
140 }
141
142 /* Adjust the pointer so it is aligned for
143 storing pointers. */
144 size_t adjust = ((__alignof__ (char *)
145 - (first_unused - (char *) 0) % __alignof__ (char *))
146 % __alignof__ (char *));
147 if (room_left < adjust + 3 * sizeof (char *))
148 goto no_more_room;
149 first_unused += adjust;
150 room_left -= adjust;
151 host->h_addr_list = (char **) first_unused;
152
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];
157
158 /* When this is a call to gethostbyname4_r we do not need the aliases. */
159 if (af != AF_UNSPEC)
160 {
161 i = 0;
162 while (*line != '\0')
163 {
164 /* Skip leading blanks. */
165 while (isspace (*line))
166 ++line;
167
168 if (*line == '\0')
169 break;
170
171 if (room_left < sizeof (char *))
172 goto no_more_room;
173
174 room_left -= sizeof (char *);
175 host->h_aliases[i++] = line;
176
177 while (*line != '\0' && *line != ' ')
178 ++line;
179
180 if (*line == ' ')
181 *line++ = '\0';
182 }
183
184 host->h_aliases[i] = NULL;
185 }
186
187 return 1;
188}
189
190
191static enum nss_status
192_nss_create_tablename (int *errnop)
193{
194 if (tablename_val == NULL)
195 {
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.";
199
200 char *p = malloc (sizeof (prefix) + local_dir_len);
201 if (p == NULL)
202 {
203 *errnop = errno;
204 return NSS_STATUS_TRYAGAIN;
205 }
206
207 memcpy (__stpcpy (p, prefix), local_dir, local_dir_len + 1);
208
209 tablename_len = sizeof (prefix) - 1 + local_dir_len;
210
211 atomic_write_barrier ();
212
213 tablename_val = p;
214 }
215
216 return NSS_STATUS_SUCCESS;
217}
218
219
220enum nss_status
221_nss_nisplus_sethostent (int stayopen)
222{
223 enum nss_status status = NSS_STATUS_SUCCESS;
224 int err;
225
226 __libc_lock_lock (lock);
227
228 if (result != NULL)
229 {
230 nis_freeresult (result);
231 result = NULL;
232 }
233
234 if (tablename_val == NULL)
235 status = _nss_create_tablename (&err);
236
237 __libc_lock_unlock (lock);
238
239 return status;
240}
241
242
243enum nss_status
244_nss_nisplus_endhostent (void)
245{
246 __libc_lock_lock (lock);
247
248 if (result != NULL)
249 {
250 nis_freeresult (result);
251 result = NULL;
252 }
253
254 __libc_lock_unlock (lock);
255
256 return NSS_STATUS_SUCCESS;
257}
258
259
260static enum nss_status
261internal_nisplus_gethostent_r (struct hostent *host, char *buffer,
262 size_t buflen, int *errnop, int *herrnop)
263{
264 int parse_res;
265
266 /* Get the next entry until we found a correct one. */
267 do
268 {
269 nis_result *saved_res;
270
271 if (result == NULL)
272 {
273 saved_res = NULL;
274 if (tablename_val == NULL)
275 {
276 enum nss_status status = _nss_create_tablename (errnop);
277
278 if (status != NSS_STATUS_SUCCESS)
279 return status;
280 }
281
282 result = nis_first_entry (tablename_val);
283 if (result == NULL)
284 {
285 *errnop = errno;
286 return NSS_STATUS_TRYAGAIN;
287 }
288 if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
289 {
290 enum nss_status retval = niserr2nss (result->status);
291 if (retval == NSS_STATUS_TRYAGAIN)
292 {
293 *herrnop = NETDB_INTERNAL;
294 *errnop = errno;
295 }
296 return retval;
297 }
298
299 }
300 else
301 {
302 saved_res = result;
303 result = nis_next_entry (tablename_val, &result->cookie);
304 if (result == NULL)
305 {
306 *errnop = errno;
307 return NSS_STATUS_TRYAGAIN;
308 }
309 if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
310 {
311 enum nss_status retval= niserr2nss (result->status);
312
313 nis_freeresult (result);
314 result = saved_res;
315 if (retval == NSS_STATUS_TRYAGAIN)
316 {
317 *herrnop = NETDB_INTERNAL;
318 *errnop = errno;
319 }
320 return retval;
321 }
322 }
323
324 if (_res.options & RES_USE_INET6)
325 parse_res = _nss_nisplus_parse_hostent (result, AF_INET6, host, buffer,
326 buflen, errnop, AI_V4MAPPED);
327 else
328 parse_res = _nss_nisplus_parse_hostent (result, AF_INET, host, buffer,
329 buflen, errnop, 0);
330
331 if (parse_res == -1)
332 {
333 nis_freeresult (result);
334 result = saved_res;
335 *herrnop = NETDB_INTERNAL;
336 *errnop = ERANGE;
337 return NSS_STATUS_TRYAGAIN;
338 }
339 if (saved_res != NULL)
340 nis_freeresult (saved_res);
341
342 } while (!parse_res);
343
344 return NSS_STATUS_SUCCESS;
345}
346
347
348enum nss_status
349_nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
350 size_t buflen, int *errnop, int *herrnop)
351{
352 int status;
353
354 __libc_lock_lock (lock);
355
356 status = internal_nisplus_gethostent_r (result, buffer, buflen, errnop,
357 herrnop);
358
359 __libc_lock_unlock (lock);
360
361 return status;
362}
363
364
365static enum nss_status
366get_tablename (int *herrnop)
367{
368 __libc_lock_lock (lock);
369
370 enum nss_status status = _nss_create_tablename (herrnop);
371
372 __libc_lock_unlock (lock);
373
374 if (status != NSS_STATUS_SUCCESS)
375 *herrnop = NETDB_INTERNAL;
376
377 return status;
378}
379
380
381static enum nss_status
382internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
383 char *buffer, size_t buflen, int *errnop,
384 int *herrnop, int flags)
385{
386 if (tablename_val == NULL)
387 {
388 enum nss_status status = get_tablename (herrnop);
389 if (status != NSS_STATUS_SUCCESS)
390 return status;
391 }
392
393 if (name == NULL)
394 {
395 *errnop = EINVAL;
396 *herrnop = NETDB_INTERNAL;
397 return NSS_STATUS_NOTFOUND;
398 }
399
400 char buf[strlen (name) + 10 + tablename_len];
401 int olderr = errno;
402
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);
407
408 if (result != NULL)
409 {
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. */
412 char *bufptr = buf;
413 size_t buflen = sizeof (buf);
414
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,
418 "hosts_tbl") == 0
419 && result->objects.objects_val->EN_data.en_cols.en_cols_len >= 3)
420 {
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);
426 }
427
428 snprintf (bufptr, buflen, "[cname=%s],%s", name, tablename_val);
429
430 nis_freeresult (result);
431 result = nis_list (bufptr, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
432 }
433
434 if (result == NULL)
435 {
436 *errnop = ENOMEM;
437 *herrnop = NETDB_INTERNAL;
438 return NSS_STATUS_TRYAGAIN;
439 }
440
441 int retval = niserr2nss (result->status);
442 if (__glibc_unlikely (retval != NSS_STATUS_SUCCESS))
443 {
444 if (retval == NSS_STATUS_TRYAGAIN)
445 {
446 *errnop = errno;
447 *herrnop = TRY_AGAIN;
448 }
449 else
450 {
451 __set_errno (olderr);
452 *herrnop = NETDB_INTERNAL;
453 }
454 nis_freeresult (result);
455 return retval;
456 }
457
458 int parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer,
459 buflen, errnop, flags);
460
461 nis_freeresult (result);
462
463 if (parse_res > 0)
464 return NSS_STATUS_SUCCESS;
465
466 *herrnop = NETDB_INTERNAL;
467 if (parse_res == -1)
468 {
469 *errnop = ERANGE;
470 return NSS_STATUS_TRYAGAIN;
471 }
472
473 __set_errno (olderr);
474 return NSS_STATUS_NOTFOUND;
475}
476
477
478enum nss_status
479_nss_nisplus_gethostbyname2_r (const char *name, int af, struct hostent *host,
480 char *buffer, size_t buflen, int *errnop,
481 int *herrnop)
482{
483 if (af != AF_INET && af != AF_INET6)
484 {
485 *herrnop = HOST_NOT_FOUND;
486 return NSS_STATUS_NOTFOUND;
487 }
488
489 return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
490 herrnop,
491 ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
492}
493
494
495enum nss_status
496_nss_nisplus_gethostbyname_r (const char *name, struct hostent *host,
497 char *buffer, size_t buflen, int *errnop,
498 int *h_errnop)
499{
500 if (_res.options & RES_USE_INET6)
501 {
502 enum nss_status status;
503
504 status = internal_gethostbyname2_r (name, AF_INET6, host, buffer,
505 buflen, errnop, h_errnop,
506 AI_V4MAPPED);
507 if (status == NSS_STATUS_SUCCESS)
508 return status;
509 }
510
511 return internal_gethostbyname2_r (name, AF_INET, host, buffer,
512 buflen, errnop, h_errnop, 0);
513}
514
515
516enum nss_status
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)
520{
521 if (tablename_val == NULL)
522 {
523 enum nss_status status = get_tablename (herrnop);
524 if (status != NSS_STATUS_SUCCESS)
525 return status;
526 }
527
528 if (addr == NULL)
529 return NSS_STATUS_NOTFOUND;
530
531 char buf[24 + tablename_len];
532 int retval, parse_res;
533 int olderr = errno;
534
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);
538
539 if (result == NULL)
540 {
541 __set_errno (ENOMEM);
542 return NSS_STATUS_TRYAGAIN;
543 }
544
545 retval = niserr2nss (result->status);
546 if (__glibc_unlikely (retval != NSS_STATUS_SUCCESS))
547 {
548 if (retval == NSS_STATUS_TRYAGAIN)
549 {
550 *errnop = errno;
551 *herrnop = NETDB_INTERNAL;
552 }
553 else
554 __set_errno (olderr);
555 nis_freeresult (result);
556 return retval;
557 }
558
559 parse_res = _nss_nisplus_parse_hostent (result, af, host,
560 buffer, buflen, errnop,
561 ((_res.options & RES_USE_INET6)
562 ? AI_V4MAPPED : 0));
563 nis_freeresult (result);
564
565 if (parse_res > 0)
566 return NSS_STATUS_SUCCESS;
567
568 *herrnop = NETDB_INTERNAL;
569 if (parse_res == -1)
570 {
571 *errnop = ERANGE;
572 return NSS_STATUS_TRYAGAIN;
573 }
574
575 __set_errno (olderr);
576 return NSS_STATUS_NOTFOUND;
577}
578
579
580enum nss_status
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)
584{
585 struct hostent host;
586
587 enum nss_status status = internal_gethostbyname2_r (name, AF_UNSPEC, &host,
588 buffer, buflen,
589 errnop, herrnop, 0);
590 if (__glibc_likely (status == NSS_STATUS_SUCCESS))
591 {
592 if (*pat == NULL)
593 {
594 uintptr_t pad = (-(uintptr_t) buffer
595 % __alignof__ (struct gaih_addrtuple));
596 buffer += pad;
597 buflen = buflen > pad ? buflen - pad : 0;
598
599 if (__glibc_unlikely (buflen < sizeof (struct gaih_addrtuple)))
600 {
601 free (result);
602 *errnop = ERANGE;
603 *herrnop = NETDB_INTERNAL;
604 return NSS_STATUS_TRYAGAIN;
605 }
606 }
607
608 (*pat)->next = NULL;
609 (*pat)->name = host.h_name;
610 (*pat)->family = host.h_addrtype;
611
612 memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
613 (*pat)->scopeid = 0;
614 assert (host.h_addr_list[1] == NULL);
615 }
616
617 return status;
618}
619