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