1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * files/gethostent.c -- "files" backend for nsswitch "hosts" database
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <netdb.h>
31 #include "files_common.h"
32 #include <string.h>
33 #include <strings.h>
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/nameser.h>
40 #include <arpa/inet.h>
41 #include <ctype.h>
42 
43 static int	check_name(nss_XbyY_args_t *, const char *, int,
44 			int, const char **, int *, void *, int *);
45 static char *do_aliases();
46 static char *strcasestr(const char *as1, const char *as2);
47 nss_status_t __nss_files_XY_hostbyname();
48 int __nss_files_2herrno();
49 static int	__nss_files_get_addr(int, const char *, int,
50 			void *, int, int *);
51 
52 static int
53 check_name(nss_XbyY_args_t *argp, const char *line, int linelen,
54 	int type, const char **namep, int *namelen,
55 	void *addrp, int *addrsize)
56 {
57 	const char	*limit, *linep, *keyp, *addrstart;
58 	int		v6flag = 0, addrlen;
59 
60 	linep = line;
61 	limit = line + linelen;
62 
63 	/* Address */
64 	addrstart = linep;
65 	while (linep < limit && !isspace(*linep)) {
66 		if (*linep == ':')
67 			v6flag++;
68 		linep++;
69 	}
70 	addrlen = linep - addrstart;
71 
72 	/* skip the delimiting spaces */
73 	while (linep < limit && isspace(*linep))
74 		linep++;
75 
76 	/* Canonical name */
77 	keyp = argp->key.name;
78 	*namep = linep;
79 	while (*keyp && linep < limit && !isspace(*linep) &&
80 			tolower(*keyp) == tolower(*linep)) {
81 		keyp++;
82 		linep++;
83 	}
84 	if (*keyp == '\0' && (linep == limit || isspace(*linep))) {
85 		if (__nss_files_get_addr(type, addrstart, addrlen,
86 					addrp, v6flag, addrsize)) {
87 			*namelen = linep - *namep;
88 			return (1);
89 		}
90 	}
91 	while (linep < limit && !isspace(*linep))
92 		linep++;
93 	*namelen = linep - *namep;
94 
95 	/* Aliases */
96 	while (linep < limit) {
97 		/* skip the delimiting spaces */
98 		while (linep < limit && isspace(*linep))
99 			linep++;
100 
101 		/* compare name (case insensitive) */
102 		keyp = argp->key.name;
103 		while (*keyp && linep < limit && !isspace(*linep) &&
104 				tolower(*keyp) == tolower(*linep)) {
105 			keyp++;
106 			linep++;
107 		}
108 		if (*keyp == '\0' && (linep == limit || isspace(*linep)))
109 			return (__nss_files_get_addr(type, addrstart, addrlen,
110 					addrp, v6flag, addrsize));
111 
112 		/* skip remainder of alias, if any */
113 		while (linep < limit && !isspace(*linep))
114 			linep++;
115 	}
116 	return (0);
117 
118 }
119 
120 static nss_status_t
121 getbyname(be, a)
122 	files_backend_ptr_t	be;
123 	void			*a;
124 {
125 	nss_XbyY_args_t		*argp = (nss_XbyY_args_t *)a;
126 	nss_status_t		res;
127 
128 	res = __nss_files_XY_hostbyname(be, argp, argp->key.name, AF_INET);
129 	if (res != NSS_SUCCESS)
130 		argp->h_errno = __nss_files_2herrno(res);
131 	return (res);
132 }
133 
134 static int
135 __nss_files_get_addr(int af, const char *addrstart, int addrlen,
136 	void *addrp, int v6flag, int *h_length)
137 {
138 	struct in_addr	addr_ipv4;
139 	struct in6_addr	*addrpv6;
140 	in_addr_t	*addrpv4;
141 	char		addrbuf[INET6_ADDRSTRLEN + 1];
142 
143 	if (addrlen >= sizeof (addrbuf))
144 		return (0);
145 	(void) memcpy(addrbuf, addrstart, addrlen);
146 	addrbuf[addrlen] = '\0';
147 
148 	if (af == AF_INET) {
149 		addrpv4 = (in_addr_t *)addrp;
150 		if ((*addrpv4 = inet_addr(addrbuf)) == 0xffffffffU)
151 			return (0);
152 		*h_length = sizeof (in_addr_t);
153 	} else if (af == AF_INET6) {
154 		addrpv6 = (struct in6_addr *)addrp;
155 		if (v6flag) {
156 			if (inet_pton(af, addrbuf, addrpv6) != 1)
157 				return (0);
158 		} else {
159 			if ((addr_ipv4.s_addr = inet_addr(addrbuf)) ==
160 							0xffffffffU)
161 				return (0);
162 			IN6_INADDR_TO_V4MAPPED(&addr_ipv4, addrpv6);
163 		}
164 		*h_length = sizeof (struct in6_addr);
165 	} else {
166 		return (0);
167 	}
168 	return (1);
169 }
170 
171 
172 int
173 __nss_files_check_addr(int af, nss_XbyY_args_t *argp, const char *line,
174 		int linelen)
175 {
176 	const char	*limit, *linep, *addrstart;
177 	int		v6flag = 0, addrlen, h_length;
178 	in_addr_t	addr_ipv4;
179 	struct in6_addr	addr_ipv6;
180 	char		*h_addrp;
181 
182 	/* Compare the address type */
183 	if (argp->key.hostaddr.type != af)
184 		return (0);
185 
186 	/* Retrieve the address */
187 	if (af == AF_INET)
188 		h_addrp = (char *)&addr_ipv4;
189 	else
190 		h_addrp = (char *)&addr_ipv6;
191 	linep = line;
192 	limit = line + linelen;
193 	addrstart = linep;
194 	while (linep < limit && !isspace(*linep)) {
195 		if (*linep == ':')
196 			v6flag++;
197 		linep++;
198 	}
199 	addrlen = linep - addrstart;
200 	if (__nss_files_get_addr(af, addrstart, addrlen, h_addrp,
201 			v6flag, &h_length) == 0)
202 		return (0);
203 
204 	/* Compare the address */
205 	return (h_length == argp->key.hostaddr.len &&
206 		memcmp(h_addrp, argp->key.hostaddr.addr,
207 			argp->key.hostaddr.len) == 0);
208 }
209 
210 static int
211 check_addr(nss_XbyY_args_t *argp, const char *line, int linelen)
212 {
213 	return (__nss_files_check_addr(AF_INET, argp, line, linelen));
214 }
215 
216 static nss_status_t
217 getbyaddr(be, a)
218 	files_backend_ptr_t	be;
219 	void			*a;
220 {
221 	nss_XbyY_args_t		*argp	= (nss_XbyY_args_t *)a;
222 	nss_status_t		res;
223 
224 	res = _nss_files_XY_all(be, argp, 1, 0, check_addr);
225 	if (res != NSS_SUCCESS)
226 		argp->h_errno = __nss_files_2herrno(res);
227 	return (res);
228 }
229 
230 
231 static files_backend_op_t host_ops[] = {
232 	_nss_files_destr,
233 	_nss_files_endent,
234 	_nss_files_setent,
235 	_nss_files_getent_netdb,
236 	getbyname,
237 	getbyaddr,
238 };
239 
240 /*ARGSUSED*/
241 nss_backend_t *
242 _nss_files_hosts_constr(dummy1, dummy2, dummy3)
243 	const char	*dummy1, *dummy2, *dummy3;
244 {
245 	return (_nss_files_constr(host_ops,
246 				sizeof (host_ops) / sizeof (host_ops[0]),
247 				_PATH_HOSTS,
248 				NSS_LINELEN_HOSTS,
249 				NULL));
250 }
251 
252 
253 /*
254  * XXX - this duplicates code from files_common.c because we need to keep
255  * going after we've found a match to satisfy the multihomed host case.
256  */
257 nss_status_t
258 __nss_files_XY_hostbyname(be, args, filter, type)
259 	files_backend_ptr_t be;
260 	nss_XbyY_args_t *args;
261 	const char *filter;		/* hint for name string */
262 	int type;
263 {
264 	nss_status_t	res;
265 	char		*abuf = NULL, *abuf_start = NULL, *abuf_end;
266 	char		*first, *last, *buffer;
267 	int		parsestat, i, nhosts = 0, buflen;
268 	const char	*namep;
269 	char		*h_name;
270 	int		h_namelen, namelen;
271 	struct hostent	*hp;
272 	in_addr_t	*taddr = NULL;
273 	struct in6_addr	*taddr6 = NULL;
274 	size_t		ntaddr;
275 	void		*addrp;
276 	char		*alias_end = NULL;
277 
278 	if (be->buf == 0 && (be->buf = malloc(be->minbuf)) == 0) {
279 		return (NSS_UNAVAIL);
280 	}
281 
282 	if (be->f == 0) {
283 		if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS)
284 			return (res);
285 	}
286 
287 	ntaddr = MAXADDRS;
288 	if (type == AF_INET) {
289 		taddr = (in_addr_t *)calloc(ntaddr, sizeof (*taddr));
290 		if (taddr == NULL)
291 			return (NSS_UNAVAIL);
292 	} else {
293 		taddr6 = (struct in6_addr *)calloc(ntaddr, sizeof (*taddr6));
294 		if (taddr6 == NULL)
295 			return (NSS_UNAVAIL);
296 	}
297 
298 	res = NSS_NOTFOUND;
299 	args->returnval = (char *)0;
300 	args->returnlen = 0;
301 	hp = (struct hostent *)args->buf.result;
302 	buffer = args->buf.buffer;
303 	buflen = args->buf.buflen;
304 	h_namelen = 0;
305 	h_name = NULL;
306 
307 	for (;;) {
308 		char *instr = be->buf;
309 		int linelen;
310 
311 		if ((linelen = _nss_files_read_line(be->f,
312 		    instr, be->minbuf)) < 0) {
313 			break;		/* EOF */
314 		}
315 
316 		/*
317 		 * This check avoids a malloc()/free() for the common
318 		 * case. Also, if we're trying to match an alias and an
319 		 * already matched entry doesn't share a canonical name
320 		 * with the current one, bail.
321 		 */
322 		if (nhosts == 0 && strcasestr(instr, filter) == 0) {
323 			continue;
324 		}
325 
326 		if ((last = strchr(instr, '#')) == 0)
327 			last = instr + linelen;
328 		*last-- = '\0';
329 		for (first = instr;  isspace(*first);  first++)
330 			;
331 		/* Ignore blank and comment lines */
332 		if (*first == '\0')
333 			continue;
334 
335 		while (isspace(*last))
336 			--last;
337 		linelen = last - first + 1;
338 		if (first != instr)
339 			instr = first;
340 
341 		/* Bail out if the canonical name does not match */
342 		if (nhosts && strcasestr(instr, h_name) == 0) {
343 			continue;
344 		}
345 
346 		/*
347 		 * Still need to check, strcasestr() above is just a hint.
348 		 */
349 		addrp = (type == AF_INET)?
350 				(void *)&taddr[nhosts]:
351 				(void *)&taddr6[nhosts];
352 
353 		if (check_name(args, instr, linelen,
354 				type, &namep, &namelen,
355 				addrp, &i)) {
356 
357 			/*
358 			 * If we've already matched once and have a possible
359 			 * match on this line, copy the aliases where they're
360 			 * safe from being overwritten when we look at the
361 			 * next entry. They're saved as a string of blank
362 			 * separated names for the alias parser. On errors,
363 			 * we return failure whether or not we have already
364 			 * obtained a valid address.
365 			 */
366 			if (nhosts == 1 && hp) {
367 				if (h_namelen + 1 > args->buf.buflen) {
368 					args->erange = 1;
369 					res = NSS_NOTFOUND;
370 					break;
371 				}
372 				abuf = (char *)malloc(args->buf.buflen);
373 				if (abuf == NULL) {
374 					res = NSS_UNAVAIL;
375 					break;
376 				}
377 				abuf_start = abuf;
378 				abuf_end = abuf_start + args->buf.buflen;
379 				(void) memcpy(abuf, h_name, h_namelen);
380 				abuf += h_namelen;
381 				*abuf = '\0';
382 				abuf = do_aliases(hp, abuf, abuf_end);
383 				if (abuf == NULL) {
384 					args->erange = 1;
385 					res = NSS_NOTFOUND;
386 					break;
387 				}
388 			}
389 
390 			if (hp != NULL) {
391 				/* inside the application */
392 				parsestat = (*args->str2ent)(instr, linelen,
393 						hp, buffer, buflen);
394 				if (parsestat != NSS_STR_PARSE_SUCCESS) {
395 					if (parsestat == NSS_STR_PARSE_ERANGE)
396 						args->erange = 1;
397 					(void) memset(buffer, 0, buflen);
398 					continue;
399 				}
400 			} else {
401 				/* inside nscd */
402 				int	alen, cplen, erange = 0;
403 				char	*ap;
404 
405 				/* Add alias to the first line if any */
406 				if (nhosts > 0) {
407 
408 					/* get to the start of alias */
409 					ap = (char *)namep + namelen;
410 					/* see if there's any alias */
411 					if (ap == instr + linelen)
412 						alen = 0;
413 					else
414 						alen = linelen - (ap - instr);
415 					if (alen + 1 >= buflen)
416 						erange  = 1;
417 					if (erange == 0 && alen != 0) {
418 						/* make room for the alias */
419 						if (alias_end != NULL)
420 						(void) memmove(alias_end +
421 						alen, alias_end, buffer -
422 						alias_end);
423 						/* copy in the alias */
424 						(void) memmove(alias_end,
425 							ap, alen);
426 						buffer += alen;
427 						buflen -= alen;
428 						alias_end += alen;
429 					}
430 
431 					/* Add delimiter to the buffer */
432 					*buffer++ = '\n';
433 					buflen--;
434 					args->returnlen++;
435 				}
436 
437 				/* copy just the addr if not first one */
438 				if (alias_end == NULL)
439 					cplen = linelen;
440 				else
441 					cplen = namep - instr;
442 
443 				if (cplen >= buflen || erange == 1) {
444 					args->erange = 1;
445 					if (nhosts > 0) {
446 						*(--buffer) = '\0';
447 						buflen++;
448 						args->returnlen--;
449 					}
450 					continue;
451 				}
452 
453 				(void) memcpy(buffer, instr, cplen);
454 				/* Adjust buffer */
455 				buffer += cplen;
456 				*buffer = '\0';
457 				buflen -= cplen;
458 				if (alias_end == NULL)
459 					alias_end = buffer;
460 			}
461 
462 			args->returnlen += linelen;
463 
464 			/*
465 			 * If this is the first one, save the canonical
466 			 * name for future matches and continue.
467 			 */
468 			if (++nhosts == 1) {
469 				h_name = malloc(namelen + 1);
470 				if (h_name == NULL) {
471 					res = NSS_UNAVAIL;
472 					break;
473 				}
474 				res = NSS_SUCCESS;
475 				(void) memcpy(h_name, namep, namelen);
476 				h_name[namelen] = '\0';
477 				h_namelen = namelen;
478 				if (hp)
479 					args->returnval = hp;
480 				else
481 					args->returnval = args->buf.buffer;
482 				continue;
483 			}
484 
485 
486 			/* Extend the array */
487 			if (nhosts >= ntaddr) {
488 				ntaddr *= 2;
489 				if (type == AF_INET) {
490 					addrp = realloc(taddr,
491 						sizeof (*taddr) * ntaddr);
492 					if (addrp == NULL) {
493 						res = NSS_UNAVAIL;
494 						break;
495 					}
496 					taddr = (in_addr_t *)addrp;
497 				} else {
498 					addrp = realloc(taddr6,
499 						sizeof (*taddr6) * ntaddr);
500 					if (addrp == NULL) {
501 						res = NSS_UNAVAIL;
502 						break;
503 					}
504 					taddr6 = (struct in6_addr *)addrp;
505 				}
506 			}
507 
508 			/*
509 			 * For non-nscd, save aliases in a temporary buffer
510 			 * Don't have to do this for nscd as 'buffer' already
511 			 * contains the required data in the appropriate
512 			 * format
513 			 */
514 			if (hp) {
515 				abuf = do_aliases(hp, abuf, abuf_end);
516 				if (abuf == NULL) {
517 					args->erange = 1;
518 					res = NSS_NOTFOUND;
519 					break;
520 				}
521 			}
522 		} else if (namep && h_namelen == namelen &&
523 		    strncasecmp(h_name, namep, namelen) == 0) {
524 			/*
525 			 * This line didn't have the requested name but
526 			 * is part of the same multihomed host (i.e. it
527 			 * has the same canonical name as the previous
528 			 * line), so march on...
529 			 */
530 			continue;
531 		} else if (nhosts) {
532 			break;
533 		}
534 	}
535 
536 	if (abuf && res == NSS_SUCCESS) {
537 
538 		/* abuf != NULL implies hp and abuf_start != NULL */
539 
540 		struct in_addr *addrp;
541 		struct in6_addr *addrp6;
542 
543 		if (type == AF_INET) {
544 			addrp = (struct in_addr *)(ROUND_DOWN(args->buf.buffer +
545 			    args->buf.buflen, sizeof (*addrp)));
546 			hp->h_addr_list = (char **)(ROUND_DOWN(addrp -
547 			    ((nhosts + 1) * sizeof (char *) +
548 			    (nhosts * sizeof (*addrp))), sizeof (char *)));
549 			for (i = 0, --addrp; i < nhosts; i++, --addrp) {
550 				(*(in_addr_t *)addrp) = taddr[i];
551 				hp->h_addr_list[i] = (char *)addrp;
552 			}
553 		} else {
554 			addrp6 = (struct in6_addr *)
555 			(ROUND_DOWN(args->buf.buffer + args->buf.buflen,
556 			sizeof (*addrp6)));
557 			hp->h_addr_list = (char **)(ROUND_DOWN(addrp6 -
558 			    ((nhosts + 1) * sizeof (char *) +
559 			    (nhosts * sizeof (*addrp6))), sizeof (char *)));
560 			for (i = 0, --addrp6; i < nhosts; i++, --addrp6) {
561 				(void) memcpy(addrp6, &taddr6[i],
562 						sizeof (struct in6_addr));
563 				hp->h_addr_list[i] = (char *)addrp6;
564 			}
565 		}
566 
567 		hp->h_addr_list[nhosts] = 0;
568 		hp->h_aliases = _nss_netdb_aliases(abuf_start,
569 		    abuf - abuf_start, args->buf.buffer,
570 		    (char *)hp->h_addr_list - args->buf.buffer);
571 		if (hp->h_aliases == 0) {
572 			args->erange = 1;
573 			res = NSS_NOTFOUND;
574 		} else {
575 			hp->h_name = hp->h_aliases[0];
576 			hp->h_aliases++;
577 		}
578 	}
579 
580 	/*
581 	 * stayopen is set to 0 by default in order to close the opened
582 	 * file.  Some applications may break if it is set to 1.
583 	 */
584 	if (!args->stayopen)
585 		(void) _nss_files_endent(be, 0);
586 
587 	if (taddr)
588 		free(taddr);
589 	if (taddr6)
590 		free(taddr6);
591 	if (h_name)
592 		free(h_name);
593 	if (abuf_start)
594 		free(abuf_start);
595 
596 	return (res);
597 }
598 
599 /*
600  * A case-insensitive version of strstr().
601  */
602 static char *
603 strcasestr(const char *as1, const char *as2)
604 {
605 	int c2;
606 	register const char *tptr;
607 	register const char *s1, *s2;
608 
609 	s1 = as1;
610 	s2 = as2;
611 
612 	if (s2 == NULL || *s2 == '\0')
613 		return (0);
614 
615 	while (*s1) {
616 		if (tolower(*s1++) == tolower(c2 = *s2)) {
617 			tptr = s1;
618 			while ((tolower(c2 = *++s2) ==
619 			    tolower(*s1++)) && c2 != 0)
620 				;
621 			if (c2 == 0)
622 				return ((char *)tptr - 1);
623 			s1 = tptr;
624 			s2 = as2;
625 		}
626 	}
627 	return (0);
628 }
629 
630 
631 static char *
632 do_aliases(struct hostent *hp, char *abuf, char *end)
633 {
634 	char	**cp;
635 	size_t	len;
636 
637 	if ((cp = hp->h_aliases) == NULL)
638 		return (abuf);
639 
640 	for (; *cp; cp++) {
641 		len = strlen(*cp);
642 		if (abuf+len+1 >= end) {
643 			return (NULL);
644 		}
645 		*abuf++ = ' ';
646 		(void) memcpy(abuf, *cp, len);
647 		abuf += len;
648 	}
649 	*abuf = '\0';
650 
651 	return (abuf);
652 }
653 
654 
655 /*
656  * This is a copy of a routine in libnsl/nss/netdir_inet.c.  It is
657  * here because /etc/lib/nss_files.so.1 cannot call routines
658  * in libnsl.  Care should be taken to keep the two copies in sync.
659  */
660 int
661 __nss_files_2herrno(nsstat)
662 	nss_status_t nsstat;
663 {
664 	switch (nsstat) {
665 	case NSS_SUCCESS:
666 		/* no macro-defined success code for h_errno */
667 		return (0);
668 	case NSS_NOTFOUND:
669 		return (HOST_NOT_FOUND);
670 	case NSS_TRYAGAIN:
671 		return (TRY_AGAIN);
672 	case NSS_UNAVAIL:
673 		return (NO_RECOVERY);
674 	}
675 	/* anything else */
676 	return (NO_RECOVERY);
677 }
678