nis_subr.c revision 11262:b7ebfbf2359e
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 */
65int __nisipbufsize = 8192;
66
67
68/*
69 * Static function prototypes.
70 */
71static struct local_names *__get_local_names(void);
72static 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 */
78name_pos
79nis_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
135struct principal_list {
136	uid_t uid;
137	char principal[LN_BUFSIZE];
138	struct principal_list *next;
139};
140
141
142struct 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
150static mutex_t ln_lock = DEFAULTMUTEX; /* lock level 2 */
151static struct local_names *ln = NULL;
152static struct local_names *__get_local_names1();
153
154static 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
166static 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 */
247nis_name
248nis_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 */
271nis_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 */
318nis_name
319nis_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 */
339nis_name
340nis_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
360static pthread_key_t buf_key = PTHREAD_ONCE_KEY_NP;
361static char buf_main[LN_BUFSIZE];
362
363nis_name
364nis_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
384nis_name
385nis_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
434nis_name
435nis_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 */
453nis_name
454nis_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 */
471nis_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 */
490nis_name
491nis_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 */
512void
513nis_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
521static void
522destroy_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 */
540nis_object *
541nis_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
588nis_object *
589nis_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 */
604nis_object *
605nis_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
636int
637nis_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 */
670static 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
783extern int __can_use_af(sa_family_t af);
784
785CLIENT *
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
840static mutex_t __nis_ss_used_lock = DEFAULTMUTEX; /* lock level 3 */
841int	__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 */
853void *
854nis_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