xref: /illumos-gate/usr/src/lib/libnsl/yp/yp_bind.c (revision 65a89a64c60f3061bbe2381edaacc81660af9a95)
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 */
59 int	 __ypipbufsize = 8192;		/* size used for clnt_tli_create */
60 
61 /* This should match the one in ypbind.c */
62 
63 extern int getdomainname(char *, int);
64 
65 static CLIENT *getclnt(rpcprog_t, rpcvers_t, struct netconfig *, int *);
66 static struct dom_binding *load_dom_binding(struct ypbind_resp *, char *,
67     int *);
68 static ypbind_resp *get_cached_domain(char *);
69 static int get_cached_transport(struct netconfig *, int, char *, int);
70 static int ypbind_running(int, int);
71 static void set_rdev(struct dom_binding *);
72 static int check_rdev(struct dom_binding *);
73 
74 static 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 */
80 unsigned 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. */
97 struct timeval _ypserv_timeout = {
98 	YPTIMEOUT,			/* Seconds */
99 	0				/* Microseconds */
100 	};
101 
102 static mutex_t			default_domain_lock = DEFAULTMUTEX;
103 static 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  */
123 static mutex_t			bound_domains_lock = DEFAULTMUTEX;
124 static 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  */
131 void
132 free_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 
153 static 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 
191 void
192 yp_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  */
207 static void
208 newborn(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  */
237 static bool
238 check_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))
286 static 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 
334 err:
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 
351 int
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  */
372 static struct dom_binding *
373 load_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  */
434 static int
435 tli_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  */
470 static struct dom_binding *
471 load_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  */
540 int
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 
677 int
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 
686 void
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  */
710 int
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 
833 int
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  */
846 int
847 yp_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 
859 static 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  */
894 int
895 yp_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  */
905 int
906 usingypmap(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  */
936 CLIENT *
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 
969 static CLIENT *
970 getclnt(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 
1068 create_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 
1083 static int
1084 get_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 
1113 static ypbind_resp *
1114 get_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 
1149 static int
1150 ypbind_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 
1175 static void
1176 set_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 
1191 static int
1192 check_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