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 (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27/*	  All Rights Reserved   */
28
29/*
30 * Portions of this source code were derived from Berkeley
31 * under license from the Regents of the University of
32 * California.
33 */
34#include "mt.h"
35#include "../rpc/rpc_mt.h"
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <sys/types.h>
40#include <sys/stat.h>
41#include <errno.h>
42#include <unistd.h>
43#include <rpc/rpc.h>
44#include <netconfig.h>
45#include <netdir.h>
46#include <syslog.h>
47#include "yp_b.h"
48#include <rpcsvc/yp_prot.h>
49#include <rpcsvc/ypclnt.h>
50#include <sys/tiuser.h>
51
52#define	BFSIZE	(YPMAXDOMAIN + 32)	/* size of binding file */
53int	 __ypipbufsize = 8192;		/* size used for clnt_tli_create */
54
55/* This should match the one in ypbind.c */
56
57extern int getdomainname(char *, int);
58
59static CLIENT *getclnt(rpcprog_t, rpcvers_t, struct netconfig *, int *);
60static struct dom_binding *load_dom_binding(struct ypbind_resp *, char *,
61    int *);
62static ypbind_resp *get_cached_domain(char *);
63static int get_cached_transport(struct netconfig *, int, char *, int);
64static int ypbind_running(int, int);
65static void set_rdev(struct dom_binding *);
66static int check_rdev(struct dom_binding *);
67
68static char nullstring[] = "";
69/*
70 * Time parameters when talking to the ypbind and pmap processes
71 */
72
73#define	YPSLEEPTIME	5		/* Time to sleep between tries */
74unsigned int _ypsleeptime = YPSLEEPTIME;
75
76/*
77 * Time parameters when talking to the ypserv process
78 */
79
80#ifdef  DEBUG
81#define	YPTIMEOUT	120		/* Total seconds for timeout */
82#define	YPINTER_TRY	60		/* Seconds between tries */
83#else
84#define	YPTIMEOUT	20		/* Total seconds for timeout */
85#define	YPINTER_TRY	5		/* Seconds between tries */
86#endif
87
88#define	MAX_TRIES_FOR_NEW_YP	1	/* Number of times we'll try to */
89					/* get a new YP server before   */
90					/* we'll settle for an old one. */
91struct timeval _ypserv_timeout = {
92	YPTIMEOUT,			/* Seconds */
93	0				/* Microseconds */
94	};
95
96static mutex_t			default_domain_lock = DEFAULTMUTEX;
97static char			*default_domain;
98
99/*
100 * The bound_domains_lock serializes all action in yp_unbind(), __yp_dobind(),
101 *   newborn(), check_binding() and laod_dom_binding(), not just the direct
102 *   manipulation of the bound_domains list.
103 * It also protects all of the fields within a domain binding except
104 *   the server_name field (which is protected by the server_name_lock).
105 * A better implementation might try to serialize each domain separately,
106 *   but normally we're only dealing with one domain (the default) anyway.
107 * To avoid one thread freeing a domain binding while another is using
108 *   the binding, we maintain a reference count for each binding.  The
109 *   reference count is incremented in __yp_dobind.  The thread calls
110 *   __yp_rel_binding() when it has finished using the binding (which
111 *   decrements the reference count).  If the reference count is non-zero
112 *   when a thread tries to free a binding, the need_free flag is set and
113 *   the free is delayed.  The __yp_rel_binding() routine checks the flag
114 *   and calls the free routine if the flag is set and the reference
115 *   count is zero.
116 */
117static mutex_t			bound_domains_lock = DEFAULTMUTEX;
118static struct dom_binding	*bound_domains; /* List of bound domains */
119
120
121/*
122 *  Must be called with bound_domains_lock held or with a dom_binding
123 *  that cannot be referenced by another thread.
124 */
125void
126free_dom_binding(struct dom_binding *p)
127{
128	if (p->ref_count != 0) {
129		p->need_free = 1;
130		return;
131	}
132	(void) check_rdev(p);
133	clnt_destroy(p->dom_client);
134	free(p->dom_domain);
135	free(p);
136}
137
138/*
139 * Attempts to find a dom_binding in the list at bound_domains having the
140 * domain name field equal to the passed domain name, and removes it if found.
141 * The domain-server binding will not exist after the call to this function.
142 * All resources associated with the binding will be freed.
143 *
144 * yp_unbind is MT-safe because it serializes on bound_domains_lock.
145 */
146
147static void
148__yp_unbind_nolock(char *domain)
149{
150	struct dom_binding *p;
151	struct dom_binding **prev;
152
153	if ((domain == NULL) || (strlen(domain) == 0)) {
154		return;
155	}
156
157	/*
158	 *  If we used a cache file to bind, then we will mark the
159	 *  cache bad.  This will cause a subsequent call to __yp_dobind
160	 *  to ignore the cache and talk to ypbind.  Otherwise, we
161	 *  have already gotten a binding by talking to ypbind and
162	 *  the binding is not good.
163	 *
164	 *  An optimization could be to check to see if the cache
165	 *  file has changed (ypbind is pointing at a new server) and
166	 *  reload the binding from it.  But that is too much work
167	 *  for now.
168	 */
169	for (prev = &bound_domains;  (p = *prev) != 0;  prev = &p->dom_pnext) {
170
171		if (strcmp(domain, p->dom_domain) == 0) {
172			if (!p->cache_bad) {
173				p->cache_bad = 1;
174				break;
175			}
176			*prev = p->dom_pnext;
177			free_dom_binding(p);
178			break;
179		}
180
181	}
182}
183
184
185void
186yp_unbind(char *domain)
187{
188	(void) mutex_lock(&bound_domains_lock);
189	__yp_unbind_nolock(domain);
190	(void) mutex_unlock(&bound_domains_lock);
191}
192
193
194/*
195 * This checks to see if this is a new process incarnation which has
196 * inherited bindings from a parent, and unbinds the world if so.
197 *
198 * MT-safe because it is only invoked from __yp_dobind(), which serializes
199 * all requests.
200 */
201static void
202newborn(void)
203{
204	static pid_t mypid;	/* Cached to detect forks */
205	pid_t testpid;
206	struct dom_binding *p, *q;
207
208	if ((testpid = getpid()) != mypid) {
209
210		mypid = testpid;
211
212		for (p = bound_domains;  p != 0;  p = q) {
213			q = p->dom_pnext;
214			free_dom_binding(p);
215		}
216		bound_domains = 0;
217	}
218}
219
220/*
221 * This checks that the socket for a domain which has already been bound
222 * hasn't been closed or changed under us.  If it has, unbind the domain
223 * without closing the socket, which may be in use by some higher level
224 * code.  This returns TRUE and points the binding parameter at the found
225 * dom_binding if the binding is found and the socket looks OK, and FALSE
226 * otherwise.
227 *
228 * MT-safe because it is only invoked from __yp_dobind(), which serializes
229 * all requests.
230 */
231static bool
232check_binding(char *domain, struct dom_binding **binding)
233{
234	struct dom_binding *pdomb;
235	struct ypbind_resp *ypbind_resp;
236	int status;
237
238	for (pdomb = bound_domains; pdomb != NULL; pdomb = pdomb->dom_pnext) {
239
240		if (strcmp(domain, pdomb->dom_domain) == 0) {
241		/*
242		 * XXX How do we really make sure the udp connection hasn't
243		 * changes under us ? If it happens and we can't detect it,
244		 * the appliction is doomed !
245		 * POLICY: Let nobody do a yp_bind or __yp_dobind explicitly
246		 * and forget to to yp_unbind it. All apps should go
247		 * through the standard yp_match/first etc. functions.
248		 */
249
250			*binding = pdomb;
251			return (TRUE);
252		}
253	}
254
255	/*
256	 *  We check to see if we can do a quick bind to ypserv.
257	 *  If we can, then we load the binding (i.e., add it to our
258	 *  cache of bindings) and then return it.
259	 */
260	if ((ypbind_resp = get_cached_domain(domain)) != 0) {
261		pdomb = load_dom_binding(ypbind_resp, domain, &status);
262		if (pdomb == 0)
263			return (FALSE);
264		*binding = pdomb;
265		return (TRUE);
266	}
267	return (FALSE);
268}
269
270/*
271 *  This routine adds a binding for a particular server to our
272 *  list of bound domains.  We check to see if there is actually
273 *  a yp server at the given address.  If not, or if there is
274 *  any other error, we return 0.  We have to malloc the binding
275 *  structure because that is what a call to ypbind returns and
276 *  we are basically doing what a call to ypbind would do.
277 */
278
279#define	SOCKADDR_SIZE (sizeof (struct sockaddr_in6))
280static int
281__yp_add_binding_netid(char *domain, char *addr, char *netid)
282{
283	struct netconfig *nconf = 0;
284	struct netbuf *svcaddr = 0;
285	struct ypbind_binding *binding = 0;
286	int status;
287	struct ypbind_resp resp;
288	struct dom_binding *pdomb;
289
290	nconf = getnetconfigent(netid);
291	if (nconf == 0)
292		goto err;
293
294	svcaddr = malloc(sizeof (struct netbuf));
295	if (svcaddr == 0)
296		goto err;
297
298	svcaddr->maxlen = SOCKADDR_SIZE;
299	svcaddr->buf = malloc(SOCKADDR_SIZE);
300	if (svcaddr->buf == 0)
301		goto err;
302
303	if (!rpcb_getaddr(YPPROG, YPVERS, nconf, svcaddr, addr))
304		goto err;
305
306	binding = malloc(sizeof (struct ypbind_binding));
307	if (binding == 0)
308		goto err;
309
310	binding->ypbind_hi_vers = YPVERS;
311	binding->ypbind_lo_vers = YPVERS;
312	binding->ypbind_nconf = nconf;
313	binding->ypbind_svcaddr = svcaddr;
314	binding->ypbind_servername = (char *)strdup(addr);
315	if (binding->ypbind_servername == 0)
316		goto err;
317
318	resp.ypbind_status = YPBIND_SUCC_VAL;
319	resp.ypbind_resp_u.ypbind_bindinfo = binding;
320
321	(void) mutex_lock(&bound_domains_lock);
322	newborn();
323	pdomb = load_dom_binding(&resp, domain, &status);
324	(void) mutex_unlock(&bound_domains_lock);
325
326	return (pdomb != 0);
327
328err:
329	if (nconf)
330		freenetconfigent(nconf);
331	if (svcaddr) {
332		if (svcaddr->buf)
333			free(svcaddr->buf);
334		free(svcaddr);
335	}
336	if (binding) {
337		if (binding->ypbind_servername)
338			free(binding->ypbind_servername);
339		free(binding);
340	}
341	return (0);
342}
343
344
345int
346__yp_add_binding(char *domain, char *addr) {
347
348	int ret = __yp_add_binding_netid(domain, addr, "udp6");
349
350	if (ret == 0)
351		ret = __yp_add_binding_netid(domain, addr, "udp");
352
353	return (ret);
354}
355
356
357/*
358 * This allocates some memory for a domain binding, initialize it, and
359 * returns a pointer to it.  Based on the program version we ended up
360 * talking to ypbind with, fill out an opvector of appropriate protocol
361 * modules.
362 *
363 * MT-safe because it is only invoked from __yp_dobind(), which serializes
364 * all requests.
365 */
366static struct dom_binding *
367load_dom_binding(struct ypbind_resp *ypbind_res, char *domain, int *err)
368{
369	int fd;
370	struct dom_binding *pdomb;
371
372	pdomb = NULL;
373
374	if ((pdomb = malloc(sizeof (struct dom_binding))) == NULL) {
375		syslog(LOG_ERR, "load_dom_binding:  malloc failure.");
376		*err = YPERR_RESRC;
377		return (NULL);
378	}
379
380	pdomb->dom_binding = ypbind_res->ypbind_resp_u.ypbind_bindinfo;
381	/*
382	 * Open up a path to the server, which will remain active globally.
383	 */
384	pdomb->dom_client = clnt_tli_create(RPC_ANYFD,
385					    pdomb->dom_binding->ypbind_nconf,
386					    pdomb->dom_binding->ypbind_svcaddr,
387					    YPPROG, YPVERS, __ypipbufsize,
388					    __ypipbufsize);
389	if (pdomb->dom_client == NULL) {
390		clnt_pcreateerror("yp_bind: clnt_tli_create");
391		free(pdomb);
392		*err = YPERR_RPC;
393		return (NULL);
394	}
395#ifdef DEBUG
396(void) printf("yp_bind: clnt_tli_create suceeded\n");
397#endif
398
399	pdomb->dom_pnext = bound_domains;	/* Link this to the list as */
400	pdomb->dom_domain = malloc(strlen(domain) + (unsigned)1);
401	if (pdomb->dom_domain == NULL) {
402		clnt_destroy(pdomb->dom_client);
403		free(pdomb);
404		*err = YPERR_RESRC;
405		return (NULL);
406	}
407	/*
408	 *  We may not have loaded from a cache file, but we assume the
409	 *  cache is good until we find out otherwise.
410	 */
411	pdomb->cache_bad = 0;
412	set_rdev(pdomb);
413	if (clnt_control(pdomb->dom_client, CLGET_FD, (char *)&fd))
414		(void) fcntl(fd, F_SETFD, 1);  /* make it "close on exec" */
415
416	(void) strcpy(pdomb->dom_domain, domain); /* Remember the domain name */
417	pdomb->ref_count = 0;
418	pdomb->need_free = 0;
419	(void) mutex_init(&pdomb->server_name_lock, USYNC_THREAD, 0);
420	bound_domains = pdomb;			/* ... the head entry */
421	return (pdomb);
422}
423
424/*
425 * XXX special code for handling C2 (passwd.adjunct) lookups when we need
426 * a reserved port.
427 */
428static int
429tli_open_rsvdport(struct netconfig *nconf)
430{
431	int fd;
432
433	if (nconf == NULL)
434		return (-1);
435
436	fd = t_open(nconf->nc_device, O_RDWR, NULL);
437	if (fd == -1)
438		return (-1);
439
440	if (netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL) == -1) {
441		if (t_bind(fd, NULL, NULL) == -1) {
442			(void) t_close(fd);
443			return (-1);
444		}
445	}
446	return (fd);
447}
448
449/*
450 * This allocates some memory for a domain binding, initialize it, and
451 * returns a pointer to it.  Based on the program version we ended up
452 * talking to ypbind with, fill out an opvector of appropriate protocol
453 * modules.
454 *
455 * MT-safe because it is only invoked from __yp_dobind(), which serializes
456 * all requests.
457 *
458 * XXX special version for handling C2 (passwd.adjunct) lookups when we need
459 * a reserved port.
460 *
461 * Note that the binding is not cached. The caller has to free the binding
462 * using free_dom_binding().
463 */
464static struct dom_binding *
465load_dom_binding_rsvdport(struct ypbind_binding *dom_binding, char *domain,
466								int *err)
467{
468	struct dom_binding *pdomb;
469	int fd;
470
471	pdomb = NULL;
472
473	if ((pdomb = malloc(sizeof (struct dom_binding))) == NULL) {
474		syslog(LOG_ERR, "load_dom_binding_rsvdport:  malloc failure.");
475		*err = YPERR_RESRC;
476		return (NULL);
477	}
478
479	pdomb->dom_binding = dom_binding;
480	/*
481	 * Open up a path to the server, which will remain active globally.
482	 */
483	fd = tli_open_rsvdport(pdomb->dom_binding->ypbind_nconf);
484	if (fd < 0) {
485		clnt_pcreateerror("yp_bind: tli_open_rsvdport");
486		free(pdomb);
487		*err = YPERR_RPC;
488		return (NULL);
489	}
490	pdomb->dom_client = clnt_tli_create(fd,
491					    pdomb->dom_binding->ypbind_nconf,
492					    pdomb->dom_binding->ypbind_svcaddr,
493					    YPPROG, YPVERS, __ypipbufsize,
494					    __ypipbufsize);
495	if (pdomb->dom_client == NULL) {
496		clnt_pcreateerror("yp_bind: clnt_tli_create");
497		free(pdomb);
498		*err = YPERR_RPC;
499		return (NULL);
500	}
501#ifdef DEBUG
502(void) printf("yp_bind: clnt_tli_create suceeded\n");
503#endif
504	(void) CLNT_CONTROL(pdomb->dom_client, CLSET_FD_CLOSE, NULL);
505
506	pdomb->dom_domain = malloc(strlen(domain) + (unsigned)1);
507	if (pdomb->dom_domain == NULL) {
508		clnt_destroy(pdomb->dom_client);
509		free(pdomb);
510		*err = YPERR_RESRC;
511		return (NULL);
512	}
513
514	(void) strcpy(pdomb->dom_domain, domain); /* Remember the domain name */
515	pdomb->ref_count = 0;
516	pdomb->need_free = 0;
517	set_rdev(pdomb);
518	(void) mutex_init(&pdomb->server_name_lock, USYNC_THREAD, 0);
519	return (pdomb);
520}
521
522/*
523 * Attempts to locate a yellow pages server that serves a passed domain.  If
524 * one is found, an entry is created on the static list of domain-server pairs
525 * pointed to by cell bound_domains, a udp path to the server is created and
526 * the function returns 0.  Otherwise, the function returns a defined errorcode
527 * YPERR_xxxx.
528 *
529 * MT-safe because it serializes on bound_domains_lock.
530 *
531 * If hardlookup is set then loop forever until success, else try 4
532 * times (each try is relatively short) max.
533 */
534int
535__yp_dobind_cflookup(
536	char *domain,
537	struct dom_binding **binding,	/* if result==0, ptr to dom_binding */
538	int hardlookup)
539
540{
541	struct dom_binding *pdomb;	/* Ptr to new domain binding */
542	struct ypbind_resp *ypbind_resp; /* Response from local ypbinder */
543	struct ypbind_domain ypbd;
544	int status, err = YPERR_DOMAIN;
545	int first_try = 1;
546	CLIENT *tb = NULL;
547
548	if ((domain == NULL) ||(strlen(domain) == 0))
549		return (YPERR_BADARGS);
550
551	(void) mutex_lock(&bound_domains_lock);
552	/*
553	 * ===>
554	 * If someone managed to fork() while we were holding this lock,
555	 *   we'll probably end up hanging on the lock.  Tant pis.
556	 */
557	newborn();
558
559	if (check_binding(domain, binding)) {
560		/*
561		 *  If the cache is okay and if the underlying file
562		 *  descriptor is okay (application did not close it).
563		 *  then use the binding.
564		 */
565		if (!(*binding)->cache_bad && check_rdev(*binding)) {
566			(*binding)->ref_count += 1;
567			(void) mutex_unlock(&bound_domains_lock);
568			return (0);		/* We are bound */
569		}
570
571		/*
572		 *  If we get here, one of two things happened:  the
573		 *  cache is bad, or the underlying file descriptor
574		 *  had changed.
575		 *
576		 *  If the cache is bad, then we call yp_unbind to remove
577		 *  the binding.
578		 *
579		 *  If the file descriptor has changed, then we call
580		 *  yp_unbind to remove the binding (we set cache_bad
581		 *  to force yp_unbind to do the remove), and then
582		 *  call check_binding to reload the binding from the
583		 *  cache again.
584		 */
585		if ((*binding)->cache_bad) {
586			__yp_unbind_nolock(domain);
587		} else {
588			(*binding)->cache_bad = 1;
589			(void) mutex_unlock(&bound_domains_lock);
590			yp_unbind(domain);
591			(void) mutex_lock(&bound_domains_lock);
592			if (check_binding(domain, binding)) {
593				(*binding)->ref_count += 1;
594				(void) mutex_unlock(&bound_domains_lock);
595				return (0);
596			}
597		}
598	}
599
600	do {
601		if (first_try)
602			first_try = 0;
603		else {
604			/*
605			 * ===> sleep() -- Ugh.  And with the lock held, too.
606			 */
607			(void) sleep(_ypsleeptime);
608		}
609		tb = __clnt_create_loopback(YPBINDPROG, YPBINDVERS, &err);
610		if (tb == NULL) {
611			if (ypbind_running(err, rpc_createerr.cf_stat))
612				continue;
613			break;
614		}
615		ypbd.ypbind_domainname = domain;
616		ypbd.ypbind_vers = YPVERS;
617		/*
618		 * The interface to ypbindproc_domain_3 is MT-unsafe, but we're
619		 *   OK as long as we're the only ones who call it and we
620		 *   serialize all requests (for all domains).  Otherwise,
621		 *   change the interface (pass in the ypbind_resp struct).
622		 */
623		ypbind_resp = ypbindproc_domain_3(&ypbd, tb);
624		/*
625		 * Although we talk to ypbind on loopback,
626		 * it gives us a udp address for the ypserv.
627		 */
628		if (ypbind_resp == NULL) {
629			/* lost ypbind? */
630			clnt_perror(tb,
631				"ypbindproc_domain_3: can't contact ypbind");
632			clnt_destroy(tb);
633			tb = NULL;
634			continue;
635		}
636		if (ypbind_resp->ypbind_status == YPBIND_SUCC_VAL) {
637			/*
638			 * Local ypbind has let us in on the ypserv's address,
639			 * go get in touch with it !
640			 */
641			pdomb = load_dom_binding(ypbind_resp, domain, &status);
642			if (pdomb == 0) {
643				err = status;
644				clnt_destroy(tb);
645				tb = NULL;
646				continue;
647			}
648			clnt_destroy(tb);
649			pdomb->ref_count += 1;
650			(void) mutex_unlock(&bound_domains_lock);
651			*binding = pdomb; /* Return ptr to the binding entry */
652			return (0);		/* This is the go path */
653		}
654		if (ypbind_resp->ypbind_resp_u.ypbind_error ==
655		    YPBIND_ERR_NOSERV)
656			err = YPERR_DOMAIN;
657		else
658			err = YPERR_YPBIND;
659		clnt_destroy(tb);
660		tb = NULL;
661	} while (hardlookup);
662
663	if (tb != NULL)
664		clnt_destroy(tb);
665	(void) mutex_unlock(&bound_domains_lock);
666	if (err)
667		return (err);
668	return (YPERR_DOMAIN);
669}
670
671int
672__yp_dobind(
673	char *domain,
674	struct dom_binding **binding)	/* if result == 0, ptr to dom_binding */
675{
676	/* traditional __yp_dobind loops forever so set hardlookup */
677	return (__yp_dobind_cflookup(domain, binding, 1));
678}
679
680void
681__yp_rel_binding(struct dom_binding *binding)
682{
683	(void) mutex_lock(&bound_domains_lock);
684	binding->ref_count -= 1;
685	if (binding->need_free && binding->ref_count == 0)
686		free_dom_binding(binding);
687	(void) mutex_unlock(&bound_domains_lock);
688}
689
690/*
691 * Attempts to locate a yellow pages server that serves a passed domain.  If
692 * one is found, an entry is created on the static list of domain-server pairs
693 * pointed to by cell bound_domains, a udp path to the server is created and
694 * the function returns 0.  Otherwise, the function returns a defined errorcode
695 * YPERR_xxxx.
696 *
697 * MT-safe because it serializes on bound_domains_lock.
698 *
699 * XXX special version for handling C2 (passwd.adjunct) lookups when we need
700 * a reserved port.
701 * This returns an uncached binding which the caller has to free using
702 * free_dom_binding().
703 */
704int
705__yp_dobind_rsvdport_cflookup(
706	char *domain,
707	struct dom_binding **binding,	/* if result==0, ptr to dom_binding */
708	int hardlookup)
709{
710	struct dom_binding *pdomb;	/* Ptr to new domain binding */
711	struct ypbind_resp *ypbind_resp; /* Response from local ypbinder */
712	struct ypbind_domain ypbd;
713	int status,  err = YPERR_DOMAIN;
714	int first_try = 1;
715	CLIENT *tb = NULL;
716
717	if ((domain == NULL) ||(strlen(domain) == 0))
718		return (YPERR_BADARGS);
719
720	(void) mutex_lock(&bound_domains_lock);
721	/*
722	 * ===>
723	 * If someone managed to fork() while we were holding this lock,
724	 *   we'll probably end up hanging on the lock.  Tant pis.
725	 */
726	newborn();
727
728	/*
729	 * Check for existing bindings and use the information in the binding
730	 * to create a transport endpoint with a reserved port.
731	 */
732	if (check_binding(domain, binding)) {
733		/*
734		 * If the cache is bad, yp_unbind() the entry again and then
735		 * talk to ypbind.
736		 */
737		if ((*binding)->cache_bad) {
738			__yp_unbind_nolock(domain);
739		} else {
740			pdomb = load_dom_binding_rsvdport(
741						(*binding)->dom_binding,
742							domain, &status);
743			if (pdomb == 0) {
744				(void) mutex_unlock(&bound_domains_lock);
745				return (status);
746			}
747			pdomb->ref_count += 1;
748			(void) mutex_unlock(&bound_domains_lock);
749			*binding = pdomb; /* Return ptr to the binding entry */
750			return (0);
751		}
752	}
753
754	do {
755		if (first_try)
756			first_try = 0;
757		else {
758			/*
759			 * ===> sleep() -- Ugh.  And with the lock held, too.
760			 */
761			(void) sleep(_ypsleeptime);
762		}
763		tb = __clnt_create_loopback(YPBINDPROG, YPBINDVERS, &err);
764		if (tb == NULL) {
765			if (ypbind_running(err, rpc_createerr.cf_stat))
766				continue;
767			break;
768		}
769		ypbd.ypbind_domainname = domain;
770		ypbd.ypbind_vers = YPVERS;
771		/*
772		 * The interface to ypbindproc_domain_3 is MT-unsafe, but we're
773		 *   OK as long as we're the only ones who call it and we
774		 *   serialize all requests (for all domains).  Otherwise,
775		 *   change the interface (pass in the ypbind_resp struct).
776		 */
777		ypbind_resp = ypbindproc_domain_3(&ypbd, tb);
778		/*
779		 * Although we talk to ypbind on loopback,
780		 * it gives us a udp address for the ypserv.
781		 */
782		if (ypbind_resp == NULL) {
783			/* lost ypbind? */
784			clnt_perror(tb,
785				"ypbindproc_domain_3: can't contact ypbind");
786			clnt_destroy(tb);
787			tb = NULL;
788			continue;
789		}
790		if (ypbind_resp->ypbind_status == YPBIND_SUCC_VAL) {
791			/*
792			 * Local ypbind has let us in on the ypserv's address,
793			 * go get in touch with it !
794			 */
795			pdomb = load_dom_binding_rsvdport(
796				    ypbind_resp->ypbind_resp_u.ypbind_bindinfo,
797				    domain, &status);
798			if (pdomb == 0) {
799				err = status;
800				clnt_destroy(tb);
801				tb = NULL;
802				continue;
803			}
804			clnt_destroy(tb);
805			pdomb->ref_count += 1;
806			(void) mutex_unlock(&bound_domains_lock);
807			*binding = pdomb; /* Return ptr to the binding entry */
808			return (0);		/* This is the go path */
809		}
810		if (ypbind_resp->ypbind_resp_u.ypbind_error ==
811		    YPBIND_ERR_NOSERV)
812			err = YPERR_DOMAIN;
813		else
814			err = YPERR_YPBIND;
815		clnt_destroy(tb);
816		tb = NULL;
817	} while (hardlookup);
818
819	if (tb != NULL)
820		clnt_destroy(tb);
821	(void) mutex_unlock(&bound_domains_lock);
822	if (err)
823		return (err);
824	return (YPERR_DOMAIN);
825}
826
827int
828__yp_dobind_rsvdport(
829	char *domain,
830	struct dom_binding **binding)	/* if result==0, ptr to dom_binding */
831{
832	/* traditional __yp_dobind_rsvdport loops forever so set hardlookup */
833	return (__yp_dobind_rsvdport_cflookup(domain, binding, 1));
834}
835
836/*
837 * This is a "wrapper" function for __yp_dobind for vanilla user-level
838 * functions which neither know nor care about struct dom_bindings.
839 */
840int
841yp_bind(char *domain)
842{
843
844	struct dom_binding *binding;
845	int    res;
846
847	res = __yp_dobind(domain, &binding);
848	if (res == 0)
849		__yp_rel_binding(binding);
850	return (res);
851}
852
853static char *
854__default_domain(void)
855{
856	char temp[256];
857
858	(void) mutex_lock(&default_domain_lock);
859
860	if (default_domain) {
861		(void) mutex_unlock(&default_domain_lock);
862		return (default_domain);
863	}
864	if (getdomainname(temp, sizeof (temp)) < 0) {
865		(void) mutex_unlock(&default_domain_lock);
866		return (0);
867	}
868	if (strlen(temp) > 0) {
869		default_domain = malloc((strlen(temp) + 1));
870		if (default_domain == 0) {
871			(void) mutex_unlock(&default_domain_lock);
872			return (0);
873		}
874		(void) strcpy(default_domain, temp);
875		(void) mutex_unlock(&default_domain_lock);
876		return (default_domain);
877	}
878	(void) mutex_unlock(&default_domain_lock);
879	return (0);
880}
881
882/*
883 * This is a wrapper for the system call getdomainname which returns a
884 * ypclnt.h error code in the failure case.  It also checks to see that
885 * the domain name is non-null, knowing that the null string is going to
886 * get rejected elsewhere in the yp client package.
887 */
888int
889yp_get_default_domain(char **domain)
890{
891	if ((*domain = __default_domain()) != 0)
892		return (0);
893	return (YPERR_YPERR);
894}
895
896/*
897 * ===> Nobody uses this, do they?  Can we nuke it?
898 */
899int
900usingypmap(char **ddn, char *map)
901{
902	char in, *outval = NULL;
903	int outvallen, stat;
904	char *domain;
905
906	if ((domain = __default_domain()) == 0)
907		return (FALSE);
908	*ddn = domain;
909	/* does the map exist ? */
910	in = (char)0xff;
911	stat = yp_match(domain, map, &in, 1, &outval, &outvallen);
912	if (outval != NULL)
913		free(outval);
914	switch (stat) {
915
916	case 0:  /* it actually succeeded! */
917	case YPERR_KEY:  /* no such key in map */
918	case YPERR_NOMORE:
919	case YPERR_BUSY:
920		return (TRUE);
921	}
922	return (FALSE);
923}
924
925/*
926 * Creates a quick connection on a connection oriented loopback
927 * transport. Fails quickly without timeout. Only naming service
928 * it goes to is straddr.so.
929 */
930CLIENT *
931__clnt_create_loopback(rpcprog_t prog, rpcvers_t vers, int *err)
932{
933	struct netconfig *nconf;
934	CLIENT *clnt = NULL;
935	void *nc_handle;	/* Net config handle */
936
937	*err = 0;
938	nc_handle = setnetconfig();
939	if (nc_handle == NULL) {
940		/* fails to open netconfig file */
941		rpc_createerr.cf_stat = RPC_FAILED;
942		*err = YPERR_RPC;
943		return (NULL);
944	}
945	while (nconf = getnetconfig(nc_handle))
946		/* Try only one connection oriented loopback transport */
947		if ((strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) &&
948			((nconf->nc_semantics == NC_TPI_COTS) ||
949			(nconf->nc_semantics == NC_TPI_COTS_ORD))) {
950			clnt = getclnt(prog, vers, nconf, err);
951			break;
952		}
953	(void) endnetconfig(nc_handle);
954
955	if (clnt == NULL) {	/* no loopback transport available */
956		if (rpc_createerr.cf_stat == 0)
957			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
958		if (*err == 0) *err = YPERR_RPC;
959	}
960	return (clnt);
961}
962
963static CLIENT *
964getclnt(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, int *err)
965{
966	int fd;
967	struct netbuf *svcaddr;			/* servers address */
968	CLIENT *cl;
969	struct nd_addrlist *nas;
970	struct nd_hostserv rpcbind_hs;
971	struct t_call sndcall;
972	char uaddress[1024]; /* XXX maxlen ?? */
973	RPCB parms;
974	enum clnt_stat clnt_st;
975	char *ua;
976	struct timeval tv = { 30, 0 };
977
978	if (nconf == NULL) {
979		rpc_createerr.cf_stat = RPC_TLIERROR;
980		*err = YPERR_RPC;
981		return (NULL);
982	}
983
984	/*
985	 *  The ypbind process might cache its transport address.
986	 *  If we can get at it, then we will use it and avoid
987	 *  wasting time talking to rpcbind.
988	 */
989
990	if (get_cached_transport(nconf, vers, uaddress, sizeof (uaddress))) {
991		goto create_client;
992	}
993
994	/*
995	 * Check to see if local rpcbind is up or not. If it
996	 * isn't, it is best that the application should realize
997	 * yp is not up and take a remedial action. This is to
998	 * avoid the minute long timeout incurred by rpcbind_getaddr.
999	 * Looks like the only way to accomplish this it is to unfold
1000	 * rpcb_getaddr and make a few changes. Alas !
1001	 */
1002	rpcbind_hs.h_host = HOST_SELF_CONNECT;
1003	rpcbind_hs.h_serv = "rpcbind";
1004	if (netdir_getbyname(nconf, &rpcbind_hs, &nas) != ND_OK) {
1005		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
1006		*err = YPERR_RPC;
1007		return (NULL);
1008	}
1009	if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) == -1) {
1010		rpc_createerr.cf_stat = RPC_TLIERROR;
1011		*err = YPERR_RPC;
1012		return (NULL);
1013	}
1014	if (t_bind(fd, NULL, NULL) == -1) {
1015		rpc_createerr.cf_stat = RPC_TLIERROR;
1016		*err = YPERR_RPC;
1017		(void) t_close(fd);
1018		return (NULL);
1019	}
1020	sndcall.addr = *(nas->n_addrs);
1021	sndcall.opt.len = 0;
1022	sndcall.udata.len = 0;
1023	if (t_connect(fd, &sndcall, NULL) == -1) {
1024		netdir_free((char *)nas, ND_ADDRLIST);
1025		rpc_createerr.cf_stat = RPC_TLIERROR;
1026		(void) t_close(fd);
1027		*err = YPERR_PMAP;
1028		return (NULL);
1029	}
1030
1031	/*
1032	 * Get the address of the server
1033	 */
1034	cl = clnt_tli_create(fd, nconf, nas->n_addrs,
1035		RPCBPROG, RPCBVERS, __ypipbufsize, __ypipbufsize);
1036	netdir_free((char *)nas, ND_ADDRLIST);
1037	if (cl == NULL) {
1038		(void) t_close(fd);
1039		*err = YPERR_PMAP;
1040		return (NULL);
1041	}
1042	parms.r_prog = prog;
1043	parms.r_vers = vers;
1044	parms.r_netid = nconf->nc_netid;
1045	parms.r_addr = nullstring;
1046	parms.r_owner = nullstring;
1047	ua = uaddress;
1048	clnt_st = CLNT_CALL(cl, RPCBPROC_GETADDR, xdr_rpcb, (char *)&parms,
1049		xdr_wrapstring, (char *)&ua, tv);
1050	(void) t_close(fd);
1051	clnt_destroy(cl);
1052	if (clnt_st != RPC_SUCCESS) {
1053		*err = YPERR_YPBIND;
1054		return (NULL);
1055	}
1056	if (strlen(uaddress) == 0) {
1057		*err = YPERR_YPBIND;
1058		rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
1059		return (NULL);
1060	}
1061
1062create_client:
1063	svcaddr = uaddr2taddr(nconf, uaddress);
1064	cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr, prog, vers,
1065					__ypipbufsize, __ypipbufsize);
1066	netdir_free((char *)svcaddr, ND_ADDR);
1067	if (cl == NULL) {
1068		*err = YPERR_YPBIND;
1069		return (NULL);
1070	}
1071	/*
1072	 * The fd should be closed while destroying the handle.
1073	 */
1074	return (cl);
1075}
1076
1077static int
1078get_cached_transport(struct netconfig *nconf, int vers, char *uaddress,
1079								int ulen)
1080{
1081	ssize_t st;
1082	int fd;
1083
1084	(void) snprintf(uaddress, ulen,
1085		"%s/xprt.%s.%d", BINDING, nconf->nc_netid, vers);
1086	fd = open(uaddress, O_RDONLY);
1087	if (fd == -1)
1088		return (0);
1089
1090	/* if first byte is not locked, then ypbind must not be running */
1091	st = lockf(fd, F_TEST, 1);
1092	if (st != -1 || (errno != EAGAIN && errno != EACCES)) {
1093		(void) close(fd);
1094		return (0);
1095	}
1096
1097	st = read(fd, uaddress, ulen);
1098	if (st == -1) {
1099		(void) close(fd);
1100		return (0);
1101	}
1102
1103	(void) close(fd);
1104	return (1);
1105}
1106
1107static ypbind_resp *
1108get_cached_domain(char *domain)
1109{
1110	FILE *fp;
1111	int st;
1112	char filename[300];
1113	static ypbind_resp res;
1114	XDR xdrs;
1115
1116	(void) snprintf(filename, sizeof (filename),
1117					"%s/%s/cache_binding", BINDING, domain);
1118	fp = fopen(filename, "rF");
1119	if (fp == 0)
1120		return (0);
1121
1122	/* if first byte is not locked, then ypbind must not be running */
1123	st = lockf(fileno(fp), F_TEST, 1);
1124	if (st != -1 || (errno != EAGAIN && errno != EACCES)) {
1125		(void) fclose(fp);
1126		return (0);
1127	}
1128
1129	xdrstdio_create(&xdrs, fp, XDR_DECODE);
1130
1131	(void) memset((char *)&res, 0, sizeof (res));
1132	st = xdr_ypbind_resp(&xdrs, &res);
1133
1134	xdr_destroy(&xdrs);
1135	(void) fclose(fp);
1136
1137	if (st)
1138		return (&res);
1139
1140	return (0);
1141}
1142
1143static int
1144ypbind_running(int err, int status)
1145{
1146	char filename[300];
1147	int st;
1148	int fd;
1149
1150	(void) snprintf(filename, sizeof (filename), "%s/ypbind.pid", BINDING);
1151	fd = open(filename, O_RDONLY);
1152	if (fd == -1) {
1153		if ((err == YPERR_YPBIND) && (status != RPC_PROGNOTREGISTERED))
1154			return (1);
1155		return (0);
1156	}
1157
1158	/* if first byte is not locked, then ypbind must not be running */
1159	st = lockf(fd, F_TEST, 1);
1160	if (st != -1 || (errno != EAGAIN && errno != EACCES)) {
1161		(void) close(fd);
1162		return (0);
1163	}
1164
1165	(void) close(fd);
1166	return (1);
1167}
1168
1169static void
1170set_rdev(struct dom_binding *pdomb)
1171{
1172	int fd;
1173	struct stat stbuf;
1174
1175	if (clnt_control(pdomb->dom_client, CLGET_FD, (char *)&fd) != TRUE ||
1176	    fstat(fd, &stbuf) == -1) {
1177		syslog(LOG_DEBUG, "ypbind client:  can't get rdev");
1178		pdomb->fd = -1;
1179		return;
1180	}
1181	pdomb->fd = fd;
1182	pdomb->rdev = stbuf.st_rdev;
1183}
1184
1185static int
1186check_rdev(struct dom_binding *pdomb)
1187{
1188	struct stat stbuf;
1189
1190	if (pdomb->fd == -1)
1191		return (1);    /* can't check it, assume it is okay */
1192
1193	if (fstat(pdomb->fd, &stbuf) == -1) {
1194		syslog(LOG_DEBUG, "yp_bind client:  can't stat %d", pdomb->fd);
1195		/* could be because file descriptor was closed */
1196		/* it's not our file descriptor, so don't try to close it */
1197		clnt_control(pdomb->dom_client, CLSET_FD_NCLOSE, NULL);
1198		return (0);
1199	}
1200	if (pdomb->rdev != stbuf.st_rdev) {
1201		syslog(LOG_DEBUG,
1202		    "yp_bind client:  fd %d changed, old=0x%x, new=0x%x",
1203		    pdomb->fd, pdomb->rdev, stbuf.st_rdev);
1204		/* it's not our file descriptor, so don't try to close it */
1205		clnt_control(pdomb->dom_client, CLSET_FD_NCLOSE, NULL);
1206		return (0);
1207	}
1208	return (1);    /* fd is okay */
1209}
1210