Home | History | Annotate | Download | only in yp
      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