]> git.ipfire.org Git - thirdparty/glibc.git/blob - nis/nss_nisplus/nisplus-hosts.c
2.5-18.1
[thirdparty/glibc.git] / nis / nss_nisplus / nisplus-hosts.c
1 /* Copyright (C) 1997-2002, 2003, 2005, 2006 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, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
19
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 <bits/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 /* Get implementation for some internal functions. */
46 #include <resolv/mapv4v6addr.h>
47
48
49 static 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_INET6 || (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_INET && inet_pton (af, NISENTRYVAL (0, 2, result), data) > 0)
79 {
80 if (flags & AI_V4MAPPED)
81 {
82 map_v4v6_address (data, data);
83 host->h_addrtype = AF_INET6;
84 host->h_length = IN6ADDRSZ;
85 }
86 else
87 {
88 host->h_addrtype = AF_INET;
89 host->h_length = INADDRSZ;
90 }
91 }
92 else if (af == AF_INET6
93 && inet_pton (AF_INET6, NISENTRYVAL (0, 2, result), data) > 0)
94 {
95 host->h_addrtype = AF_INET6;
96 host->h_length = IN6ADDRSZ;
97 }
98 else
99 /* Illegal address: ignore line. */
100 return 0;
101
102 first_unused += host->h_length;
103 room_left -= host->h_length;
104
105 if (NISENTRYLEN (0, 0, result) + 1 > room_left)
106 goto no_more_room;
107
108 host->h_name = first_unused;
109 first_unused = __stpncpy (first_unused, NISENTRYVAL (0, 0, result),
110 NISENTRYLEN (0, 0, result));
111 *first_unused++ = '\0';
112 room_left -= NISENTRYLEN (0, 0, result) + 1;
113
114 /* XXX Rewrite at some point to allocate the array first and then
115 copy the strings. It wasteful to first concatenate the strings
116 to just split them again later. */
117 char *line = first_unused;
118 for (i = 0; i < NIS_RES_NUMOBJ (result); ++i)
119 {
120 if (strcmp (NISENTRYVAL (i, 1, result), host->h_name) != 0)
121 {
122 if (NISENTRYLEN (i, 1, result) + 2 > room_left)
123 goto no_more_room;
124
125 *first_unused++ = ' ';
126 first_unused = __stpncpy (first_unused, NISENTRYVAL (i, 1, result),
127 NISENTRYLEN (i, 1, result));
128 *first_unused = '\0';
129 room_left -= NISENTRYLEN (i, 1, result) + 1;
130 }
131 }
132 *first_unused++ = '\0';
133
134 /* Adjust the pointer so it is aligned for
135 storing pointers. */
136 size_t adjust = ((__alignof__ (char *)
137 - (first_unused - (char *) 0) % __alignof__ (char *))
138 % __alignof__ (char *));
139 if (room_left < adjust + 3 * sizeof (char *))
140 goto no_more_room;
141 first_unused += adjust;
142 room_left -= adjust;
143 host->h_addr_list = (char **) first_unused;
144
145 room_left -= 3 * sizeof (char *);
146 host->h_addr_list[0] = data;
147 host->h_addr_list[1] = NULL;
148 host->h_aliases = &host->h_addr_list[2];
149
150 i = 0;
151 while (*line != '\0')
152 {
153 /* Skip leading blanks. */
154 while (isspace (*line))
155 ++line;
156
157 if (*line == '\0')
158 break;
159
160 if (room_left < sizeof (char *))
161 goto no_more_room;
162
163 room_left -= sizeof (char *);
164 host->h_aliases[i++] = line;
165
166 while (*line != '\0' && *line != ' ')
167 ++line;
168
169 if (*line == ' ')
170 *line++ = '\0';
171 }
172
173 host->h_aliases[i] = NULL;
174
175 return 1;
176 }
177
178
179 static enum nss_status
180 _nss_create_tablename (int *errnop)
181 {
182 if (tablename_val == NULL)
183 {
184 const char *local_dir = nis_local_directory ();
185 size_t local_dir_len = strlen (local_dir);
186 static const char prefix[] = "hosts.org_dir.";
187
188 char *p = malloc (sizeof (prefix) + local_dir_len);
189 if (p == NULL)
190 {
191 *errnop = errno;
192 return NSS_STATUS_TRYAGAIN;
193 }
194
195 memcpy (__stpcpy (p, prefix), local_dir, local_dir_len + 1);
196
197 tablename_len = sizeof (prefix) - 1 + local_dir_len;
198
199 atomic_write_barrier ();
200
201 tablename_val = p;
202 }
203
204 return NSS_STATUS_SUCCESS;
205 }
206
207 enum nss_status
208 _nss_nisplus_sethostent (int stayopen)
209 {
210 enum nss_status status = NSS_STATUS_SUCCESS;
211 int err;
212
213 __libc_lock_lock (lock);
214
215 if (result != NULL)
216 {
217 nis_freeresult (result);
218 result = NULL;
219 }
220
221 if (tablename_val == NULL)
222 status = _nss_create_tablename (&err);
223
224 __libc_lock_unlock (lock);
225
226 return status;
227 }
228
229 enum nss_status
230 _nss_nisplus_endhostent (void)
231 {
232 __libc_lock_lock (lock);
233
234 if (result != NULL)
235 {
236 nis_freeresult (result);
237 result = NULL;
238 }
239
240 __libc_lock_unlock (lock);
241
242 return NSS_STATUS_SUCCESS;
243 }
244
245 static enum nss_status
246 internal_nisplus_gethostent_r (struct hostent *host, char *buffer,
247 size_t buflen, int *errnop, int *herrnop)
248 {
249 int parse_res;
250
251 /* Get the next entry until we found a correct one. */
252 do
253 {
254 nis_result *saved_res;
255
256 if (result == NULL)
257 {
258 saved_res = NULL;
259 if (tablename_val == NULL)
260 {
261 enum nss_status status = _nss_create_tablename (errnop);
262
263 if (status != NSS_STATUS_SUCCESS)
264 return status;
265 }
266
267 result = nis_first_entry (tablename_val);
268 if (result == NULL)
269 {
270 *errnop = errno;
271 return NSS_STATUS_TRYAGAIN;
272 }
273 if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
274 {
275 enum nss_status retval = niserr2nss (result->status);
276 if (retval == NSS_STATUS_TRYAGAIN)
277 {
278 *herrnop = NETDB_INTERNAL;
279 *errnop = errno;
280 }
281 return retval;
282 }
283
284 }
285 else
286 {
287 saved_res = result;
288 result = nis_next_entry (tablename_val, &result->cookie);
289 if (result == NULL)
290 {
291 *errnop = errno;
292 return NSS_STATUS_TRYAGAIN;
293 }
294 if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
295 {
296 enum nss_status retval= niserr2nss (result->status);
297
298 nis_freeresult (result);
299 result = saved_res;
300 if (retval == NSS_STATUS_TRYAGAIN)
301 {
302 *herrnop = NETDB_INTERNAL;
303 *errnop = errno;
304 }
305 return retval;
306 }
307 }
308
309 if (_res.options & RES_USE_INET6)
310 parse_res = _nss_nisplus_parse_hostent (result, AF_INET6, host, buffer,
311 buflen, errnop, AI_V4MAPPED);
312 else
313 parse_res = _nss_nisplus_parse_hostent (result, AF_INET, host, buffer,
314 buflen, errnop, 0);
315
316 if (parse_res == -1)
317 {
318 nis_freeresult (result);
319 result = saved_res;
320 *herrnop = NETDB_INTERNAL;
321 *errnop = ERANGE;
322 return NSS_STATUS_TRYAGAIN;
323 }
324 if (saved_res != NULL)
325 nis_freeresult (saved_res);
326
327 } while (!parse_res);
328
329 return NSS_STATUS_SUCCESS;
330 }
331
332 enum nss_status
333 _nss_nisplus_gethostent_r (struct hostent *result, char *buffer,
334 size_t buflen, int *errnop, int *herrnop)
335 {
336 int status;
337
338 __libc_lock_lock (lock);
339
340 status = internal_nisplus_gethostent_r (result, buffer, buflen, errnop,
341 herrnop);
342
343 __libc_lock_unlock (lock);
344
345 return status;
346 }
347
348 static enum nss_status
349 internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
350 char *buffer, size_t buflen, int *errnop,
351 int *herrnop, int flags)
352 {
353 int parse_res, retval;
354
355 if (tablename_val == NULL)
356 {
357 __libc_lock_lock (lock);
358
359 enum nss_status status = _nss_create_tablename (errnop);
360
361 __libc_lock_unlock (lock);
362
363 if (status != NSS_STATUS_SUCCESS)
364 {
365 *herrnop = NETDB_INTERNAL;
366 return NSS_STATUS_UNAVAIL;
367 }
368 }
369
370 if (name == NULL)
371 {
372 *errnop = EINVAL;
373 *herrnop = NETDB_INTERNAL;
374 return NSS_STATUS_NOTFOUND;
375 }
376
377 nis_result *result;
378 char buf[strlen (name) + 10 + tablename_len];
379 int olderr = errno;
380
381 /* Search at first in the alias list, and use the correct name
382 for the next search. */
383 snprintf (buf, sizeof (buf), "[name=%s],%s", name, tablename_val);
384 result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
385
386 if (result != NULL)
387 {
388 char *bufptr = buf;
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 if ((result->status != NIS_SUCCESS
393 && result->status != NIS_S_SUCCESS)
394 || __type_of (result->objects.objects_val) != NIS_ENTRY_OBJ
395 || strcmp (result->objects.objects_val->EN_data.en_type,
396 "hosts_tbl") != 0
397 || result->objects.objects_val->EN_data.en_cols.en_cols_len < 3)
398 snprintf (buf, sizeof (buf), "[cname=%s],%s", name, tablename_val);
399 else
400 {
401 /* We need to allocate a new buffer since there is no
402 guarantee the returned name has a length limit. */
403 const char *entryval = NISENTRYVAL(0, 0, result);
404 size_t buflen = strlen (entryval) + 10 + tablename_len;
405 bufptr = alloca (buflen);
406 snprintf (bufptr, buflen, "[cname=%s],%s",
407 entryval, tablename_val);
408 }
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 return NSS_STATUS_TRYAGAIN;
418 }
419
420 retval = niserr2nss (result->status);
421 if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0))
422 {
423 if (retval == NSS_STATUS_TRYAGAIN)
424 {
425 *errnop = errno;
426 *herrnop = NETDB_INTERNAL;
427 }
428 else
429 __set_errno (olderr);
430 nis_freeresult (result);
431 return retval;
432 }
433
434 parse_res = _nss_nisplus_parse_hostent (result, af, host, buffer,
435 buflen, errnop, flags);
436
437 nis_freeresult (result);
438
439 if (parse_res > 0)
440 return NSS_STATUS_SUCCESS;
441
442 *herrnop = NETDB_INTERNAL;
443 if (parse_res == -1)
444 {
445 *errnop = ERANGE;
446 return NSS_STATUS_TRYAGAIN;
447 }
448
449 __set_errno (olderr);
450 return NSS_STATUS_NOTFOUND;
451 }
452
453 enum nss_status
454 _nss_nisplus_gethostbyname2_r (const char *name, int af, struct hostent *host,
455 char *buffer, size_t buflen, int *errnop,
456 int *herrnop)
457 {
458 return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
459 herrnop,
460 ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0));
461 }
462
463 enum nss_status
464 _nss_nisplus_gethostbyname_r (const char *name, struct hostent *host,
465 char *buffer, size_t buflen, int *errnop,
466 int *h_errnop)
467 {
468 if (_res.options & RES_USE_INET6)
469 {
470 enum nss_status status;
471
472 status = internal_gethostbyname2_r (name, AF_INET6, host, buffer,
473 buflen, errnop, h_errnop,
474 AI_V4MAPPED);
475 if (status == NSS_STATUS_SUCCESS)
476 return status;
477 }
478
479 return internal_gethostbyname2_r (name, AF_INET, host, buffer,
480 buflen, errnop, h_errnop, 0);
481 }
482
483 enum nss_status
484 _nss_nisplus_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
485 struct hostent *host, char *buffer,
486 size_t buflen, int *errnop, int *herrnop)
487 {
488 if (tablename_val == NULL)
489 {
490 __libc_lock_lock (lock);
491
492 enum nss_status status = _nss_create_tablename (errnop);
493
494 __libc_lock_unlock (lock);
495
496 if (status != NSS_STATUS_SUCCESS)
497 return status;
498 }
499
500 if (addr == NULL)
501 return NSS_STATUS_NOTFOUND;
502
503 char buf[24 + tablename_len];
504 int retval, parse_res;
505 int olderr = errno;
506
507 snprintf (buf, sizeof (buf), "[addr=%s],%s",
508 inet_ntoa (*(const struct in_addr *) addr), tablename_val);
509 nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
510
511 if (result == NULL)
512 {
513 __set_errno (ENOMEM);
514 return NSS_STATUS_TRYAGAIN;
515 }
516
517 retval = niserr2nss (result->status);
518 if (__builtin_expect (retval != NSS_STATUS_SUCCESS, 0))
519 {
520 if (retval == NSS_STATUS_TRYAGAIN)
521 {
522 *errnop = errno;
523 *herrnop = NETDB_INTERNAL;
524 }
525 else
526 __set_errno (olderr);
527 nis_freeresult (result);
528 return retval;
529 }
530
531 parse_res = _nss_nisplus_parse_hostent (result, af, host,
532 buffer, buflen, errnop,
533 ((_res.options & RES_USE_INET6)
534 ? AI_V4MAPPED : 0));
535 nis_freeresult (result);
536
537 if (parse_res > 0)
538 return NSS_STATUS_SUCCESS;
539
540 *herrnop = NETDB_INTERNAL;
541 if (parse_res == -1)
542 {
543 *errnop = ERANGE;
544 return NSS_STATUS_TRYAGAIN;
545 }
546
547 __set_errno (olderr);
548 return NSS_STATUS_NOTFOUND;
549 }