/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley * under license from the Regents of the University of * California. */ #pragma ident "%Z%%M% %I% %E% SMI" #include "mt.h" #include "../rpc/rpc_mt.h" #include #include #include #include #include #include #include #include #include #include #include #include "yp_b.h" #include #include #include #include "nsl_stdio_prv.h" #if defined(sparc) #define _FSTAT _fstat extern int _fstat(int, struct stat *); #else /* !sparc */ #define _FSTAT fstat #endif /* sparc */ #define BFSIZE (YPMAXDOMAIN + 32) /* size of binding file */ int __ypipbufsize = 8192; /* size used for clnt_tli_create */ /* This should match the one in ypbind.c */ extern int getdomainname(char *, int); static CLIENT *getclnt(rpcprog_t, rpcvers_t, struct netconfig *, int *); static struct dom_binding *load_dom_binding(struct ypbind_resp *, char *, int *); static ypbind_resp *get_cached_domain(char *); static int get_cached_transport(struct netconfig *, int, char *, int); static int ypbind_running(int, int); static void set_rdev(struct dom_binding *); static int check_rdev(struct dom_binding *); static char nullstring[] = ""; /* * Time parameters when talking to the ypbind and pmap processes */ #define YPSLEEPTIME 5 /* Time to sleep between tries */ unsigned int _ypsleeptime = YPSLEEPTIME; /* * Time parameters when talking to the ypserv process */ #ifdef DEBUG #define YPTIMEOUT 120 /* Total seconds for timeout */ #define YPINTER_TRY 60 /* Seconds between tries */ #else #define YPTIMEOUT 20 /* Total seconds for timeout */ #define YPINTER_TRY 5 /* Seconds between tries */ #endif #define MAX_TRIES_FOR_NEW_YP 1 /* Number of times we'll try to */ /* get a new YP server before */ /* we'll settle for an old one. */ struct timeval _ypserv_timeout = { YPTIMEOUT, /* Seconds */ 0 /* Microseconds */ }; static mutex_t default_domain_lock = DEFAULTMUTEX; static char *default_domain; /* * The bound_domains_lock serializes all action in yp_unbind(), __yp_dobind(), * newborn(), check_binding() and laod_dom_binding(), not just the direct * manipulation of the bound_domains list. * It also protects all of the fields within a domain binding except * the server_name field (which is protected by the server_name_lock). * A better implementation might try to serialize each domain separately, * but normally we're only dealing with one domain (the default) anyway. * To avoid one thread freeing a domain binding while another is using * the binding, we maintain a reference count for each binding. The * reference count is incremented in __yp_dobind. The thread calls * __yp_rel_binding() when it has finished using the binding (which * decrements the reference count). If the reference count is non-zero * when a thread tries to free a binding, the need_free flag is set and * the free is delayed. The __yp_rel_binding() routine checks the flag * and calls the free routine if the flag is set and the reference * count is zero. */ static mutex_t bound_domains_lock = DEFAULTMUTEX; static struct dom_binding *bound_domains; /* List of bound domains */ /* * Must be called with bound_domains_lock held or with a dom_binding * that cannot be referenced by another thread. */ void free_dom_binding(struct dom_binding *p) { if (p->ref_count != 0) { p->need_free = 1; return; } (void) check_rdev(p); clnt_destroy(p->dom_client); free(p->dom_domain); free(p); } /* * Attempts to find a dom_binding in the list at bound_domains having the * domain name field equal to the passed domain name, and removes it if found. * The domain-server binding will not exist after the call to this function. * All resources associated with the binding will be freed. * * yp_unbind is MT-safe because it serializes on bound_domains_lock. */ static void __yp_unbind_nolock(char *domain) { struct dom_binding *p; struct dom_binding **prev; if ((domain == NULL) || (strlen(domain) == 0)) { return; } /* * If we used a cache file to bind, then we will mark the * cache bad. This will cause a subsequent call to __yp_dobind * to ignore the cache and talk to ypbind. Otherwise, we * have already gotten a binding by talking to ypbind and * the binding is not good. * * An optimization could be to check to see if the cache * file has changed (ypbind is pointing at a new server) and * reload the binding from it. But that is too much work * for now. */ for (prev = &bound_domains; (p = *prev) != 0; prev = &p->dom_pnext) { if (strcmp(domain, p->dom_domain) == 0) { if (!p->cache_bad) { p->cache_bad = 1; break; } *prev = p->dom_pnext; free_dom_binding(p); break; } } } void yp_unbind(char *domain) { (void) mutex_lock(&bound_domains_lock); __yp_unbind_nolock(domain); (void) mutex_unlock(&bound_domains_lock); } /* * This checks to see if this is a new process incarnation which has * inherited bindings from a parent, and unbinds the world if so. * * MT-safe because it is only invoked from __yp_dobind(), which serializes * all requests. */ static void newborn(void) { static pid_t mypid; /* Cached to detect forks */ pid_t testpid; struct dom_binding *p, *q; if ((testpid = getpid()) != mypid) { mypid = testpid; for (p = bound_domains; p != 0; p = q) { q = p->dom_pnext; free_dom_binding(p); } bound_domains = 0; } } /* * This checks that the socket for a domain which has already been bound * hasn't been closed or changed under us. If it has, unbind the domain * without closing the socket, which may be in use by some higher level * code. This returns TRUE and points the binding parameter at the found * dom_binding if the binding is found and the socket looks OK, and FALSE * otherwise. * * MT-safe because it is only invoked from __yp_dobind(), which serializes * all requests. */ static bool check_binding(char *domain, struct dom_binding **binding) { struct dom_binding *pdomb; struct ypbind_resp *ypbind_resp; int status; for (pdomb = bound_domains; pdomb != NULL; pdomb = pdomb->dom_pnext) { if (strcmp(domain, pdomb->dom_domain) == 0) { /* * XXX How do we really make sure the udp connection hasn't * changes under us ? If it happens and we can't detect it, * the appliction is doomed ! * POLICY: Let nobody do a yp_bind or __yp_dobind explicitly * and forget to to yp_unbind it. All apps should go * through the standard yp_match/first etc. functions. */ *binding = pdomb; return (TRUE); } } /* * We check to see if we can do a quick bind to ypserv. * If we can, then we load the binding (i.e., add it to our * cache of bindings) and then return it. */ if ((ypbind_resp = get_cached_domain(domain)) != 0) { pdomb = load_dom_binding(ypbind_resp, domain, &status); if (pdomb == 0) return (FALSE); *binding = pdomb; return (TRUE); } return (FALSE); } /* * This routine adds a binding for a particular server to our * list of bound domains. We check to see if there is actually * a yp server at the given address. If not, or if there is * any other error, we return 0. We have to malloc the binding * structure because that is what a call to ypbind returns and * we are basically doing what a call to ypbind would do. */ #define SOCKADDR_SIZE (sizeof (struct sockaddr_in6)) static int __yp_add_binding_netid(char *domain, char *addr, char *netid) { struct netconfig *nconf = 0; struct netbuf *svcaddr = 0; struct ypbind_binding *binding = 0; int status; struct ypbind_resp resp; struct dom_binding *pdomb; nconf = getnetconfigent(netid); if (nconf == 0) goto err; svcaddr = malloc(sizeof (struct netbuf)); if (svcaddr == 0) goto err; svcaddr->maxlen = SOCKADDR_SIZE; svcaddr->buf = malloc(SOCKADDR_SIZE); if (svcaddr->buf == 0) goto err; if (!rpcb_getaddr(YPPROG, YPVERS, nconf, svcaddr, addr)) goto err; binding = malloc(sizeof (struct ypbind_binding)); if (binding == 0) goto err; binding->ypbind_hi_vers = YPVERS; binding->ypbind_lo_vers = YPVERS; binding->ypbind_nconf = nconf; binding->ypbind_svcaddr = svcaddr; binding->ypbind_servername = (char *)strdup(addr); if (binding->ypbind_servername == 0) goto err; resp.ypbind_status = YPBIND_SUCC_VAL; resp.ypbind_resp_u.ypbind_bindinfo = binding; (void) mutex_lock(&bound_domains_lock); newborn(); pdomb = load_dom_binding(&resp, domain, &status); (void) mutex_unlock(&bound_domains_lock); return (pdomb != 0); err: if (nconf) freenetconfigent(nconf); if (svcaddr) { if (svcaddr->buf) free(svcaddr->buf); free(svcaddr); } if (binding) { if (binding->ypbind_servername) free(binding->ypbind_servername); free(binding); } return (0); } int __yp_add_binding(char *domain, char *addr) { int ret = __yp_add_binding_netid(domain, addr, "udp6"); if (ret == 0) ret = __yp_add_binding_netid(domain, addr, "udp"); return (ret); } /* * This allocates some memory for a domain binding, initialize it, and * returns a pointer to it. Based on the program version we ended up * talking to ypbind with, fill out an opvector of appropriate protocol * modules. * * MT-safe because it is only invoked from __yp_dobind(), which serializes * all requests. */ static struct dom_binding * load_dom_binding(struct ypbind_resp *ypbind_res, char *domain, int *err) { int fd; struct dom_binding *pdomb; pdomb = NULL; if ((pdomb = malloc(sizeof (struct dom_binding))) == NULL) { syslog(LOG_ERR, "load_dom_binding: malloc failure."); *err = YPERR_RESRC; return (NULL); } pdomb->dom_binding = ypbind_res->ypbind_resp_u.ypbind_bindinfo; /* * Open up a path to the server, which will remain active globally. */ pdomb->dom_client = clnt_tli_create(RPC_ANYFD, pdomb->dom_binding->ypbind_nconf, pdomb->dom_binding->ypbind_svcaddr, YPPROG, YPVERS, __ypipbufsize, __ypipbufsize); if (pdomb->dom_client == NULL) { clnt_pcreateerror("yp_bind: clnt_tli_create"); free(pdomb); *err = YPERR_RPC; return (NULL); } #ifdef DEBUG (void) printf("yp_bind: clnt_tli_create suceeded\n"); #endif pdomb->dom_pnext = bound_domains; /* Link this to the list as */ pdomb->dom_domain = malloc(strlen(domain) + (unsigned)1); if (pdomb->dom_domain == NULL) { clnt_destroy(pdomb->dom_client); free(pdomb); *err = YPERR_RESRC; return (NULL); } /* * We may not have loaded from a cache file, but we assume the * cache is good until we find out otherwise. */ pdomb->cache_bad = 0; set_rdev(pdomb); if (clnt_control(pdomb->dom_client, CLGET_FD, (char *)&fd)) _fcntl(fd, F_SETFD, 1); /* make it "close on exec" */ (void) strcpy(pdomb->dom_domain, domain); /* Remember the domain name */ pdomb->ref_count = 0; pdomb->need_free = 0; (void) mutex_init(&pdomb->server_name_lock, USYNC_THREAD, 0); bound_domains = pdomb; /* ... the head entry */ return (pdomb); } /* * XXX special code for handling C2 (passwd.adjunct) lookups when we need * a reserved port. */ static int tli_open_rsvdport(struct netconfig *nconf) { int fd; if (nconf == NULL) return (-1); fd = t_open(nconf->nc_device, O_RDWR, NULL); if (fd == -1) return (-1); if (netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL) == -1) { if (t_bind(fd, NULL, NULL) == -1) { (void) t_close(fd); return (-1); } } return (fd); } /* * This allocates some memory for a domain binding, initialize it, and * returns a pointer to it. Based on the program version we ended up * talking to ypbind with, fill out an opvector of appropriate protocol * modules. * * MT-safe because it is only invoked from __yp_dobind(), which serializes * all requests. * * XXX special version for handling C2 (passwd.adjunct) lookups when we need * a reserved port. * * Note that the binding is not cached. The caller has to free the binding * using free_dom_binding(). */ static struct dom_binding * load_dom_binding_rsvdport(struct ypbind_binding *dom_binding, char *domain, int *err) { struct dom_binding *pdomb; int fd; pdomb = NULL; if ((pdomb = malloc(sizeof (struct dom_binding))) == NULL) { syslog(LOG_ERR, "load_dom_binding_rsvdport: malloc failure."); *err = YPERR_RESRC; return (NULL); } pdomb->dom_binding = dom_binding; /* * Open up a path to the server, which will remain active globally. */ fd = tli_open_rsvdport(pdomb->dom_binding->ypbind_nconf); if (fd < 0) { clnt_pcreateerror("yp_bind: tli_open_rsvdport"); free(pdomb); *err = YPERR_RPC; return (NULL); } pdomb->dom_client = clnt_tli_create(fd, pdomb->dom_binding->ypbind_nconf, pdomb->dom_binding->ypbind_svcaddr, YPPROG, YPVERS, __ypipbufsize, __ypipbufsize); if (pdomb->dom_client == NULL) { clnt_pcreateerror("yp_bind: clnt_tli_create"); free(pdomb); *err = YPERR_RPC; return (NULL); } #ifdef DEBUG (void) printf("yp_bind: clnt_tli_create suceeded\n"); #endif (void) CLNT_CONTROL(pdomb->dom_client, CLSET_FD_CLOSE, NULL); pdomb->dom_domain = malloc(strlen(domain) + (unsigned)1); if (pdomb->dom_domain == NULL) { clnt_destroy(pdomb->dom_client); free(pdomb); *err = YPERR_RESRC; return (NULL); } (void) strcpy(pdomb->dom_domain, domain); /* Remember the domain name */ pdomb->ref_count = 0; pdomb->need_free = 0; set_rdev(pdomb); (void) mutex_init(&pdomb->server_name_lock, USYNC_THREAD, 0); return (pdomb); } /* * Attempts to locate a yellow pages server that serves a passed domain. If * one is found, an entry is created on the static list of domain-server pairs * pointed to by cell bound_domains, a udp path to the server is created and * the function returns 0. Otherwise, the function returns a defined errorcode * YPERR_xxxx. * * MT-safe because it serializes on bound_domains_lock. * * If hardlookup is set then loop forever until success, else try 4 * times (each try is relatively short) max. */ int __yp_dobind_cflookup( char *domain, struct dom_binding **binding, /* if result==0, ptr to dom_binding */ int hardlookup) { struct dom_binding *pdomb; /* Ptr to new domain binding */ struct ypbind_resp *ypbind_resp; /* Response from local ypbinder */ struct ypbind_domain ypbd; int status, err = YPERR_DOMAIN; int tries = 4; /* if not hardlookup, try 4 times max to bind */ int first_try = 1; CLIENT *tb = NULL; if ((domain == NULL) ||(strlen(domain) == 0)) return (YPERR_BADARGS); (void) mutex_lock(&bound_domains_lock); /* * ===> * If someone managed to fork() while we were holding this lock, * we'll probably end up hanging on the lock. Tant pis. */ newborn(); if (check_binding(domain, binding)) { /* * If the cache is okay and if the underlying file * descriptor is okay (application did not close it). * then use the binding. */ if (!(*binding)->cache_bad && check_rdev(*binding)) { (*binding)->ref_count += 1; (void) mutex_unlock(&bound_domains_lock); return (0); /* We are bound */ } /* * If we get here, one of two things happened: the * cache is bad, or the underlying file descriptor * had changed. * * If the cache is bad, then we call yp_unbind to remove * the binding. * * If the file descriptor has changed, then we call * yp_unbind to remove the binding (we set cache_bad * to force yp_unbind to do the remove), and then * call check_binding to reload the binding from the * cache again. */ if ((*binding)->cache_bad) { __yp_unbind_nolock(domain); } else { (*binding)->cache_bad = 1; (void) mutex_unlock(&bound_domains_lock); yp_unbind(domain); (void) mutex_lock(&bound_domains_lock); if (check_binding(domain, binding)) { (*binding)->ref_count += 1; (void) mutex_unlock(&bound_domains_lock); return (0); } } } while (hardlookup ? 1 : tries--) { if (first_try) first_try = 0; else { /* * ===> sleep() -- Ugh. And with the lock held, too. */ (void) sleep(_ypsleeptime); } tb = __clnt_create_loopback(YPBINDPROG, YPBINDVERS, &err); if (tb == NULL) { if (ypbind_running(err, rpc_createerr.cf_stat)) continue; break; } ypbd.ypbind_domainname = domain; ypbd.ypbind_vers = YPVERS; /* * The interface to ypbindproc_domain_3 is MT-unsafe, but we're * OK as long as we're the only ones who call it and we * serialize all requests (for all domains). Otherwise, * change the interface (pass in the ypbind_resp struct). */ ypbind_resp = ypbindproc_domain_3(&ypbd, tb); /* * Although we talk to ypbind on loopback, * it gives us a udp address for the ypserv. */ if (ypbind_resp == NULL) { /* lost ypbind? */ clnt_perror(tb, "ypbindproc_domain_3: can't contact ypbind"); clnt_destroy(tb); tb = NULL; continue; } if (ypbind_resp->ypbind_status == YPBIND_SUCC_VAL) { /* * Local ypbind has let us in on the ypserv's address, * go get in touch with it ! */ pdomb = load_dom_binding(ypbind_resp, domain, &status); if (pdomb == 0) { err = status; clnt_destroy(tb); tb = NULL; continue; } clnt_destroy(tb); pdomb->ref_count += 1; (void) mutex_unlock(&bound_domains_lock); *binding = pdomb; /* Return ptr to the binding entry */ return (0); /* This is the go path */ } if (ypbind_resp->ypbind_resp_u.ypbind_error == YPBIND_ERR_NOSERV) err = YPERR_DOMAIN; else err = YPERR_YPBIND; clnt_destroy(tb); tb = NULL; } if (tb != NULL) clnt_destroy(tb); (void) mutex_unlock(&bound_domains_lock); if (err) return (err); return (YPERR_DOMAIN); } int __yp_dobind( char *domain, struct dom_binding **binding) /* if result == 0, ptr to dom_binding */ { /* traditional __yp_dobind loops forever so set hardlookup */ return (__yp_dobind_cflookup(domain, binding, 1)); } void __yp_rel_binding(struct dom_binding *binding) { (void) mutex_lock(&bound_domains_lock); binding->ref_count -= 1; if (binding->need_free && binding->ref_count == 0) free_dom_binding(binding); (void) mutex_unlock(&bound_domains_lock); } /* * Attempts to locate a yellow pages server that serves a passed domain. If * one is found, an entry is created on the static list of domain-server pairs * pointed to by cell bound_domains, a udp path to the server is created and * the function returns 0. Otherwise, the function returns a defined errorcode * YPERR_xxxx. * * MT-safe because it serializes on bound_domains_lock. * * XXX special version for handling C2 (passwd.adjunct) lookups when we need * a reserved port. * This returns an uncached binding which the caller has to free using * free_dom_binding(). */ int __yp_dobind_rsvdport_cflookup( char *domain, struct dom_binding **binding, /* if result==0, ptr to dom_binding */ int hardlookup) { struct dom_binding *pdomb; /* Ptr to new domain binding */ struct ypbind_resp *ypbind_resp; /* Response from local ypbinder */ struct ypbind_domain ypbd; int status, err = YPERR_DOMAIN; int tries = 4; /* if not hardlookup, try a few times to bind */ int first_try = 1; CLIENT *tb = NULL; if ((domain == NULL) ||(strlen(domain) == 0)) return (YPERR_BADARGS); (void) mutex_lock(&bound_domains_lock); /* * ===> * If someone managed to fork() while we were holding this lock, * we'll probably end up hanging on the lock. Tant pis. */ newborn(); /* * Check for existing bindings and use the information in the binding * to create a transport endpoint with a reserved port. */ if (check_binding(domain, binding)) { /* * If the cache is bad, yp_unbind() the entry again and then * talk to ypbind. */ if ((*binding)->cache_bad) { __yp_unbind_nolock(domain); } else { pdomb = load_dom_binding_rsvdport( (*binding)->dom_binding, domain, &status); if (pdomb == 0) { (void) mutex_unlock(&bound_domains_lock); return (status); } pdomb->ref_count += 1; (void) mutex_unlock(&bound_domains_lock); *binding = pdomb; /* Return ptr to the binding entry */ return (0); } } while (hardlookup ? 1 : tries--) { if (first_try) first_try = 0; else { /* * ===> sleep() -- Ugh. And with the lock held, too. */ (void) sleep(_ypsleeptime*tries); } tb = __clnt_create_loopback(YPBINDPROG, YPBINDVERS, &err); if (tb == NULL) { if (ypbind_running(err, rpc_createerr.cf_stat)) continue; break; } ypbd.ypbind_domainname = domain; ypbd.ypbind_vers = YPVERS; /* * The interface to ypbindproc_domain_3 is MT-unsafe, but we're * OK as long as we're the only ones who call it and we * serialize all requests (for all domains). Otherwise, * change the interface (pass in the ypbind_resp struct). */ ypbind_resp = ypbindproc_domain_3(&ypbd, tb); /* * Although we talk to ypbind on loopback, * it gives us a udp address for the ypserv. */ if (ypbind_resp == NULL) { /* lost ypbind? */ clnt_perror(tb, "ypbindproc_domain_3: can't contact ypbind"); clnt_destroy(tb); tb = NULL; continue; } if (ypbind_resp->ypbind_status == YPBIND_SUCC_VAL) { /* * Local ypbind has let us in on the ypserv's address, * go get in touch with it ! */ pdomb = load_dom_binding_rsvdport( ypbind_resp->ypbind_resp_u.ypbind_bindinfo, domain, &status); if (pdomb == 0) { err = status; clnt_destroy(tb); tb = NULL; continue; } clnt_destroy(tb); pdomb->ref_count += 1; (void) mutex_unlock(&bound_domains_lock); *binding = pdomb; /* Return ptr to the binding entry */ return (0); /* This is the go path */ } if (ypbind_resp->ypbind_resp_u.ypbind_error == YPBIND_ERR_NOSERV) err = YPERR_DOMAIN; else err = YPERR_YPBIND; clnt_destroy(tb); tb = NULL; } if (tb != NULL) clnt_destroy(tb); (void) mutex_unlock(&bound_domains_lock); if (err) return (err); return (YPERR_DOMAIN); } int __yp_dobind_rsvdport( char *domain, struct dom_binding **binding) /* if result==0, ptr to dom_binding */ { /* traditional __yp_dobind_rsvdport loops forever so set hardlookup */ return (__yp_dobind_rsvdport_cflookup(domain, binding, 1)); } /* * This is a "wrapper" function for __yp_dobind for vanilla user-level * functions which neither know nor care about struct dom_bindings. */ int yp_bind(char *domain) { struct dom_binding *binding; int res; res = __yp_dobind(domain, &binding); if (res == 0) __yp_rel_binding(binding); return (res); } static char * __default_domain(void) { char temp[256]; (void) mutex_lock(&default_domain_lock); if (default_domain) { (void) mutex_unlock(&default_domain_lock); return (default_domain); } if (getdomainname(temp, sizeof (temp)) < 0) { (void) mutex_unlock(&default_domain_lock); return (0); } if (strlen(temp) > 0) { default_domain = malloc((strlen(temp) + 1)); if (default_domain == 0) { (void) mutex_unlock(&default_domain_lock); return (0); } (void) strcpy(default_domain, temp); (void) mutex_unlock(&default_domain_lock); return (default_domain); } (void) mutex_unlock(&default_domain_lock); return (0); } /* * This is a wrapper for the system call getdomainname which returns a * ypclnt.h error code in the failure case. It also checks to see that * the domain name is non-null, knowing that the null string is going to * get rejected elsewhere in the yp client package. */ int yp_get_default_domain(char **domain) { if ((*domain = __default_domain()) != 0) return (0); return (YPERR_YPERR); } /* * ===> Nobody uses this, do they? Can we nuke it? */ int usingypmap(char **ddn, char *map) { char in, *outval = NULL; int outvallen, stat; char *domain; if ((domain = __default_domain()) == 0) return (FALSE); *ddn = domain; /* does the map exist ? */ in = (char)0xff; stat = yp_match(domain, map, &in, 1, &outval, &outvallen); if (outval != NULL) free(outval); switch (stat) { case 0: /* it actually succeeded! */ case YPERR_KEY: /* no such key in map */ case YPERR_NOMORE: case YPERR_BUSY: return (TRUE); } return (FALSE); } /* * Creates a quick connection on a connection oriented loopback * transport. Fails quickly without timeout. Only naming service * it goes to is straddr.so. */ CLIENT * __clnt_create_loopback(rpcprog_t prog, rpcvers_t vers, int *err) { struct netconfig *nconf; CLIENT *clnt = NULL; void *nc_handle; /* Net config handle */ *err = 0; nc_handle = setnetconfig(); if (nc_handle == NULL) { /* fails to open netconfig file */ rpc_createerr.cf_stat = RPC_FAILED; *err = YPERR_RPC; return (NULL); } while (nconf = getnetconfig(nc_handle)) /* Try only one connection oriented loopback transport */ if ((strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) && ((nconf->nc_semantics == NC_TPI_COTS) || (nconf->nc_semantics == NC_TPI_COTS_ORD))) { clnt = getclnt(prog, vers, nconf, err); break; } (void) endnetconfig(nc_handle); if (clnt == NULL) { /* no loopback transport available */ if (rpc_createerr.cf_stat == 0) rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; if (*err == 0) *err = YPERR_RPC; } return (clnt); } static CLIENT * getclnt(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, int *err) { int fd; struct netbuf *svcaddr; /* servers address */ CLIENT *cl; struct nd_addrlist *nas; struct nd_hostserv rpcbind_hs; struct t_call sndcall; char uaddress[1024]; /* XXX maxlen ?? */ RPCB parms; enum clnt_stat clnt_st; char *ua; struct timeval tv = { 30, 0 }; if (nconf == NULL) { rpc_createerr.cf_stat = RPC_TLIERROR; *err = YPERR_RPC; return (NULL); } /* * The ypbind process might cache its transport address. * If we can get at it, then we will use it and avoid * wasting time talking to rpcbind. */ if (get_cached_transport(nconf, vers, uaddress, sizeof (uaddress))) { goto create_client; } /* * Check to see if local rpcbind is up or not. If it * isn't, it is best that the application should realize * yp is not up and take a remedial action. This is to * avoid the minute long timeout incurred by rpcbind_getaddr. * Looks like the only way to accomplish this it is to unfold * rpcb_getaddr and make a few changes. Alas ! */ rpcbind_hs.h_host = HOST_SELF_CONNECT; rpcbind_hs.h_serv = "rpcbind"; if (netdir_getbyname(nconf, &rpcbind_hs, &nas) != ND_OK) { rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; *err = YPERR_RPC; return (NULL); } if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) == -1) { rpc_createerr.cf_stat = RPC_TLIERROR; *err = YPERR_RPC; return (NULL); } if (t_bind(fd, NULL, NULL) == -1) { rpc_createerr.cf_stat = RPC_TLIERROR; *err = YPERR_RPC; (void) t_close(fd); return (NULL); } sndcall.addr = *(nas->n_addrs); sndcall.opt.len = 0; sndcall.udata.len = 0; if (t_connect(fd, &sndcall, NULL) == -1) { netdir_free((char *)nas, ND_ADDRLIST); rpc_createerr.cf_stat = RPC_TLIERROR; (void) t_close(fd); *err = YPERR_PMAP; return (NULL); } /* * Get the address of the server */ cl = clnt_tli_create(fd, nconf, nas->n_addrs, RPCBPROG, RPCBVERS, __ypipbufsize, __ypipbufsize); netdir_free((char *)nas, ND_ADDRLIST); if (cl == NULL) { (void) t_close(fd); *err = YPERR_PMAP; return (NULL); } parms.r_prog = prog; parms.r_vers = vers; parms.r_netid = nconf->nc_netid; parms.r_addr = nullstring; parms.r_owner = nullstring; ua = uaddress; clnt_st = CLNT_CALL(cl, RPCBPROC_GETADDR, xdr_rpcb, (char *)&parms, xdr_wrapstring, (char *)&ua, tv); (void) t_close(fd); clnt_destroy(cl); if (clnt_st != RPC_SUCCESS) { *err = YPERR_YPBIND; return (NULL); } if (strlen(uaddress) == 0) { *err = YPERR_YPBIND; rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; return (NULL); } create_client: svcaddr = uaddr2taddr(nconf, uaddress); cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr, prog, vers, __ypipbufsize, __ypipbufsize); netdir_free((char *)svcaddr, ND_ADDR); if (cl == NULL) { *err = YPERR_YPBIND; return (NULL); } /* * The fd should be closed while destroying the handle. */ return (cl); } static int get_cached_transport(struct netconfig *nconf, int vers, char *uaddress, int ulen) { ssize_t st; int fd; (void) snprintf(uaddress, ulen, "%s/xprt.%s.%d", BINDING, nconf->nc_netid, vers); fd = open(uaddress, O_RDONLY); if (fd == -1) return (0); /* if first byte is not locked, then ypbind must not be running */ st = lockf(fd, F_TEST, 1); if (st != -1 || (errno != EAGAIN && errno != EACCES)) { (void) close(fd); return (0); } st = read(fd, uaddress, ulen); if (st == -1) { (void) close(fd); return (0); } (void) close(fd); return (1); } static ypbind_resp * get_cached_domain(char *domain) { __NSL_FILE *fp; int st; char filename[300]; static ypbind_resp res; XDR xdrs; (void) snprintf(filename, sizeof (filename), "%s/%s/cache_binding", BINDING, domain); fp = __nsl_fopen(filename, "r"); if (fp == 0) return (0); /* if first byte is not locked, then ypbind must not be running */ st = lockf(__nsl_fileno(fp), F_TEST, 1); if (st != -1 || (errno != EAGAIN && errno != EACCES)) { (void) __nsl_fclose(fp); return (0); } __nsl_xdrstdio_create(&xdrs, fp, XDR_DECODE); (void) memset((char *)&res, 0, sizeof (res)); st = xdr_ypbind_resp(&xdrs, &res); xdr_destroy(&xdrs); (void) __nsl_fclose(fp); if (st) return (&res); return (0); } static int ypbind_running(int err, int status) { char filename[300]; int st; int fd; (void) snprintf(filename, sizeof (filename), "%s/ypbind.pid", BINDING); fd = open(filename, O_RDONLY); if (fd == -1) { if ((err == YPERR_YPBIND) && (status != RPC_PROGNOTREGISTERED)) return (1); return (0); } /* if first byte is not locked, then ypbind must not be running */ st = lockf(fd, F_TEST, 1); if (st != -1 || (errno != EAGAIN && errno != EACCES)) { (void) close(fd); return (0); } (void) close(fd); return (1); } static void set_rdev(struct dom_binding *pdomb) { int fd; struct stat stbuf; if (clnt_control(pdomb->dom_client, CLGET_FD, (char *)&fd) != TRUE || _FSTAT(fd, &stbuf) == -1) { syslog(LOG_DEBUG, "ypbind client: can't get rdev"); pdomb->fd = -1; return; } pdomb->fd = fd; pdomb->rdev = stbuf.st_rdev; } static int check_rdev(struct dom_binding *pdomb) { struct stat stbuf; if (pdomb->fd == -1) return (1); /* can't check it, assume it is okay */ if (_FSTAT(pdomb->fd, &stbuf) == -1) { syslog(LOG_DEBUG, "yp_bind client: can't stat %d", pdomb->fd); /* could be because file descriptor was closed */ /* it's not our file descriptor, so don't try to close it */ clnt_control(pdomb->dom_client, CLSET_FD_NCLOSE, NULL); return (0); } if (pdomb->rdev != stbuf.st_rdev) { syslog(LOG_DEBUG, "yp_bind client: fd %d changed, old=0x%x, new=0x%x", pdomb->fd, pdomb->rdev, stbuf.st_rdev); /* it's not our file descriptor, so don't try to close it */ clnt_control(pdomb->dom_client, CLSET_FD_NCLOSE, NULL); return (0); } return (1); /* fd is okay */ }