yp_bind.c revision 004388ebfdfe2ed7dfd2d153a876dfcc22d2c006
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2006 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netconfig.h>
#include <netdir.h>
#include <syslog.h>
#include "yp_b.h"
/* This should match the one in ypbind.c */
extern int getdomainname(char *, int);
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
*/
unsigned int _ypsleeptime = YPSLEEPTIME;
/*
* Time parameters when talking to the ypserv process
*/
#ifdef DEBUG
#else
#endif
/* get a new YP server before */
/* we'll settle for an old one. */
struct timeval _ypserv_timeout = {
YPTIMEOUT, /* Seconds */
0 /* Microseconds */
};
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.
*/
/*
* 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;
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.
*/
if (!p->cache_bad) {
p->cache_bad = 1;
break;
}
free_dom_binding(p);
break;
}
}
}
void
{
(void) mutex_lock(&bound_domains_lock);
(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)
{
struct dom_binding *p, *q;
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
{
struct dom_binding *pdomb;
struct ypbind_resp *ypbind_resp;
int status;
/*
* 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
*/
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 (pdomb == 0)
return (FALSE);
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
{
struct ypbind_binding *binding = 0;
int status;
struct ypbind_resp resp;
struct dom_binding *pdomb;
if (nconf == 0)
goto err;
if (svcaddr == 0)
goto err;
goto err;
goto err;
if (binding == 0)
goto err;
if (binding->ypbind_servername == 0)
goto err;
(void) mutex_lock(&bound_domains_lock);
newborn();
(void) mutex_unlock(&bound_domains_lock);
return (pdomb != 0);
err:
if (nconf)
if (svcaddr) {
}
if (binding) {
if (binding->ypbind_servername)
}
return (0);
}
int
if (ret == 0)
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 *
{
int fd;
struct dom_binding *pdomb;
*err = YPERR_RESRC;
return (NULL);
}
/*
* Open up a path to the server, which will remain active globally.
*/
clnt_pcreateerror("yp_bind: clnt_tli_create");
return (NULL);
}
#ifdef DEBUG
(void) printf("yp_bind: clnt_tli_create suceeded\n");
#endif
*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.
*/
return (pdomb);
}
/*
* XXX special code for handling C2 (passwd.adjunct) lookups when we need
* a reserved port.
*/
static int
{
int fd;
return (-1);
if (fd == -1)
return (-1);
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 *
int *err)
{
struct dom_binding *pdomb;
int fd;
*err = YPERR_RESRC;
return (NULL);
}
/*
* Open up a path to the server, which will remain active globally.
*/
if (fd < 0) {
clnt_pcreateerror("yp_bind: tli_open_rsvdport");
return (NULL);
}
clnt_pcreateerror("yp_bind: clnt_tli_create");
return (NULL);
}
#ifdef DEBUG
(void) printf("yp_bind: clnt_tli_create suceeded\n");
#endif
*err = YPERR_RESRC;
return (NULL);
}
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
char *domain,
int hardlookup)
{
struct ypbind_domain ypbd;
int first_try = 1;
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 the cache is okay and if the underlying file
* descriptor is okay (application did not close it).
* then use the binding.
*/
(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.
*/
} else {
(void) mutex_unlock(&bound_domains_lock);
(void) mutex_lock(&bound_domains_lock);
(void) mutex_unlock(&bound_domains_lock);
return (0);
}
}
}
if (first_try)
first_try = 0;
else {
/*
* ===> sleep() -- Ugh. And with the lock held, too.
*/
(void) sleep(_ypsleeptime);
}
continue;
break;
}
/*
* 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).
*/
/*
* Although we talk to ypbind on loopback,
* it gives us a udp address for the ypserv.
*/
if (ypbind_resp == NULL) {
/* lost ypbind? */
"ypbindproc_domain_3: can't contact ypbind");
continue;
}
/*
* Local ypbind has let us in on the ypserv's address,
* go get in touch with it !
*/
if (pdomb == 0) {
continue;
}
(void) mutex_unlock(&bound_domains_lock);
return (0); /* This is the go path */
}
err = YPERR_DOMAIN;
else
err = YPERR_YPBIND;
}
(void) mutex_unlock(&bound_domains_lock);
if (err)
return (err);
return (YPERR_DOMAIN);
}
int
char *domain,
{
/* traditional __yp_dobind loops forever so set hardlookup */
}
void
{
(void) mutex_lock(&bound_domains_lock);
(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
char *domain,
int hardlookup)
{
struct ypbind_domain ypbd;
int first_try = 1;
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 the cache is bad, yp_unbind() the entry again and then
* talk to ypbind.
*/
} else {
(*binding)->dom_binding,
if (pdomb == 0) {
(void) mutex_unlock(&bound_domains_lock);
return (status);
}
(void) mutex_unlock(&bound_domains_lock);
return (0);
}
}
if (first_try)
first_try = 0;
else {
/*
* ===> sleep() -- Ugh. And with the lock held, too.
*/
}
continue;
break;
}
/*
* 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).
*/
/*
* Although we talk to ypbind on loopback,
* it gives us a udp address for the ypserv.
*/
if (ypbind_resp == NULL) {
/* lost ypbind? */
"ypbindproc_domain_3: can't contact ypbind");
continue;
}
/*
* Local ypbind has let us in on the ypserv's address,
* go get in touch with it !
*/
if (pdomb == 0) {
continue;
}
(void) mutex_unlock(&bound_domains_lock);
return (0); /* This is the go path */
}
err = YPERR_DOMAIN;
else
err = YPERR_YPBIND;
}
(void) mutex_unlock(&bound_domains_lock);
if (err)
return (err);
return (YPERR_DOMAIN);
}
int
char *domain,
{
/* traditional __yp_dobind_rsvdport loops forever so set hardlookup */
}
/*
* This is a "wrapper" function for __yp_dobind for vanilla user-level
* functions which neither know nor care about struct dom_bindings.
*/
int
{
struct dom_binding *binding;
int res;
if (res == 0)
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);
}
(void) mutex_unlock(&default_domain_lock);
return (0);
}
if (default_domain == 0) {
(void) mutex_unlock(&default_domain_lock);
return (0);
}
(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
{
char *domain;
if ((domain = __default_domain()) == 0)
return (FALSE);
/* does the map exist ? */
in = (char)0xff;
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 *
{
void *nc_handle; /* Net config handle */
*err = 0;
nc_handle = setnetconfig();
/* fails to open netconfig file */
return (NULL);
}
/* Try only one connection oriented loopback transport */
break;
}
(void) endnetconfig(nc_handle);
if (rpc_createerr.cf_stat == 0)
}
return (clnt);
}
static CLIENT *
{
int fd;
struct nd_addrlist *nas;
struct nd_hostserv rpcbind_hs;
char *ua;
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.
*/
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 !
*/
return (NULL);
}
return (NULL);
}
return (NULL);
}
*err = YPERR_PMAP;
return (NULL);
}
/*
* Get the address of the server
*/
*err = YPERR_PMAP;
return (NULL);
}
if (clnt_st != RPC_SUCCESS) {
*err = YPERR_YPBIND;
return (NULL);
}
*err = YPERR_YPBIND;
return (NULL);
}
*err = YPERR_YPBIND;
return (NULL);
}
/*
* The fd should be closed while destroying the handle.
*/
return (cl);
}
static int
int ulen)
{
int fd;
if (fd == -1)
return (0);
/* if first byte is not locked, then ypbind must not be running */
return (0);
}
if (st == -1) {
return (0);
}
return (1);
}
static ypbind_resp *
get_cached_domain(char *domain)
{
int st;
char filename[300];
static ypbind_resp res;
if (fp == 0)
return (0);
/* if first byte is not locked, then ypbind must not be running */
return (0);
}
xdr_destroy(&xdrs);
if (st)
return (&res);
return (0);
}
static int
{
char filename[300];
int st;
int fd;
if (fd == -1) {
return (1);
return (0);
}
/* if first byte is not locked, then ypbind must not be running */
return (0);
}
return (1);
}
static void
{
int fd;
return;
}
}
static int
{
return (1); /* can't check it, assume it is okay */
/* could be because file descriptor was closed */
/* it's not our file descriptor, so don't try to close it */
return (0);
}
"yp_bind client: fd %d changed, old=0x%x, new=0x%x",
/* it's not our file descriptor, so don't try to close it */
return (0);
}
return (1); /* fd is okay */
}