gethostent.c revision 2830:5228d1267a01
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
43static int	check_name(nss_XbyY_args_t *, const char *, int,
44			int, const char **, int *, void *, int *);
45static char *do_aliases();
46static char *strcasestr(const char *as1, const char *as2);
47nss_status_t __nss_files_XY_hostbyname();
48int __nss_files_2herrno();
49static int	__nss_files_get_addr(int, const char *, int,
50			void *, int, int *);
51
52static int
53check_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
120static nss_status_t
121getbyname(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
134static 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
172int
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
210static int
211check_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
216static nss_status_t
217getbyaddr(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
231static 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*/
241nss_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 */
257nss_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 */
602static char *
603strcasestr(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
631static char *
632do_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 */
660int
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