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