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