]> git.ipfire.org Git - thirdparty/glibc.git/blob - nis/nss_nis/nis-hosts.c
d70973dceee6b45cc4c5c2891d85c7d1600578dc
[thirdparty/glibc.git] / nis / nss_nis / nis-hosts.c
1 /* Copyright (C) 1996-2019 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
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 <nss.h>
21 #include <ctype.h>
22 /* The following is an ugly trick to avoid a prototype declaration for
23 _nss_nis_endgrent. */
24 #define _nss_nis_endhostent _nss_nis_endhostent_XXX
25 #include <netdb.h>
26 #undef _nss_nis_endhostent
27 #include <string.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <libc-lock.h>
31 #include <rpcsvc/yp.h>
32 #include <rpcsvc/ypclnt.h>
33
34 #include "nss-nis.h"
35
36 #define ENTNAME hostent
37 #define DATABASE "hosts"
38 #define NEED_H_ERRNO
39
40 #define EXTRA_ARGS , af
41 #define EXTRA_ARGS_DECL , int af
42
43 #define ENTDATA hostent_data
44 struct hostent_data
45 {
46 unsigned char host_addr[16]; /* IPv4 or IPv6 address. */
47 char *h_addr_ptrs[2]; /* Points to that and null terminator. */
48 };
49
50 #define TRAILING_LIST_MEMBER h_aliases
51 #define TRAILING_LIST_SEPARATOR_P isspace
52 #include <nss/nss_files/files-parse.c>
53 LINE_PARSER
54 ("#",
55 {
56 char *addr;
57
58 STRING_FIELD (addr, isspace, 1);
59
60 assert (af == AF_INET || af == AF_INET6 || af == AF_UNSPEC);
61
62 /* Parse address. */
63 if (af != AF_INET6 && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
64 {
65 result->h_addrtype = AF_INET;
66 result->h_length = INADDRSZ;
67 }
68 else if (af != AF_INET
69 && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
70 {
71 result->h_addrtype = AF_INET6;
72 result->h_length = IN6ADDRSZ;
73 }
74 else
75 /* Illegal address: ignore line. */
76 return 0;
77
78 /* Store a pointer to the address in the expected form. */
79 entdata->h_addr_ptrs[0] = (char *) entdata->host_addr;
80 entdata->h_addr_ptrs[1] = NULL;
81 result->h_addr_list = entdata->h_addr_ptrs;
82
83 STRING_FIELD (result->h_name, isspace, 1);
84 })
85
86
87 __libc_lock_define_initialized (static, lock)
88
89 static bool_t new_start = 1;
90 static char *oldkey = NULL;
91 static int oldkeylen = 0;
92
93
94 enum nss_status
95 _nss_nis_sethostent (int stayopen)
96 {
97 __libc_lock_lock (lock);
98
99 new_start = 1;
100 if (oldkey != NULL)
101 {
102 free (oldkey);
103 oldkey = NULL;
104 oldkeylen = 0;
105 }
106
107 __libc_lock_unlock (lock);
108
109 return NSS_STATUS_SUCCESS;
110 }
111 /* Make _nss_nis_endhostent an alias of _nss_nis_sethostent. We do this
112 even though the prototypes don't match. The argument of sethostent
113 is used so this makes no difference. */
114 strong_alias (_nss_nis_sethostent, _nss_nis_endhostent)
115
116
117 /* The calling function always need to get a lock first. */
118 static enum nss_status
119 internal_nis_gethostent_r (struct hostent *host, char *buffer,
120 size_t buflen, int *errnop, int *h_errnop,
121 int af)
122 {
123 char *domain;
124 if (__glibc_unlikely (yp_get_default_domain (&domain)))
125 return NSS_STATUS_UNAVAIL;
126
127 uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct parser_data);
128 buffer += pad;
129
130 struct parser_data *data = (void *) buffer;
131 if (__glibc_unlikely (buflen < sizeof *data + 1 + pad))
132 {
133 *errnop = ERANGE;
134 *h_errnop = NETDB_INTERNAL;
135 return NSS_STATUS_TRYAGAIN;
136 }
137 buflen -= pad;
138
139 /* Get the next entry until we found a correct one. */
140 const size_t linebuflen = buffer + buflen - data->linebuffer;
141 int parse_res;
142 do
143 {
144 char *result;
145 int len;
146 char *outkey;
147 int keylen;
148 int yperr;
149 if (new_start)
150 yperr = yp_first (domain, "hosts.byname", &outkey, &keylen, &result,
151 &len);
152 else
153 yperr = yp_next (domain, "hosts.byname", oldkey, oldkeylen, &outkey,
154 &keylen, &result, &len);
155
156 if (__glibc_unlikely (yperr != YPERR_SUCCESS))
157 {
158 enum nss_status retval = yperr2nss (yperr);
159
160 switch (retval)
161 {
162 case NSS_STATUS_TRYAGAIN:
163 *errnop = errno;
164 *h_errnop = TRY_AGAIN;
165 break;
166 case NSS_STATUS_NOTFOUND:
167 *h_errnop = HOST_NOT_FOUND;
168 break;
169 default:
170 *h_errnop = NO_RECOVERY;
171 break;
172 }
173 return retval;
174 }
175
176 if (__glibc_unlikely ((size_t) (len + 1) > linebuflen))
177 {
178 free (result);
179 *h_errnop = NETDB_INTERNAL;
180 *errnop = ERANGE;
181 return NSS_STATUS_TRYAGAIN;
182 }
183
184 char *p = strncpy (data->linebuffer, result, len);
185 data->linebuffer[len] = '\0';
186 while (isspace (*p))
187 ++p;
188 free (result);
189
190 parse_res = parse_line (p, host, data, buflen, errnop, af);
191 if (__glibc_unlikely (parse_res == -1))
192 {
193 free (outkey);
194 *h_errnop = NETDB_INTERNAL;
195 *errnop = ERANGE;
196 return NSS_STATUS_TRYAGAIN;
197 }
198 free (oldkey);
199 oldkey = outkey;
200 oldkeylen = keylen;
201 new_start = 0;
202 }
203 while (!parse_res);
204
205 *h_errnop = NETDB_SUCCESS;
206 return NSS_STATUS_SUCCESS;
207 }
208
209
210 enum nss_status
211 _nss_nis_gethostent_r (struct hostent *host, char *buffer, size_t buflen,
212 int *errnop, int *h_errnop)
213 {
214 enum nss_status status;
215
216 __libc_lock_lock (lock);
217
218 status = internal_nis_gethostent_r (host, buffer, buflen, errnop, h_errnop,
219 AF_INET);
220
221 __libc_lock_unlock (lock);
222
223 return status;
224 }
225
226
227 static enum nss_status
228 internal_gethostbyname2_r (const char *name, int af, struct hostent *host,
229 char *buffer, size_t buflen, int *errnop,
230 int *h_errnop)
231 {
232 uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct parser_data);
233 buffer += pad;
234
235 struct parser_data *data = (void *) buffer;
236
237 if (name == NULL)
238 {
239 *errnop = EINVAL;
240 return NSS_STATUS_UNAVAIL;
241 }
242
243 char *domain;
244 if (yp_get_default_domain (&domain))
245 return NSS_STATUS_UNAVAIL;
246
247 if (buflen < sizeof *data + 1 + pad)
248 {
249 *h_errnop = NETDB_INTERNAL;
250 *errnop = ERANGE;
251 return NSS_STATUS_TRYAGAIN;
252 }
253 buflen -= pad;
254
255 /* Convert name to lowercase. */
256 size_t namlen = strlen (name);
257 /* Limit name length to the maximum size of an RPC packet. */
258 if (namlen > UDPMSGSIZE)
259 {
260 *errnop = ERANGE;
261 return NSS_STATUS_UNAVAIL;
262 }
263
264 char name2[namlen + 1];
265 size_t i;
266
267 for (i = 0; i < namlen; ++i)
268 name2[i] = tolower (name[i]);
269 name2[i] = '\0';
270
271 char *result;
272 int len;
273 int yperr = yp_match (domain, "hosts.byname", name2, namlen, &result, &len);
274
275 if (__glibc_unlikely (yperr != YPERR_SUCCESS))
276 {
277 enum nss_status retval = yperr2nss (yperr);
278
279 if (retval == NSS_STATUS_TRYAGAIN)
280 {
281 *h_errnop = TRY_AGAIN;
282 *errnop = errno;
283 }
284 if (retval == NSS_STATUS_NOTFOUND)
285 *h_errnop = HOST_NOT_FOUND;
286 return retval;
287 }
288
289 const size_t linebuflen = buffer + buflen - data->linebuffer;
290 if (__glibc_unlikely ((size_t) (len + 1) > linebuflen))
291 {
292 free (result);
293 *h_errnop = NETDB_INTERNAL;
294 *errnop = ERANGE;
295 return NSS_STATUS_TRYAGAIN;
296 }
297
298 char *p = strncpy (data->linebuffer, result, len);
299 data->linebuffer[len] = '\0';
300 while (isspace (*p))
301 ++p;
302 free (result);
303
304 int parse_res = parse_line (p, host, data, buflen, errnop, af);
305
306 if (__glibc_unlikely (parse_res < 1 || host->h_addrtype != af))
307 {
308 if (parse_res == -1)
309 {
310 *h_errnop = NETDB_INTERNAL;
311 return NSS_STATUS_TRYAGAIN;
312 }
313 else
314 {
315 *h_errnop = HOST_NOT_FOUND;
316 return NSS_STATUS_NOTFOUND;
317 }
318 }
319
320 *h_errnop = NETDB_SUCCESS;
321 return NSS_STATUS_SUCCESS;
322 }
323
324
325 enum nss_status
326 _nss_nis_gethostbyname2_r (const char *name, int af, struct hostent *host,
327 char *buffer, size_t buflen, int *errnop,
328 int *h_errnop)
329 {
330 if (af != AF_INET && af != AF_INET6)
331 {
332 *h_errnop = HOST_NOT_FOUND;
333 return NSS_STATUS_NOTFOUND;
334 }
335
336 return internal_gethostbyname2_r (name, af, host, buffer, buflen, errnop,
337 h_errnop);
338 }
339
340
341 enum nss_status
342 _nss_nis_gethostbyname_r (const char *name, struct hostent *host, char *buffer,
343 size_t buflen, int *errnop, int *h_errnop)
344 {
345 return internal_gethostbyname2_r (name, AF_INET, host, buffer, buflen,
346 errnop, h_errnop);
347 }
348
349
350 enum nss_status
351 _nss_nis_gethostbyaddr_r (const void *addr, socklen_t addrlen, int af,
352 struct hostent *host, char *buffer, size_t buflen,
353 int *errnop, int *h_errnop)
354 {
355 char *domain;
356 if (__glibc_unlikely (yp_get_default_domain (&domain)))
357 return NSS_STATUS_UNAVAIL;
358
359 uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct parser_data);
360 buffer += pad;
361
362 struct parser_data *data = (void *) buffer;
363 if (__glibc_unlikely (buflen < sizeof *data + 1 + pad))
364 {
365 *errnop = ERANGE;
366 *h_errnop = NETDB_INTERNAL;
367 return NSS_STATUS_TRYAGAIN;
368 }
369 buflen -= pad;
370
371 char *buf = inet_ntoa (*(const struct in_addr *) addr);
372
373 char *result;
374 int len;
375 int yperr = yp_match (domain, "hosts.byaddr", buf, strlen (buf), &result,
376 &len);
377
378 if (__glibc_unlikely (yperr != YPERR_SUCCESS))
379 {
380 enum nss_status retval = yperr2nss (yperr);
381
382 if (retval == NSS_STATUS_TRYAGAIN)
383 {
384 *h_errnop = TRY_AGAIN;
385 *errnop = errno;
386 }
387 else if (retval == NSS_STATUS_NOTFOUND)
388 *h_errnop = HOST_NOT_FOUND;
389
390 return retval;
391 }
392
393 const size_t linebuflen = buffer + buflen - data->linebuffer;
394 if (__glibc_unlikely ((size_t) (len + 1) > linebuflen))
395 {
396 free (result);
397 *errnop = ERANGE;
398 *h_errnop = NETDB_INTERNAL;
399 return NSS_STATUS_TRYAGAIN;
400 }
401
402 char *p = strncpy (data->linebuffer, result, len);
403 data->linebuffer[len] = '\0';
404 while (isspace (*p))
405 ++p;
406 free (result);
407
408 int parse_res = parse_line (p, host, data, buflen, errnop, af);
409 if (__glibc_unlikely (parse_res < 1))
410 {
411 if (parse_res == -1)
412 {
413 *h_errnop = NETDB_INTERNAL;
414 return NSS_STATUS_TRYAGAIN;
415 }
416 else
417 {
418 *h_errnop = HOST_NOT_FOUND;
419 return NSS_STATUS_NOTFOUND;
420 }
421 }
422
423 *h_errnop = NETDB_SUCCESS;
424 return NSS_STATUS_SUCCESS;
425 }
426
427
428 enum nss_status
429 _nss_nis_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
430 char *buffer, size_t buflen, int *errnop,
431 int *herrnop, int32_t *ttlp)
432 {
433 char *domain;
434 if (yp_get_default_domain (&domain))
435 {
436 *herrnop = NO_DATA;
437 return NSS_STATUS_UNAVAIL;
438 }
439
440 /* Convert name to lowercase. */
441 size_t namlen = strlen (name);
442 /* Limit name length to the maximum size of an RPC packet. */
443 if (namlen > UDPMSGSIZE)
444 {
445 *errnop = ERANGE;
446 return NSS_STATUS_UNAVAIL;
447 }
448
449 char name2[namlen + 1];
450 size_t i;
451
452 for (i = 0; i < namlen; ++i)
453 name2[i] = tolower (name[i]);
454 name2[i] = '\0';
455
456 char *result;
457 int len;
458 int yperr = yp_match (domain, "hosts.byname", name2, namlen, &result, &len);
459
460 if (__glibc_unlikely (yperr != YPERR_SUCCESS))
461 {
462 enum nss_status retval = yperr2nss (yperr);
463
464 if (retval == NSS_STATUS_TRYAGAIN)
465 {
466 *herrnop = TRY_AGAIN;
467 *errnop = errno;
468 }
469 if (retval == NSS_STATUS_NOTFOUND)
470 *herrnop = HOST_NOT_FOUND;
471 return retval;
472 }
473
474 if (*pat == NULL)
475 {
476 uintptr_t pad = (-(uintptr_t) buffer
477 % __alignof__ (struct gaih_addrtuple));
478 buffer += pad;
479 buflen = buflen > pad ? buflen - pad : 0;
480
481 if (__glibc_unlikely (buflen < sizeof (struct gaih_addrtuple)))
482 {
483 erange:
484 free (result);
485 *errnop = ERANGE;
486 *herrnop = NETDB_INTERNAL;
487 return NSS_STATUS_TRYAGAIN;
488 }
489
490 *pat = (struct gaih_addrtuple *) buffer;
491 buffer += sizeof (struct gaih_addrtuple);
492 buflen -= sizeof (struct gaih_addrtuple);
493 }
494
495 uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct parser_data);
496 buffer += pad;
497
498 struct parser_data *data = (void *) buffer;
499
500 if (__glibc_unlikely (buflen < sizeof *data + 1 + pad))
501 goto erange;
502 buflen -= pad;
503
504 struct hostent host;
505 int parse_res = parse_line (result, &host, data, buflen, errnop, AF_UNSPEC);
506 if (__glibc_unlikely (parse_res < 1))
507 {
508 if (parse_res == -1)
509 {
510 *herrnop = NETDB_INTERNAL;
511 return NSS_STATUS_TRYAGAIN;
512 }
513 else
514 {
515 *herrnop = HOST_NOT_FOUND;
516 return NSS_STATUS_NOTFOUND;
517 }
518 }
519
520 (*pat)->next = NULL;
521 (*pat)->family = host.h_addrtype;
522 memcpy ((*pat)->addr, host.h_addr_list[0], host.h_length);
523 (*pat)->scopeid = 0;
524 assert (host.h_addr_list[1] == NULL);
525
526 /* Undo the alignment for parser_data. */
527 buffer -= pad;
528 buflen += pad;
529
530 size_t h_name_len = strlen (host.h_name) + 1;
531 if (h_name_len >= buflen)
532 goto erange;
533 (*pat)->name = memcpy (buffer, host.h_name, h_name_len);
534
535 free (result);
536
537 return NSS_STATUS_SUCCESS;
538 }