xref: /illumos-gate/usr/src/lib/nsswitch/files/common/gethostent.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * files/gethostent.c -- "files" backend for nsswitch "hosts" database
27  */
28 
29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 #include <netdb.h>
32 #include "files_common.h"
33 #include <string.h>
34 #include <strings.h>
35 #include <stddef.h>
36 #include <stdlib.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/nameser.h>
41 #include <ctype.h>
42 
43 static int check_name();
44 static char *do_aliases();
45 static char *strcasestr();
46 nss_status_t __nss_files_XY_hostbyname();
47 int __nss_files_2herrno();
48 
49 static int
50 check_name(host, args)
51 	struct hostent		*host;
52 	nss_XbyY_args_t		*args;
53 {
54 	const char		*name = args->key.name;
55 	char			**aliasp;
56 
57 	if (!host->h_name)
58 		return (0);
59 	if (strcasecmp(host->h_name, name) == 0) {
60 		return (1);
61 	}
62 	for (aliasp = host->h_aliases;  *aliasp != 0;  aliasp++) {
63 		if (strcasecmp(*aliasp, name) == 0) {
64 			return (1);
65 		}
66 	}
67 	return (0);
68 }
69 
70 static nss_status_t
71 getbyname(be, a)
72 	files_backend_ptr_t	be;
73 	void			*a;
74 {
75 	nss_XbyY_args_t		*argp = (nss_XbyY_args_t *)a;
76 	nss_status_t		res;
77 
78 	res = __nss_files_XY_hostbyname(be, argp, argp->key.name, AF_INET);
79 	if (res != NSS_SUCCESS)
80 		argp->h_errno = __nss_files_2herrno(res);
81 	return (res);
82 }
83 
84 
85 int
86 __nss_files_check_addr(argp)
87 	nss_XbyY_args_t		*argp;
88 {
89 	struct hostent		*host	= (struct hostent *)argp->returnval;
90 
91 	/*
92 	 * We know that /etc/hosts can only store one address per host, so...
93 	 */
94 	return (host->h_length == argp->key.hostaddr.len &&
95 		host->h_addrtype == argp->key.hostaddr.type &&
96 		memcmp(host->h_addr_list[0], argp->key.hostaddr.addr,
97 			argp->key.hostaddr.len) == 0);
98 }
99 
100 
101 static nss_status_t
102 getbyaddr(be, a)
103 	files_backend_ptr_t	be;
104 	void			*a;
105 {
106 	nss_XbyY_args_t		*argp	= (nss_XbyY_args_t *)a;
107 	nss_status_t		res;
108 
109 	res = _nss_files_XY_all(be, argp, 1, 0, __nss_files_check_addr);
110 	if (res != NSS_SUCCESS)
111 		argp->h_errno = __nss_files_2herrno(res);
112 	return (res);
113 }
114 
115 
116 static files_backend_op_t host_ops[] = {
117 	_nss_files_destr,
118 	_nss_files_endent,
119 	_nss_files_setent,
120 	_nss_files_getent_netdb,
121 	getbyname,
122 	getbyaddr,
123 };
124 
125 /*ARGSUSED*/
126 nss_backend_t *
127 _nss_files_hosts_constr(dummy1, dummy2, dummy3)
128 	const char	*dummy1, *dummy2, *dummy3;
129 {
130 	return (_nss_files_constr(host_ops,
131 				sizeof (host_ops) / sizeof (host_ops[0]),
132 				_PATH_HOSTS,
133 				NSS_LINELEN_HOSTS,
134 				NULL));
135 }
136 
137 
138 /*
139  * XXX - this duplicates code from files_common.c because we need to keep
140  * going after we've found a match to satisfy the multihomed host case.
141  */
142 nss_status_t
143 __nss_files_XY_hostbyname(be, args, filter, type)
144 	files_backend_ptr_t be;
145 	nss_XbyY_args_t *args;
146 	const char *filter;		/* hint for name string */
147 	int type;
148 {
149 	nss_status_t res;
150 	int parsestat;
151 	char *first;
152 	char *last;
153 	int i, nhosts = 0;
154 	struct hostent he, *hp, *thp;
155 	in_addr_t taddr[MAXADDRS];
156 	struct in6_addr taddr6[MAXADDRS];
157 	char *abuf = 0;		/* alias buffer */
158 	char *abuf_start = 0, *abuf_end;
159 	int	(*func)();
160 
161 	if (be->buf == 0 &&
162 		(be->buf = malloc(be->minbuf)) == 0) {
163 		return (NSS_UNAVAIL);
164 	}
165 
166 	if (be->f == 0) {
167 		if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS)
168 			return (res);
169 	}
170 
171 	res = NSS_NOTFOUND;
172 	args->erange = 0;
173 	args->returnval = (char *)0;
174 	hp = thp = (struct hostent *)args->buf.result;
175 
176 	for (;;) {
177 		char *instr = be->buf;
178 		int linelen;
179 
180 		if ((linelen = _nss_files_read_line(be->f,
181 		    instr, be->minbuf)) < 0) {
182 			break;		/* EOF */
183 		}
184 
185 		/*
186 		 * This check avoids a malloc()/free() for the common
187 		 * case. Also, if we're trying to match an alias and an
188 		 * already matched entry doesn't share a canonical name
189 		 * with the current one, bail.
190 		 */
191 		if (nhosts == 0 && strcasestr(instr, filter) == 0) {
192 			continue;
193 		}
194 
195 		if ((last = strchr(instr, '#')) == 0)
196 			last = instr + linelen;
197 		*last-- = '\0';
198 		for (first = instr;  isspace(*first);  first++)
199 			;
200 		/* Ignore blank and comment lines */
201 		if (*first == '\0')
202 			continue;
203 
204 		while (isspace(*last))
205 			--last;
206 		linelen = last - first + 1;
207 		if (first != instr)
208 			instr = first;
209 
210 		if (nhosts && strcasestr(instr, hp->h_name) == 0) {
211 			break;
212 		}
213 		/*
214 		 * If we've already matched once and have a possible match
215 		 * on this line, copy the aliases where they're safe from
216 		 * being overwritten when we look at the next entry. They're
217 		 * saved as a string of blank separated names for the alias
218 		 * parser. On errors, we return failure whether or not we
219 		 * have already obtained a valid address.
220 		 */
221 		if (nhosts == 1 && !abuf) {
222 			abuf = malloc(args->buf.buflen);
223 			if (abuf == NULL) {
224 				res = NSS_UNAVAIL;
225 				break;
226 			}
227 			abuf_start = &abuf[0];
228 			abuf_end = abuf_start + args->buf.buflen;
229 			if (abuf + strlen(hp->h_name) + 1 > abuf_end) {
230 				free(abuf_start);
231 				abuf = NULL;
232 				args->erange = 1;
233 				res = NSS_NOTFOUND;
234 				break;
235 			}
236 			(void) strcpy(abuf, hp->h_name);
237 			abuf += strlen(hp->h_name);
238 			*abuf++ = ' ';
239 			abuf = do_aliases(hp, abuf, abuf_start, abuf_end);
240 			if (abuf == NULL) {
241 				args->erange = 1;
242 				res = NSS_NOTFOUND;
243 				break;
244 			}
245 		}
246 		func = args->str2ent;
247 		parsestat = (*func)(instr, linelen, thp,
248 		    args->buf.buffer, args->buf.buflen);
249 
250 		if (parsestat != NSS_STR_PARSE_SUCCESS) {
251 			if (parsestat == NSS_STR_PARSE_ERANGE)
252 				args->erange = 1;
253 			continue;
254 		}
255 
256 		/*
257 		 * Still need to check, strcasestr() above is just a hint.
258 		 */
259 
260 		if (type == thp->h_addrtype)
261 		if (check_name(thp, args)) {
262 			if (type == AF_INET)
263 				taddr[nhosts++] =
264 				(*(in_addr_t *)thp->h_addr_list[0]);
265 			else {
266 				memcpy(&taddr6[nhosts++], thp->h_addr_list[0],
267 				sizeof (struct in6_addr));
268 			}
269 
270 
271 			if (nhosts == 1) {
272 				res = NSS_SUCCESS;
273 				args->returnval = args->buf.result;
274 				thp = &he;	/* switch to tmp hostent */
275 				continue;
276 			}
277 			if (nhosts >= MAXADDRS)
278 				break;
279 			abuf = do_aliases(thp, abuf, abuf_start, abuf_end);
280 			if (abuf == NULL) {
281 				args->erange = 1;
282 				res = NSS_NOTFOUND;
283 				break;
284 			}
285 		} else if (abuf &&
286 		    strcasecmp(hp->h_name, thp->h_name) == 0) {
287 			/*
288 			 * This line didn't have the requested name but
289 			 * is part of the same multihomed host (i.e. it
290 			 * has the same canonical name as the previous
291 			 * line), so march on...
292 			 */
293 			continue;
294 		} else if (nhosts) {
295 			break;
296 		}
297 	}
298 
299 	if (abuf) {
300 		struct in_addr *addrp;
301 		struct in6_addr *addrp6;
302 
303 		if (type == AF_INET) {
304 			addrp = (struct in_addr *)(ROUND_DOWN(args->buf.buffer +
305 			    args->buf.buflen, sizeof (*addrp)));
306 			hp->h_addr_list = (char **)(ROUND_DOWN(addrp -
307 			    ((nhosts + 1) * sizeof (char *) +
308 			    (nhosts * sizeof (*addrp))), sizeof (char *)));
309 			for (i = 0, --addrp; i < nhosts; i++, --addrp) {
310 				(*(in_addr_t *)addrp) = taddr[i];
311 				hp->h_addr_list[i] = (char *)addrp;
312 			}
313 		} else {
314 			addrp6 = (struct in6_addr *)
315 			(ROUND_DOWN(args->buf.buffer + args->buf.buflen,
316 			sizeof (*addrp6)));
317 			hp->h_addr_list = (char **)(ROUND_DOWN(addrp6 -
318 			    ((nhosts + 1) * sizeof (char *) +
319 			    (nhosts * sizeof (*addrp6))), sizeof (char *)));
320 			for (i = 0, --addrp6; i < nhosts; i++, --addrp6) {
321 				memcpy(addrp6, &taddr6[i],
322 				sizeof (struct in6_addr));
323 				hp->h_addr_list[i] = (char *)addrp6;
324 			}
325 		}
326 
327 		hp->h_addr_list[nhosts] = 0;
328 		hp->h_aliases = _nss_netdb_aliases(abuf_start,
329 		    abuf - abuf_start, args->buf.buffer,
330 		    (char *)hp->h_addr_list - args->buf.buffer);
331 		if (hp->h_aliases == 0) {
332 			args->erange = 1;
333 			res = NSS_STR_PARSE_ERANGE;
334 		} else {
335 			hp->h_name = hp->h_aliases[0];
336 			hp->h_aliases++;
337 		}
338 		free(abuf_start);
339 	}
340 
341 	/*
342 	 * stayopen is set to 0 by default in order to close the opened
343 	 * file.  Some applications may break if it is set to 1.
344 	 */
345 	if (!args->stayopen)
346 		(void) _nss_files_endent(be, 0);
347 
348 	return (res);
349 }
350 
351 /*
352  * A case-insensitive version of strstr().
353  */
354 static char *
355 strcasestr(as1, as2)
356 	char *as1;
357 	char *as2;
358 {
359 	int c2;
360 	register char *tptr;
361 	register char *s1, *s2;
362 
363 	s1 = as1;
364 	s2 = as2;
365 
366 	if (s2 == NULL || *s2 == '\0')
367 		return (0);
368 
369 	while (*s1) {
370 		if (tolower(*s1++) == tolower(c2 = *s2)) {
371 			tptr = s1;
372 			while ((tolower(c2 = *++s2) ==
373 			    tolower(*s1++)) && c2 != 0)
374 				;
375 			if (c2 == 0)
376 				return ((char *)tptr - 1);
377 			s1 = tptr;
378 			s2 = as2;
379 		}
380 	}
381 	return (0);
382 }
383 
384 
385 static char *
386 do_aliases(hp, abuf, start, end)
387 	struct hostent *hp;
388 	char *abuf;
389 	char *start;
390 	char *end;
391 {
392 	char **cp;
393 
394 	for (cp = hp->h_aliases; cp && *cp && **cp; cp++) {
395 		size_t len;
396 
397 		len = strlen(*cp);
398 		if (abuf+len+1 >= end) {
399 			free(start);
400 			return ((char *)0);
401 		}
402 		(void) strcpy(abuf, *cp);
403 		abuf += len;
404 		*abuf++ = ' ';
405 	}
406 	*abuf = '\0';
407 
408 	return (abuf);
409 }
410 
411 
412 /*
413  * This is a copy of a routine in libnsl/nss/netdir_inet.c.  It is
414  * here because /etc/lib/nss_files.so.1 cannot call routines
415  * in libnsl.  Care should be taken to keep the two copies in sync.
416  */
417 int
418 __nss_files_2herrno(nsstat)
419 	nss_status_t nsstat;
420 {
421 	switch (nsstat) {
422 	case NSS_SUCCESS:
423 		/* no macro-defined success code for h_errno */
424 		return (0);
425 	case NSS_NOTFOUND:
426 		return (HOST_NOT_FOUND);
427 	case NSS_TRYAGAIN:
428 		return (TRY_AGAIN);
429 	case NSS_UNAVAIL:
430 		return (NO_RECOVERY);
431 	}
432 	/* anything else */
433 	return (NO_RECOVERY);
434 }
435