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