xref: /illumos-gate/usr/src/lib/libnsl/nis/gen/nis_subr.c (revision 2bbdd445a21f9d61f4a0ca0faf05d5ceb2bd91f3)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 /*
29  * This module contains the subroutines used by the server to manipulate
30  * objects and names.
31  */
32 #include "mt.h"
33 #include <pwd.h>
34 #include <grp.h>
35 #include <syslog.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <sys/time.h>
42 #include <sys/fcntl.h>
43 #include <netinet/in.h>
44 #include <rpc/rpc.h>	/* Must be ahead of rpcb_clnt.h */
45 #include <rpc/svc.h>
46 #include <tiuser.h>
47 #include <netconfig.h>
48 #include <netdir.h>
49 #include <rpc/rpcb_clnt.h>
50 #include <rpc/pmap_clnt.h>
51 #include <rpcsvc/nis.h>
52 #include <rpcsvc/nis_dhext.h>
53 #include "nis_clnt.h"
54 #include <sys/systeminfo.h>
55 #include <nsswitch.h>
56 
57 #define	MAXIPRINT	(11)	/* max length of printed integer */
58 /*
59  * send and receive buffer size used for clnt_tli_create if not specified.
60  * This is only used for "UDP" connection.
61  * This limit can be changed from the application if this value is too
62  * small for the application.  To use the maximum value for the transport,
63  * set this value to 0.
64  */
65 int __nisipbufsize = 8192;
66 
67 
68 /*
69  * Static function prototypes.
70  */
71 static struct local_names *__get_local_names(void);
72 static char *__map_addr(struct netconfig *, char *, rpcprog_t, rpcvers_t);
73 
74 /*
75  * nis_dir_cmp() -- the results can be read as:
76  * 	"Name 'n1' is a $result than name 'n2'"
77  */
78 name_pos
79 nis_dir_cmp(
80 	nis_name	n1,
81 	nis_name	n2)	/* See if these are the same domain */
82 {
83 	size_t		l1, l2;
84 	name_pos	result;
85 
86 	if ((n1 == NULL) || (n2 == NULL))
87 		return (BAD_NAME);
88 
89 	l1 = strlen(n1);
90 	l2 = strlen(n2);
91 
92 	/* In this routine we're lenient and don't require a trailing '.' */
93 	/*   so we need to ignore it if it does appear.			  */
94 	/* ==== That's what the previous version did so this one does	  */
95 	/*   too, but why?  Is this inconsistent with rest of system?	  */
96 	if (l1 != 0 && n1[l1 - 1] == '.') {
97 		--l1;
98 	}
99 	if (l2 != 0 && n2[l2 - 1] == '.') {
100 		--l2;
101 	}
102 
103 	if (l1 > l2) {
104 		result = LOWER_NAME;
105 	} else if (l1 == l2) {
106 		result = SAME_NAME;
107 	} else /* (l1 < l2); swap l1/l2 and n1/n2 */ {
108 		nis_name	ntmp;
109 		size_t		ltmp;
110 		ntmp = n1; n1 = n2; n2 = ntmp;
111 		ltmp = l1; l1 = l2; l2 = ltmp;
112 
113 		result = HIGHER_NAME;
114 	}
115 
116 	/* Now l1 >= l2 in all cases */
117 	if (l2 == 0) {
118 		/* Special case for n2 == "." or "" */
119 		return (result);
120 	}
121 	if (l1 > l2) {
122 		n1 += l1 - l2;
123 		if (n1[-1] != '.') {
124 			return (NOT_SEQUENTIAL);
125 		}
126 	}
127 	if (strncasecmp(n1, n2, l2) == 0) {
128 		return (result);
129 	}
130 	return (NOT_SEQUENTIAL);
131 }
132 
133 #define	LN_BUFSIZE	(size_t)1024
134 
135 struct principal_list {
136 	uid_t uid;
137 	char principal[LN_BUFSIZE];
138 	struct principal_list *next;
139 };
140 
141 
142 struct local_names {
143 	char domain[LN_BUFSIZE];
144 	char host[LN_BUFSIZE];
145 	char *rpcdomain;
146 	struct principal_list *principal_map;
147 	char group[LN_BUFSIZE];
148 };
149 
150 static mutex_t ln_lock = DEFAULTMUTEX; /* lock level 2 */
151 static struct local_names *ln = NULL;
152 static struct local_names *__get_local_names1();
153 
154 static struct local_names *
155 __get_local_names(void)
156 {
157 	struct local_names *names;
158 
159 	sig_mutex_lock(&ln_lock);
160 	names = __get_local_names1();
161 	sig_mutex_unlock(&ln_lock);
162 	return (names);
163 }
164 
165 
166 static struct local_names *
167 __get_local_names1(void)
168 {
169 	char		*t;
170 
171 	if (ln != NULL) {
172 		/* Second and subsequent calls go this way */
173 		return (ln);
174 	}
175 	/* First call goes this way */
176 	ln = calloc(1, sizeof (*ln));
177 	if (ln == NULL) {
178 		syslog(LOG_ERR, "__get_local_names: Out of heap.");
179 		return (NULL);
180 	}
181 	ln->principal_map = NULL;
182 
183 	if (sysinfo(SI_SRPC_DOMAIN, ln->domain, LN_BUFSIZE) < 0)
184 		return (ln);
185 	/* If no dot exists, add one. */
186 	if (ln->domain[strlen(ln->domain)-1] != '.')
187 		(void) strcat(ln->domain, ".");
188 	if (sysinfo(SI_HOSTNAME, ln->host, LN_BUFSIZE) < 0)
189 		return (ln);
190 
191 	/*
192 	 * Check for fully qualified hostname.  If it's a fully qualified
193 	 * hostname, strip off the domain part.  We always use the local
194 	 * domainname for the host principal name.
195 	 */
196 	t = strchr(ln->host, '.');
197 	if (t)
198 		*t = 0;
199 	if (ln->domain[0] != '.')
200 		(void) strcat(ln->host, ".");
201 	ln->rpcdomain = strdup(ln->domain);
202 	(void) strcat(ln->host, ln->domain);
203 
204 	t = getenv("NIS_GROUP");
205 	if (t == NULL) {
206 		ln->group[0] = '\0';
207 	} else {
208 		size_t maxlen = LN_BUFSIZE-1;	/* max chars to copy */
209 		char *temp;			/* temp marker */
210 
211 		/*
212 		 * Copy <= maximum characters from NIS_GROUP; strncpy()
213 		 * doesn't terminate, so we do that manually. #1223323
214 		 * Also check to see if it's "".  If it's the null string,
215 		 * we return because we don't want to add ".domain".
216 		 */
217 		(void) strncpy(ln->group, t, maxlen);
218 		if (strcmp(ln->group, "") == 0) {
219 			return (ln);
220 		}
221 		ln->group[maxlen] = '\0';
222 
223 		/* Is the group name somewhat fully-qualified? */
224 		temp = strrchr(ln->group, '.');
225 
226 		/* If not, we need to add ".domain" to the group */
227 		if ((temp == NULL) || (temp[1] != '\0')) {
228 
229 			/* truncate to make room for ".domain" */
230 			ln->group[maxlen - (strlen(ln->domain)+1)] = '\0';
231 
232 			/* concat '.' if domain doesn't already have it */
233 			if (ln->domain[0] != '.') {
234 				(void) strcat(ln->group, ".");
235 			}
236 			(void) strcat(ln->group, ln->domain);
237 		}
238 	}
239 	return (ln);
240 }
241 
242 /*
243  * nis_local_group()
244  *
245  * Return's the group name of the current user.
246  */
247 nis_name
248 nis_local_group(void)
249 {
250 	struct local_names	*ln = __get_local_names();
251 
252 	/* LOCK NOTE: Warning, after initialization, "ln" is expected	 */
253 	/* to stay constant, So no need to lock here. If this assumption */
254 	/* is changed, this code must be protected.			 */
255 	if (!ln)
256 		return (NULL);
257 	return (ln->group);
258 }
259 
260 /*
261  * __nis_nextsep_of()
262  *
263  * This internal funtion will accept a pointer to a NIS name string and
264  * return a pointer to the next separator occurring in it (it will point
265  * just past the first label).  It allows for labels to be "quoted" to
266  * prevent the the dot character within them to be interpreted as a
267  * separator, also the quote character itself can be quoted by using
268  * it twice.  If the the name contains only one label and no trailing
269  * dot character, a pointer to the terminating NULL is returned.
270  */
271 nis_name
272 __nis_nextsep_of(char *s)
273 {
274 	char	*d;
275 	int	in_quotes = FALSE, quote_quote = FALSE;
276 
277 	if (!s)
278 		return (NULL);
279 
280 	for (d = s; (in_quotes && (*d != '\0')) ||
281 	    (!in_quotes && (*d != '.') && (*d != '\0')); d++) {
282 		if (quote_quote && in_quotes && (*d != '"')) {
283 			quote_quote = FALSE;
284 			in_quotes = FALSE;
285 			if (*d == '.')
286 				break;
287 		} else if (quote_quote && in_quotes && (*d == '"')) {
288 			quote_quote = FALSE;
289 		} else if (quote_quote && (*d != '"')) {
290 			quote_quote = FALSE;
291 			in_quotes = TRUE;
292 		} else if (quote_quote && (*d == '"')) {
293 			quote_quote = FALSE;
294 		} else if (in_quotes && (*d == '"')) {
295 			quote_quote = TRUE;
296 		} else if (!in_quotes && (*d == '"')) {
297 			quote_quote = TRUE;
298 		}
299 	}
300 
301 	if (quote_quote || in_quotes) {
302 		syslog(LOG_DEBUG, "__nis_nextsep_of: "
303 		    "Mismatched quotes in %s", s);
304 	}
305 
306 	return (d);
307 }
308 
309 /*
310  * nis_domain_of()
311  *
312  * This internal funtion will accept a pointer to a NIS name string and
313  * return a pointer to the "domain" part of it.
314  *
315  * ==== We don't need nis_domain_of_r(), but should we provide one for
316  *	uniformity?
317  */
318 nis_name
319 nis_domain_of(char *s)
320 {
321 	char	*d;
322 
323 	d = __nis_nextsep_of(s);
324 	if (d == NULL)
325 		return (NULL);
326 	if (*d == '.')
327 		d++;
328 	if (*d == '\0')	/* Don't return a zero length string */
329 		return ("."); /* return root domain instead */
330 	return (d);
331 }
332 
333 
334 /*
335  * nis_leaf_of()
336  *
337  * Returns the first label of a name. (other half of __domain_of)
338  */
339 nis_name
340 nis_leaf_of_r(
341 	const nis_name	s,
342 	char		*buf,
343 	size_t		bufsize)
344 {
345 	size_t		nchars;
346 	const char	*d = __nis_nextsep_of((char *)s);
347 
348 	if (d == 0) {
349 		return (0);
350 	}
351 	nchars = d - s;
352 	if (bufsize < nchars + 1) {
353 		return (0);
354 	}
355 	(void) strncpy(buf, s, nchars);
356 	buf[nchars] = '\0';
357 	return (buf);
358 }
359 
360 static pthread_key_t buf_key = PTHREAD_ONCE_KEY_NP;
361 static char buf_main[LN_BUFSIZE];
362 
363 nis_name
364 nis_leaf_of(char *s)
365 {
366 	char *buf = thr_main()? buf_main :
367 	    thr_get_storage(&buf_key, LN_BUFSIZE, free);
368 
369 	if (buf == NULL)
370 		return (NULL);
371 	return (nis_leaf_of_r(s, buf,  LN_BUFSIZE));
372 }
373 
374 /*
375  * nis_name_of()
376  * This internal function will remove from the NIS name, the domain
377  * name of the current server, this will leave the unique part in
378  * the name this becomes the "internal" version of the name. If this
379  * function returns NULL then the name we were given to resolve is
380  * bad somehow.
381  * NB: Uses static storage and this is a no-no with threads. XXX
382  */
383 
384 nis_name
385 nis_name_of_r(
386 	char	*s,	/* string with the name in it. */
387 	char		*buf,
388 	size_t		bufsize)
389 {
390 	char			*d;
391 	struct local_names 	*ln = __get_local_names();
392 	size_t			dl, sl;
393 	name_pos		p;
394 
395 #ifdef lint
396 	bufsize = bufsize;
397 #endif /* lint */
398 	if ((!s) || (!ln))
399 		return (NULL);		/* No string, this can't continue */
400 
401 	d  = &(ln->domain[0]);
402 	dl = strlen(ln->domain); 	/* _always dot terminated_   */
403 
404 	sl = strlen(s);
405 	if (sl >= bufsize || (s[sl-1] != '.' && sl >= bufsize-1))
406 		return (NULL);
407 	(void) strcpy(buf, s);		/* Make a private copy of 's'   */
408 	if (buf[sl-1] != '.') {	/* Add a dot if necessary.  */
409 		(void) strcat(buf, ".");
410 		sl++;
411 	}
412 
413 	if (dl == 1) {			/* We're the '.' directory   */
414 		buf[sl-1] = '\0';	/* Lose the 'dot'	  */
415 		return (buf);
416 	}
417 
418 	p = nis_dir_cmp(buf, d);
419 
420 	/* 's' is above 'd' in the tree */
421 	if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL) || (p == SAME_NAME))
422 		return (NULL);
423 
424 	/* Insert a NUL where the domain name starts in the string */
425 	buf[(sl - dl) - 1] = '\0';
426 
427 	/* Don't return a zero length name */
428 	if (buf[0] == '\0')
429 		return (NULL);
430 
431 	return (buf);
432 }
433 
434 nis_name
435 nis_name_of(
436 	char	*s)	/* string with the name in it. */
437 {
438 	char *buf = thr_main()? buf_main :
439 	    thr_get_storage(&buf_key, LN_BUFSIZE, free);
440 
441 	if (!buf)
442 		return (NULL);
443 	return (nis_name_of_r(s, buf,  LN_BUFSIZE));
444 }
445 
446 
447 
448 /*
449  * nis_local_directory()
450  *
451  * Return a pointer to a string with the local directory name in it.
452  */
453 nis_name
454 nis_local_directory(void)
455 {
456 	struct local_names	*ln = __get_local_names();
457 
458 	/* LOCK NOTE: Warning, after initialization, "ln" is expected	 */
459 	/* to stay constant, So no need to lock here. If this assumption */
460 	/* is changed, this code must be protected.			 */
461 	if (ln == NULL)
462 		return (NULL);
463 	return (ln->domain);
464 }
465 
466 /*
467  * __nis_rpc_domain()
468  *
469  * Return a pointer to a string with the rpc domain name in it.
470  */
471 nis_name
472 __nis_rpc_domain()
473 {
474 	struct local_names	*ln = __get_local_names();
475 
476 	/* LOCK NOTE: Warning, after initialization, "ln" is expected	 */
477 	/* to stay constant, So no need to lock here. If this assumption */
478 	/* is changed, this code must be protected.			 */
479 	if (ln == NULL)
480 		return (NULL);
481 	return (ln->rpcdomain);
482 }
483 
484 
485 /*
486  * nis_local_host()
487  * Generate the principal name for this host, "hostname"+"domainname"
488  * unless the hostname already has "dots" in its name.
489  */
490 nis_name
491 nis_local_host(void)
492 {
493 	struct local_names	*ln = __get_local_names();
494 
495 	/* LOCK NOTE: Warning, after initialization, "ln" is expected	 */
496 	/* to stay constant, So no need to lock here. If this assumption */
497 	/* is changed, this code must be protected.			 */
498 	if (ln == NULL)
499 		return (NULL);
500 
501 	return (ln->host);
502 }
503 
504 /*
505  * nis_destroy_object()
506  * This function takes a pointer to a NIS object and deallocates it. This
507  * is the inverse of __clone_object below. It must be able to correctly
508  * deallocate partially allocated objects because __clone_object will call
509  * it if it runs out of memory and has to abort. Everything is freed,
510  * INCLUDING the pointer that is passed.
511  */
512 void
513 nis_destroy_object(nis_object *obj)	/* The object to clone */
514 {
515 	if (obj == 0)
516 		return;
517 	xdr_free(xdr_nis_object, (char *)obj);
518 	free(obj);
519 } /* nis_destroy_object */
520 
521 static void
522 destroy_nis_sdata(void *p)
523 {
524 	struct nis_sdata	*ns = p;
525 
526 	if (ns->buf != 0)
527 		free(ns->buf);
528 	free(ns);
529 }
530 
531 /* XXX Why are these static ? */
532 /* static XDR in_xdrs, out_xdrs; */
533 
534 
535 /*
536  * __clone_object_r()
537  * This function takes a pointer to a NIS object and clones it. This
538  * duplicate object is now available for use in the local context.
539  */
540 nis_object *
541 nis_clone_object_r(
542 	nis_object	*obj,	/* The object to clone */
543 	nis_object	*dest,	/* Use this pointer if non-null */
544 	struct nis_sdata *clone_buf_ptr)
545 {
546 	nis_object	*result; /* The clone itself */
547 	int		status; /* a counter variable */
548 	XDR		in_xdrs, out_xdrs;
549 
550 	if (!nis_get_static_storage(clone_buf_ptr, 1,
551 	    xdr_sizeof(xdr_nis_object, obj)))
552 		return (NULL);
553 
554 	(void) memset(&in_xdrs, 0, sizeof (in_xdrs));
555 	(void) memset(&out_xdrs, 0, sizeof (out_xdrs));
556 	xdrmem_create(&in_xdrs, clone_buf_ptr->buf, clone_buf_ptr->size,
557 	    XDR_ENCODE);
558 	xdrmem_create(&out_xdrs, clone_buf_ptr->buf, clone_buf_ptr->size,
559 	    XDR_DECODE);
560 
561 	/* Allocate a basic NIS object structure */
562 	if (dest) {
563 		(void) memset(dest, 0, sizeof (nis_object));
564 		result = dest;
565 	} else
566 		result = calloc(1, sizeof (nis_object));
567 
568 	if (result == NULL)
569 		return (NULL);
570 
571 	/* Encode our object into the clone buffer */
572 	(void) xdr_setpos(&in_xdrs, 0);
573 	status = xdr_nis_object(&in_xdrs, obj);
574 	if (status == FALSE)
575 		return (NULL);
576 
577 	/* Now decode the buffer into our result pointer ... */
578 	(void) xdr_setpos(&out_xdrs, 0);
579 	status = xdr_nis_object(&out_xdrs, result);
580 	if (status == FALSE)
581 		return (NULL);
582 
583 	/* presto changeo, a new object */
584 	return (result);
585 } /* __clone_object_r */
586 
587 
588 nis_object *
589 nis_clone_object(
590 	nis_object	*obj,	/* The object to clone */
591 	nis_object	*dest)	/* Use this pointer if non-null */
592 {
593 	static pthread_key_t clone_buf_key = PTHREAD_ONCE_KEY_NP;
594 	static struct nis_sdata clone_buf_main;
595 	struct nis_sdata *clone_buf_ptr;
596 
597 	clone_buf_ptr = thr_main()? &clone_buf_main :
598 	    thr_get_storage(&clone_buf_key, sizeof (struct nis_sdata),
599 	    destroy_nis_sdata);
600 	return (nis_clone_object_r(obj, dest, clone_buf_ptr));
601 } /* __clone_object */
602 
603 /* Various subroutines used by the server code */
604 nis_object *
605 nis_read_obj(char *f)	/* name of the object to read */
606 {
607 	FILE	*rootfile;
608 	int	status;	/* Status of the XDR decoding */
609 	XDR	xdrs;	/* An xdr stream handle */
610 	nis_object	*res;
611 
612 	res = calloc(1, sizeof (nis_object));
613 	if (!res)
614 		return (NULL);
615 
616 	rootfile = fopen(f, "rF");
617 	if (rootfile == NULL) {
618 		/* This is ok if we are the root of roots. */
619 		free(res);
620 		return (NULL);
621 	}
622 	/* Now read in the object */
623 	xdrstdio_create(&xdrs, rootfile, XDR_DECODE);
624 	status = xdr_nis_object(&xdrs, res);
625 	xdr_destroy(&xdrs);
626 	(void) fclose(rootfile);
627 	if (!status) {
628 		syslog(LOG_ERR, "Object file %s is corrupt!", f);
629 		xdr_free(xdr_nis_object, (char *)res);
630 		free(res);
631 		return (NULL);
632 	}
633 	return (res);
634 }
635 
636 int
637 nis_write_obj(
638 	char	*f,	/* name of the object to read */
639 	nis_object *o)	/* The object to write */
640 {
641 	FILE	*rootfile;
642 	int	status;	/* Status of the XDR decoding */
643 	XDR	xdrs;	/* An xdr stream handle */
644 
645 	rootfile = fopen(f, "wF");
646 	if (rootfile == NULL) {
647 		return (0);
648 	}
649 	/* Now encode the object */
650 	xdrstdio_create(&xdrs, rootfile, XDR_ENCODE);
651 	status = xdr_nis_object(&xdrs, o);
652 	xdr_destroy(&xdrs);
653 	(void) fclose(rootfile);
654 	return (status);
655 }
656 
657 /*
658  * Transport INDEPENDENT RPC code. This code assumes you
659  * are using the new RPC/tli code and will build
660  * a ping handle on top of a datagram transport.
661  */
662 
663 /*
664  * __map_addr()
665  *
666  * This is our internal function that replaces rpcb_getaddr(). We
667  * build our own to prevent calling netdir_getbyname() which could
668  * recurse to the nameservice.
669  */
670 static char *
671 __map_addr(
672 	struct netconfig	*nc,		/* Our transport	*/
673 	char			*uaddr,		/* RPCBIND address */
674 	rpcprog_t		prog,		/* Name service Prog */
675 	rpcvers_t		ver)
676 {
677 	CLIENT *client;
678 	RPCB 		parms;		/* Parameters for RPC binder	  */
679 	enum clnt_stat	clnt_st;	/* Result from the rpc call	  */
680 	char 		*ua = NULL;	/* Universal address of service	  */
681 	char		*res = NULL;	/* Our result to the parent	  */
682 	struct timeval	tv;		/* Timeout for our rpcb call	  */
683 	int		ilen, olen;	/* buffer length for clnt_tli_create */
684 
685 	/*
686 	 * If using "udp", use __nisipbufsize if inbuf and outbuf are set to 0.
687 	 */
688 	if (strcmp(NC_UDP, nc->nc_proto) == 0) {
689 			/* for udp only */
690 		ilen = olen = __nisipbufsize;
691 	} else {
692 		ilen = olen = 0;
693 	}
694 	client = __nis_clnt_create(RPC_ANYFD, nc, uaddr, 0, 0,
695 	    RPCBPROG, RPCBVERS, ilen, olen);
696 	if (!client)
697 		return (NULL);
698 
699 	(void) clnt_control(client, CLSET_FD_CLOSE, NULL);
700 
701 	/*
702 	 * Now make the call to get the NIS service address.
703 	 * We set the retry timeout to 3 seconds so that we
704 	 * will retry a few times.  Retries should be rare
705 	 * because we are usually only called when we know
706 	 * a server is available.
707 	 */
708 	tv.tv_sec = 3;
709 	tv.tv_usec = 0;
710 	(void) clnt_control(client, CLSET_RETRY_TIMEOUT, (char *)&tv);
711 
712 	tv.tv_sec = 10;
713 	tv.tv_usec = 0;
714 	parms.r_prog = prog;
715 	parms.r_vers = ver;
716 	parms.r_netid = nc->nc_netid;	/* not needed */
717 	parms.r_addr = "";	/* not needed; just for xdring */
718 	parms.r_owner = "";	/* not needed; just for xdring */
719 	clnt_st = clnt_call(client, RPCBPROC_GETADDR, xdr_rpcb, (char *)&parms,
720 	    xdr_wrapstring, (char *)&ua, tv);
721 
722 	if (clnt_st == RPC_SUCCESS) {
723 		clnt_destroy(client);
724 		if (*ua == '\0') {
725 			free(ua);
726 			return (NULL);
727 		}
728 		res = strdup(ua);
729 		xdr_free(xdr_wrapstring, (char *)&ua);
730 		return (res);
731 	} else if (((clnt_st == RPC_PROGVERSMISMATCH) ||
732 	    (clnt_st == RPC_PROGUNAVAIL)) &&
733 	    (strcmp(nc->nc_protofmly, NC_INET) == 0)) {
734 		/*
735 		 * version 3 not available. Try version 2
736 		 * The assumption here is that the netbuf
737 		 * is arranged in the sockaddr_in
738 		 * style for IP cases.
739 		 *
740 		 * Note:	If the remote host doesn't support version 3,
741 		 *		we assume it doesn't know IPv6 either.
742 		 */
743 		ushort_t 		port;
744 		struct sockaddr_in	*sa;
745 		struct netbuf 		remote;
746 		int			protocol;
747 		char			buf[32];
748 
749 		(void) clnt_control(client, CLGET_SVC_ADDR, (char *)&remote);
750 		/* LINTED pointer cast */
751 		sa = (struct sockaddr_in *)(remote.buf);
752 		protocol = strcmp(nc->nc_proto, NC_TCP) ?
753 		    IPPROTO_UDP : IPPROTO_TCP;
754 		port = (ushort_t)pmap_getport(sa, prog, ver, protocol);
755 
756 		if (port != 0) {
757 			port = htons(port);
758 			(void) sprintf(buf, "%d.%d.%d.%d.%d.%d",
759 			    (sa->sin_addr.s_addr >> 24) & 0xff,
760 			    (sa->sin_addr.s_addr >> 16) & 0xff,
761 			    (sa->sin_addr.s_addr >>  8) & 0xff,
762 			    (sa->sin_addr.s_addr) & 0xff,
763 			    (port >> 8) & 0xff,
764 			    port & 0xff);
765 			res = strdup(buf);
766 		} else
767 			res = NULL;
768 		clnt_destroy(client);
769 		return (res);
770 	}
771 	if (clnt_st == RPC_TIMEDOUT)
772 		syslog(LOG_ERR, "NIS+ server not responding");
773 	else
774 		syslog(LOG_ERR, "NIS+ server could not be contacted: %s",
775 		    clnt_sperrno(clnt_st));
776 	clnt_destroy(client);
777 	return (NULL);
778 }
779 
780 
781 #define	MAX_EP (20)
782 
783 extern int __can_use_af(sa_family_t af);
784 
785 CLIENT *
786 __nis_clnt_create(int fd, struct netconfig *nc, char *uaddr,
787 			struct netbuf *addr, int domapaddr,
788 			int prog, int ver, int inbuf, int outbuf) {
789 
790 	char		*svc_addr;
791 	CLIENT		*clnt;
792 	int		freeaddr = 0;
793 
794 	/* Sanity check */
795 	if (nc == 0 || (addr == 0 && uaddr == 0)) {
796 		return (0);
797 	}
798 
799 	/*
800 	 * Check if we have a useable interface for this address family.
801 	 * This check properly belongs in RPC (or even further down),
802 	 * but until they provide it, we roll our own.
803 	 */
804 	if (__can_use_af((strcmp(nc->nc_protofmly, NC_INET6) == 0) ?
805 			AF_INET6 : AF_INET) == 0) {
806 		return (0);
807 	}
808 
809 	if (domapaddr) {
810 		svc_addr = __map_addr(nc, uaddr, prog, ver);
811 		if (svc_addr == 0)
812 			return (0);
813 		addr = uaddr2taddr(nc, svc_addr);
814 		freeaddr = 1;
815 		free(svc_addr);
816 	} else if (addr == 0) {
817 		addr = uaddr2taddr(nc, uaddr);
818 		freeaddr = 1;
819 	}
820 
821 	if (addr == 0) {
822 		return (0);
823 	}
824 
825 	clnt = clnt_tli_create(fd, nc, addr, prog, ver, outbuf, inbuf);
826 
827 	if (clnt) {
828 		if (clnt_control(clnt, CLGET_FD, (char *)&fd))
829 			/* make it "close on exec" */
830 			(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
831 		(void) clnt_control(clnt, CLSET_FD_CLOSE, NULL);
832 	}
833 
834 	if (freeaddr)
835 		netdir_free(addr, ND_ADDR);
836 
837 	return (clnt);
838 }
839 
840 static mutex_t __nis_ss_used_lock = DEFAULTMUTEX; /* lock level 3 */
841 int	__nis_ss_used = 0;
842 
843 /*
844  * nis_get_static_storage()
845  *
846  * This function is used by various functions in their effort to minimize the
847  * hassles of memory management in an RPC daemon. Because the service doesn't
848  * implement any hard limits, this function allows people to get automatically
849  * growing buffers that meet their storage requirements. It returns the
850  * pointer in the nis_sdata structure.
851  *
852  */
853 void *
854 nis_get_static_storage(
855 	struct nis_sdata	*bs,    /* User buffer structure */
856 	uint_t			el,	/* Sizeof elements	 */
857 	uint_t			nel)    /* Number of elements    */
858 {
859 	uint_t	sz;
860 
861 	sz = nel * el;
862 	if (!bs)
863 		return (NULL);
864 
865 	if (!bs->buf) {
866 		bs->buf = malloc(sz);
867 		if (!bs->buf)
868 			return (NULL);
869 		bs->size = sz;
870 		sig_mutex_lock(&__nis_ss_used_lock);
871 		__nis_ss_used += sz;
872 		sig_mutex_unlock(&__nis_ss_used_lock);
873 	} else if (bs->size < sz) {
874 		int	size_delta;
875 
876 		free(bs->buf);
877 		size_delta = - (bs->size);
878 		bs->buf = malloc(sz);
879 
880 		/* check the result of malloc() first   */
881 		/* then update the statistic.		*/
882 		if (!bs->buf)
883 			return (NULL);
884 		bs->size = sz;
885 		size_delta += sz;
886 		sig_mutex_lock(&__nis_ss_used_lock);
887 		__nis_ss_used += size_delta;
888 		sig_mutex_unlock(&__nis_ss_used_lock);
889 	}
890 
891 	(void) memset(bs->buf, 0, sz); /* SYSV version of bzero() */
892 	return (bs->buf);
893 }
894