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