2021-10-11 18:37:13 -03:00

1119 lines
29 KiB
C

#ifndef lint
static char sccsid[] = "@(#)ypbind.c 1.1 94/10/31 Copyr 1990 Sun Micro";
#endif
/*
* This constructs a list of servers by domains, and keeps more-or-less up to
* date track of those server's reachability.
*/
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <rpc/rpc.h>
#include <rpc/svc.h>
#include <sys/dir.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypv1_prot.h>
#include <rpcsvc/ypclnt.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#define CACHE_DIR "/var/yp/binding"
/*
* The domain struct is the data structure used by the NIS binder to remember
* mappings of domain to a server. The list of domains is pointed to by
* known_domains. Domains are added when the NIS binder gets binding requests
* for domains which are not currently on the list. Once on the list, an
* entry stays on the list forever. Bindings are initially made by means of
* a broadcast method, using functions ypbind_broadcast_bind and
* ypbind_broadcast_ack. This means of binding is re-done any time the domain
* becomes unbound, which happens when a server doesn't respond to a ping.
* current_domain is used to communicate among the various functions in this
* module; it is set by ypbind_get_binding.
*
*/
struct domain {
struct domain *dom_pnext;
char dom_name[MAXNAMLEN + 1];
unsigned short dom_vers; /* YPVERS or YPOLDVERS */
bool dom_boundp;
CLIENT *ping_clnt;
struct in_addr dom_serv_addr;
unsigned short int dom_serv_port;
int dom_report_success; /* Controls msg to /dev/console*/
int dom_broadcaster_pid;
int bindfile; /* File with binding info in it */
int broadcaster_fd;
FILE *broadcaster_pipe; /* to get answer from broadcaster*/
XDR broadcaster_xdr; /**/
struct timeval lastping;
};
static int ping_sock = RPC_ANYSOCK;
struct domain *known_domains = (struct domain *) NULL;
struct domain *current_domain; /* Used by ypbind_broadcast_ack, set
* by all callers of clnt_broadcast */
struct domain *broadcast_domain; /* Set by ypbind_get_binding, used
* by the mainline. */
SVCXPRT *tcphandle;
SVCXPRT *udphandle;
#define BINDING_TRIES 1 /* Number of times we'll broadcast to
* try to bind default domain. */
#define PINGTOTTIM 20 /* Total seconds for ping timeout */
#define PINGINTERTRY 10
#define SETDOMINTERTRY 20
#define SETDOMTOTTIM 60
#ifdef VERBOSE
int silent = FALSE;
#else
int silent = TRUE;
#endif
#define YPSETLOCAL 3
static int secure = FALSE; /* running more securely; associated with the -s flag */
static int securebind = FALSE; /* running insecurely*/
static int setok = FALSE; /* accept ypset*/
extern int errno;
void dispatch();
void ypbind_dispatch();
void ypbind_olddispatch();
void ypbind_get_binding();
void ypbind_set_binding();
void ypbind_pipe_setdom();
struct domain *ypbind_point_to_domain();
bool ypbind_broadcast_ack();
bool ypbind_ping();
void ypbind_init_default();
void broadcast_proc_exit();
extern bool xdr_ypdomain_wrap_string();
extern bool xdr_ypbind_resp();
static int interlock_fd = -1;
int unregister();
main(argc, argv)
int argc;
char **argv;
{
int pid;
int t;
int i;
int readfds;
char *pname;
bool true;
if (geteuid()!= 0) {
fprintf(stderr,"ypbind: can only be run by root (usually from /etc/rc.local)!\n");
exit(0);
}
setreuid(0,0);
#ifdef LOCK_EX
interlock();
#endif
for (i=1;i<=SIGTERM;i++){
if (i != SIGHUP) signal(i,unregister);
else signal(i, SIG_IGN);
}
(void) pmap_unset(YPBINDPROG, YPBINDVERS);
(void) pmap_unset(YPBINDPROG, YPBINDOLDVERS);
/*
* Scrap the initial binding processing for now, and see if we like
* fast startup better than initial bindings.
*/
#ifdef INIT_DEFAULT
ypbind_init_default();
#endif
/*
* Check to see if we are running "secure" which means that we should
* require that the server be using a reserved port. We are running
* secure if the -s option is specified
*/
argc--;
argv++;
while (argc > 0) {
if (!strcmp(*argv,"-s")) {
secure = TRUE;
securebind = TRUE;
}
else if (!strcmp(*argv,"-secure")) {
secure = TRUE;
securebind = TRUE;
}
else if (!strcmp(*argv,"-insecure")) {
secure = FALSE;
securebind = FALSE;
}
else if (!strcmp(*argv,"-v")) {
silent = FALSE;
fprintf(stderr, "running verbose (for debugging)\n");
}
else if (!strcmp(*argv,"-ypset")) {
setok = TRUE;
}
else if (!strcmp(*argv,"-ypsetme")) {
setok = YPSETLOCAL;
}
else {
fprintf(stderr, "usage: ypbind [-s] [-v] [-insecure] [-ypset] [-ypsetme]\n");
exit(1);
}
argc--,
argv++;
}
signal(SIGHUP,SIG_IGN); /*try this*/
if (silent) {
pid = fork();
if (pid == -1) {
(void) fprintf(stderr, "ypbind: fork failure.\n");
(void) fflush(stderr);
abort();
}
if (pid != 0) {
exit(0);
}
{
int nd = getdtablesize();
for (t = 0; t < nd ; t++) {
if (t!=interlock_fd)
(void) close(t);
}
}
(void) open("/dev/console", O_WRONLY+O_NOCTTY); /*posix*/
(void) dup2(0, 1);
(void) dup2(0, 2);
t = open("/dev/tty", 2);
if (t >= 0) {
(void) ioctl(t, TIOCNOTTY, (char *)0);
(void) close(t);
}
(void) setpgrp(getpid(),0); /*posix should be setsid XXX*/
}
else (void) setpgrp(getpid(),0); /*posix should be setsid XXX */
if (secure==TRUE) {
fprintf(stderr, "ypbind: Secure mode sunos 3.x servers rejected.\n");
}
if (setok==TRUE) {
fprintf(stderr, "ypbind -ypset: allowing ypset! (this is insecure)\n");
}
if (setok==YPSETLOCAL) {
fprintf(stderr, "ypbind -ypsetme: allowing local ypset! (this is insecure)\n");
}
if ((int) signal(SIGCHLD, broadcast_proc_exit) == -1) {
(void) fprintf(stderr,
"ypbind: Can't catch broadcast process exit signal.\n");
(void) fflush(stderr);
abort();
}
if ((int) signal(SIGPIPE, SIG_IGN) == -1) {
(void) fprintf(stderr,
"ypbind: Can't ignore pipe exit signal.\n");
(void) fflush(stderr);
abort();
}
/* Open a socket for pinging everyone can use */
ping_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (ping_sock < 0) {
(void) fprintf(stderr,
"ypbind: Cannot create socket for pinging.\n");
(void) fflush(stderr);
abort();
}
/*
* if not running c2 secure, don't use privileged ports.
* Accomplished by side effect of not being root when creating
* rpc based sockets.
*/
if (! securebind) {
(void) setreuid(-1, 3);
}
if ((tcphandle = svctcp_create(RPC_ANYSOCK,
RPCSMALLMSGSIZE, RPCSMALLMSGSIZE)) == NULL) {
(void) fprintf(stderr, "ypbind: can't create tcp service.\n");
(void) fflush(stderr);
abort();
}
if (!svc_register(tcphandle, YPBINDPROG, YPBINDVERS,
ypbind_dispatch, IPPROTO_TCP) ) {
(void) fprintf(stderr,
"ypbind: can't register tcp service.\n");
(void) fflush(stderr);
abort();
}
if ((udphandle = svcudp_bufcreate(RPC_ANYSOCK,
RPCSMALLMSGSIZE, RPCSMALLMSGSIZE)) == (SVCXPRT *) NULL) {
(void) fprintf(stderr, "ypbind: can't create udp service.\n");
(void) fflush(stderr);
abort();
}
if (!svc_register(udphandle, YPBINDPROG, YPBINDVERS,
ypbind_dispatch, IPPROTO_UDP) ) {
(void) fprintf(stderr,
"ypbind: can't register udp service.\n");
(void) fflush(stderr);
abort();
}
if (!svc_register(tcphandle, YPBINDPROG, YPBINDOLDVERS,
ypbind_olddispatch, IPPROTO_TCP) ) {
(void) fprintf(stderr,
"ypbind: can't register tcp service.\n");
(void) fflush(stderr);
abort();
}
if (!svc_register(udphandle, YPBINDPROG, YPBINDOLDVERS,
ypbind_olddispatch, IPPROTO_UDP) ) {
(void) fprintf(stderr,
"ypbind: can't register udp service.\n");
(void) fflush(stderr);
abort();
}
/* undo the gross hack associated with c2 security */
(void) setreuid(-1, 0);
for (;;) {
readfds = svc_fds;
errno = 0;
switch ( (int) select(32, &readfds, NULL, NULL, NULL) ) {
case -1: {
if (errno != EINTR) {
(void) fprintf (stderr,
"ypbind: bad fds bits in main loop select mask.\n");
}
break;
}
case 0: {
(void) fprintf (stderr,
"ypbind: invalid timeout in main loop select.\n");
break;
}
default: {
svc_getreq (readfds);
break;
}
}
}
/* NOTREACHED */
}
/*
* ypbind_dispatch and ypbind_olddispatch are wrappers for dispatch which
* remember which protocol the requestor is looking for. The theory is,
* that since YPVERS and YPBINDVERS are defined in the same header file, if
* a request comes in on the old binder protocol, the requestor is looking
* for the old NIS server.
*/
void
ypbind_dispatch(rqstp, transp)
struct svc_req *rqstp;
SVCXPRT *transp;
{
dispatch(rqstp, transp, (unsigned short) YPVERS);
}
void
ypbind_olddispatch(rqstp, transp)
struct svc_req *rqstp;
SVCXPRT *transp;
{
dispatch(rqstp, transp, (unsigned short) YPOLDVERS);
}
/*
* This dispatches to binder action routines.
*/
void
dispatch(rqstp, transp, vers)
struct svc_req *rqstp;
SVCXPRT *transp;
unsigned short vers;
{
switch (rqstp->rq_proc) {
case YPBINDPROC_NULL:
if (!svc_sendreply(transp, xdr_void, 0) ) {
(void) fprintf(stderr,
"ypbind: Can't reply to rpc call.\n");
}
break;
case YPBINDPROC_DOMAIN:
ypbind_get_binding(rqstp, transp, vers, FALSE, NULL );
break;
case YPBINDPROC_SETDOM:
ypbind_set_binding(rqstp, transp, vers);
break;
default:
svcerr_noproc(transp);
break;
}
}
/*
* This is a Unix SIGCHILD handler which notices when a broadcaster child
* process has exited, and retrieves the exit status. The broadcaster pid
* is set to 0. If the broadcaster succeeded, dom_report_success will be
* be set to -1.
*/
void
broadcast_proc_exit()
{
int pid;
union wait wait_status;
register struct domain *pdom;
struct ypbind_setdom req;
pid = 0;
for (;;) {
pid = wait3(&wait_status, WNOHANG, NULL);
if (pid == 0) {
return;
} else if (pid == -1) {
return;
}
for (pdom = known_domains; pdom != (struct domain *)NULL;
pdom = pdom->dom_pnext) {
if (pdom->dom_broadcaster_pid == pid) {
pdom->dom_broadcaster_pid = 0;
if ((wait_status.w_termsig == 0) &&
(wait_status.w_retcode == 0))
{
if (pdom->broadcaster_pipe) {
pdom->dom_report_success = -1;
/*do the xdr*/
if (xdr_ypbind_setdom(
&(pdom->broadcaster_xdr),&req)){
/*might check domain and version here*/
pdom->dom_serv_addr = req.ypsetdom_addr;
pdom->dom_serv_port = req.ypsetdom_port;
pdom->dom_boundp = TRUE;
/* get rid of old pinging client if one exists */
if (pdom->ping_clnt != (CLIENT *)NULL) {
clnt_destroy(pdom->ping_clnt);
pdom->ping_clnt = (CLIENT *)NULL;
}
}
else {
fprintf(stderr,"xdr_ypbind_setdom fails\n");
}
}
else {
fprintf(stderr,"ypbind:internal error -- no broadcaster pipe.\n");
}
}
/*Success or failure free the pipe*/
if (pdom->broadcaster_pipe) {
xdr_destroy( &(pdom->broadcaster_xdr));
fclose(pdom->broadcaster_pipe);
}
close(pdom->broadcaster_fd);
pdom->broadcaster_pipe=0;
pdom->broadcaster_fd= -1;
}
}
}
}
/*
* This returns the current binding for a passed domain.
*/
void
ypbind_get_binding(rqstp, transp, vers, vrfy, vfname)
struct svc_req *rqstp;
register SVCXPRT *transp;
unsigned short vers;
bool_t vrfy;
char *vfname;
{
char domain_name[YPMAXDOMAIN + 1];
char *pdomain_name = domain_name;
char *pname;
struct stat buf;
struct ypbind_resp response;
bool newbinding=FALSE; /*better set variables before using them*/
char outstring[YPMAXDOMAIN + 256];
int broadcaster_pid;
struct domain *v1binding;
int fildes[2]; /*pipe*/
int oldmask;
struct timeval tp; /*for holddown*/
int i;
#define YPBIND_PINGHOLD_DOWN 5 /*seconds*/
if (vrfy) strcpy(pdomain_name,vfname);
else if (!svc_getargs(transp, xdr_ypdomain_wrap_string, &pdomain_name) ) {
svcerr_decode(transp);
return;
}
if ( (current_domain = ypbind_point_to_domain(pdomain_name, vers) ) !=
(struct domain *) NULL) {
/*
* Ping the server to make sure it is up.
*/
if (current_domain->dom_boundp) {
(void) gettimeofday(&tp,0);
if ((tp.tv_sec - current_domain->lastping.tv_sec)>
YPBIND_PINGHOLD_DOWN){
newbinding = ypbind_ping(current_domain);
}
}
/*
* Bound or not, return the current state of the binding.
*/
if (current_domain->dom_boundp) {
response.ypbind_status = YPBIND_SUCC_VAL;
response.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr =
current_domain->dom_serv_addr;
response.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port =
current_domain->dom_serv_port;
} else {
response.ypbind_status = YPBIND_FAIL_VAL;
response.ypbind_respbody.ypbind_error =
YPBIND_ERR_NOSERV;
}
} else {
response.ypbind_status = YPBIND_FAIL_VAL;
response.ypbind_respbody.ypbind_error = YPBIND_ERR_RESC;
}
#ifndef NO_BINDING_FILE
/* If this is a new binding, update the binding file */
if (newbinding) {
/*
* This is pretty much a gross hack to speed up binding
* operations using the yp_bind library call. By saving
* this info in a file we save an rpc call and a server
* 'ping'
*/
tryagain:
/* If no file is open then try to open it. */
if (current_domain->bindfile == -1) {
/* Generate the filename required ... */
sprintf(outstring, "%s/%s.%d", CACHE_DIR,
current_domain->dom_name,
current_domain->dom_vers);
current_domain->bindfile =
open(outstring, O_RDWR+O_CREAT, 0644);
/* XXX remove when the new libc routines are ready */
if (current_domain->bindfile != -1)
flock(current_domain->bindfile, LOCK_EX);
}
/* Write the binding information to it ... */
if (current_domain->bindfile != -1) {
lseek(current_domain->bindfile, 0L, L_SET);
write(current_domain->bindfile, &(udphandle->xp_port),
sizeof(u_short));
write(current_domain->bindfile, &response,
sizeof(response));
} else {
/*
* If it failed to open, check to see that the
* directory exists, if it does not exist, create
* it and try again. If it does exist then abort
* and don't bother with the cache file.
*/
if (stat(CACHE_DIR,&buf) < 0) {
if (errno == ENOENT) mkdir("/var/yp", 0655);
mkdir(CACHE_DIR, 0655);
goto tryagain;
}
}
}
#endif
if (!vrfy ){
if (!svc_sendreply(transp, xdr_ypbind_resp, &response) ) {
(void) fprintf(stderr,
"ypbind: Can't respond to rpc request.\n");
}
if (!svc_freeargs(transp, xdr_ypdomain_wrap_string, &pdomain_name) ) {
(void) fprintf(stderr,
"ypbind: ypbind_get_binding can't free args.\n");
}
}
if ((current_domain) && (!current_domain->dom_boundp) &&
(!current_domain->dom_broadcaster_pid)) {
/*
* The current domain is unbound, and there is no broadcaster
* process active now. Fork off a child who will yell out on
* the net. Because of the flavor of request we're making of
* the server, we only expect positive ("I do serve this
* domain") responses.
*/
broadcast_domain = current_domain;
broadcast_domain->dom_report_success++;
pname = current_domain->dom_name;
if ( pipe(fildes) >=0){
oldmask=sigblock(sigmask(SIGCHLD));
if ( (broadcaster_pid = fork() ) == 0) {
int true = 1;
for (i=1;i<SIGPIPE;i++) signal(i,SIG_DFL);
for (i=SIGPIPE+1;i<=SIGTERM;i++) signal(i,SIG_DFL);
if (interlock_fd>=0) close(interlock_fd);
/*child shouldn't hold lock*/
sigsetmask(oldmask);
current_domain->broadcaster_fd=fildes[1];
current_domain->broadcaster_pipe=fdopen(fildes[1],"w");
if (current_domain->broadcaster_pipe)
xdrstdio_create(&(current_domain->broadcaster_xdr), (current_domain->broadcaster_pipe),XDR_ENCODE);
else{
perror("fdopen-pipe");
exit(-1);
}
(void) clnt_broadcast(YPPROG, vers,
YPPROC_DOMAIN_NONACK, xdr_ypdomain_wrap_string,
&pname, xdr_int, &true, ypbind_broadcast_ack);
if (current_domain->dom_boundp) {
/*
* Send out a set domain request to our parent
*/
ypbind_pipe_setdom(current_domain,pname,
current_domain->dom_serv_addr,
current_domain->dom_serv_port, vers);
if (current_domain->dom_report_success > 0) {
(void) sprintf(outstring,
"NIS: server for domain \"%s\" OK",
pname);
writeit(outstring);
}
exit(0);
} else {
/*
* Hack time. If we're looking for a current-
* version server and can't find one, but we
* do have a previous-version server bound, then
* suppress the console message.
*/
if (vers == YPVERS && ((v1binding =
ypbind_point_to_domain(pname, YPOLDVERS) ) !=
(struct domain *) NULL) &&
v1binding->dom_boundp) {
exit(1);
}
(void) sprintf(outstring,
"NIS: server not responding for domain \"%s\"; still trying",
pname);
writeit(outstring);
exit(1);
}
} else if (broadcaster_pid == -1) {
sigsetmask(oldmask);
close(fildes[0]);
close(fildes[1]);
(void) fprintf(stderr,
"ypbind: broadcaster fork failure.\n");
} else {
/*parent*/
close (fildes[1]);
current_domain->broadcaster_fd=fildes[0];
current_domain->broadcaster_pipe=fdopen(fildes[0],"r");
if (current_domain->broadcaster_pipe)
xdrstdio_create(&(current_domain->broadcaster_xdr), (current_domain->broadcaster_pipe),XDR_DECODE);
current_domain->dom_broadcaster_pid = broadcaster_pid;
sigsetmask(oldmask);
}
} else {
(void) fprintf(stderr,
"ypbind: broadcaster pipe failure.\n");
}
}
}
static int
writeit(s)
char *s;
{
FILE *f;
if ((f = fopen("/dev/console", "w")) != NULL) {
(void) fprintf(f, "%s.\n", s);
(void) fclose(f);
}
}
/*
* This sends a (current version) ypbind "Set domain" message back to our
* parent. The version embedded in the protocol message is that which is passed
* to us as a parameter.
*/
void
ypbind_pipe_setdom(dp, dom, addr, port, vers)
struct domain *dp;
char *dom;
struct in_addr addr;
unsigned short int port;
unsigned short int vers;
{
struct ypbind_setdom req;
strcpy(req.ypsetdom_domain, dom);
req.ypsetdom_addr = addr;
req.ypsetdom_port = port;
req.ypsetdom_vers = vers;
xdr_ypbind_setdom(&(dp->broadcaster_xdr),&req);
xdr_destroy(&(dp->broadcaster_xdr));
fclose(dp->broadcaster_pipe);
close(dp->broadcaster_fd);
dp->broadcaster_fd = -1;
dp->broadcaster_pipe=(FILE *) NULL;
}
/*
* This sets the internet address and port for the passed domain to the
* passed values, and marks the domain as supported. This accepts both the
* old style message (in which case the version is assumed to be that of the
* requestor) or the new one, in which case the protocol version is included
* in the protocol message. This allows our child process (which speaks the
* current protocol) to look for NIS servers on behalf old-style clients.
*/
void
ypbind_set_binding(rqstp, transp, vers)
struct svc_req *rqstp;
register SVCXPRT *transp;
unsigned short vers;
{
struct ypbind_setdom req;
struct ypbind_oldsetdom oldreq;
unsigned short version;
struct in_addr addr;
struct sockaddr_in *who;
unsigned short int port;
char *domain;
if (vers == YPVERS) {
if (!svc_getargs(transp, xdr_ypbind_setdom, &req) ) {
svcerr_decode(transp);
return;
}
version = req.ypsetdom_vers;
addr = req.ypsetdom_addr;
port = req.ypsetdom_port;
domain = req.ypsetdom_domain;
} else {
if (!svc_getargs(transp, _xdr_ypbind_oldsetdom, &oldreq) ) {
svcerr_decode(transp);
return;
}
version = vers;
addr = oldreq.ypoldsetdom_addr;
port = oldreq.ypoldsetdom_port;
domain = oldreq.ypoldsetdom_domain;
}
/* find out who originated the request */
who = svc_getcaller(transp);
if (setok == FALSE) {
fprintf(stderr,"ypbind: Set domain request to host %s, ",
inet_ntoa(addr));
fprintf(stderr,"from host %s, failed (ypset not allowed)!\n",
inet_ntoa(who->sin_addr));
svcerr_noprog(transp); /* double up meaning for ypset */
return;
}
/* This code implements some restrictions on who can set the *
* NIS server for this host */
/* This policy is that root can set the NIS server to anything, *
* everyone else can't. This should also check for a valid NIS *
* server but time is short, 4.1 for sure */
if (ntohs(who->sin_port) > IPPORT_RESERVED) {
fprintf(stderr,"ypbind: Set domain request to host %s, ",
inet_ntoa(addr));
fprintf(stderr,"from host %s, failed (bad port).\n",
inet_ntoa(who->sin_addr));
svcerr_systemerr(transp);
return;
}
if (setok == YPSETLOCAL) {
if (!chklocal(who->sin_addr)) {
fprintf(stderr,"ypbind: Set domain request to host %s, ",
inet_ntoa(addr));
fprintf(stderr,"from host %s, failed (not local).\n",
inet_ntoa(who->sin_addr));
svcerr_systemerr(transp);
return;
}
}
/* Now check the credentials */
if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
if (((struct authunix_parms *)rqstp->rq_clntcred)->aup_uid != 0) {
fprintf(stderr,"ypbind: Set domain request to host %s,",
inet_ntoa(addr));
fprintf(stderr," from host %s, failed (not root).\n",
inet_ntoa(who->sin_addr));
svcerr_systemerr(transp);
return;
}
} else {
fprintf(stderr, "ypbind: Set domain request to host %s,",
inet_ntoa(addr));
fprintf(stderr," from host %s, failed (credentials).\n",
inet_ntoa(who->sin_addr));
svcerr_weakauth(transp);
return;
}
if (!svc_sendreply(transp, xdr_void, 0) ) {
fprintf(stderr, "ypbind: Can't reply to rpc call.\n");
}
if ( (current_domain = ypbind_point_to_domain(domain,
version) ) != (struct domain *) NULL) {
current_domain->dom_serv_addr = addr;
current_domain->dom_serv_port = port;
current_domain->dom_boundp = TRUE;
current_domain->lastping.tv_sec = 0;
current_domain->lastping.tv_usec = 0; /*require ping*/
/* get rid of old pinging client if one exists */
if (current_domain->ping_clnt != (CLIENT *)NULL) {
clnt_destroy(current_domain->ping_clnt);
current_domain->ping_clnt = (CLIENT *)NULL;
}
ypbind_get_binding(rqstp, transp, vers, TRUE, domain ); /*
make sure this binding is really set in binding file*/
}
}
/*
* This returns a pointer to a domain entry. If no such domain existed on
* the list previously, an entry will be allocated, initialized, and linked
* to the list. Note: If no memory can be malloc-ed for the domain structure,
* the functional value will be (struct domain *) NULL.
*/
static struct domain *
ypbind_point_to_domain(pname, vers)
register char *pname;
unsigned short vers;
{
register struct domain *pdom;
for (pdom = known_domains; pdom != (struct domain *)NULL;
pdom = pdom->dom_pnext) {
if (!strcmp(pname, pdom->dom_name) && vers == pdom->dom_vers)
return (pdom);
}
/* Not found. Add it to the list */
if (pdom = (struct domain *)malloc(sizeof (struct domain))) {
pdom->dom_pnext = known_domains;
known_domains = pdom;
strcpy(pdom->dom_name, pname);
pdom->dom_vers = vers;
pdom->dom_boundp = FALSE;
pdom->ping_clnt = (CLIENT *)NULL;
pdom->dom_report_success = -1;
pdom->dom_broadcaster_pid = 0;
pdom->bindfile = -1;
pdom->broadcaster_fd = -1;
pdom->broadcaster_pipe = (FILE *)NULL;
}
return (pdom);
}
/*
* This is called by the broadcast rpc routines to process the responses
* coming back from the broadcast request. Since the form of the request
* which is used in ypbind_broadcast_bind is "respond only in the positive
* case", we know that we have a server. If we should be running secure,
* return FALSE if this server is not using a reserved port. Otherwise,
* the internet address of the responding server will be picked up from
* the saddr parameter, and stuffed into the domain. The domain's boundp
* field will be set TRUE. The first responding server (or the first one
* which is on a reserved port) will be the bound server for the domain.
*/
bool
ypbind_broadcast_ack(ptrue, saddr)
bool *ptrue;
struct sockaddr_in *saddr;
{
/* if we should be running secure and the server is not using
* a reserved port, return FALSE
*/
if (secure && (saddr->sin_family != AF_INET ||
saddr->sin_port >= IPPORT_RESERVED) ) {
return (FALSE);
}
current_domain->dom_boundp = TRUE;
current_domain->dom_serv_addr = saddr->sin_addr;
current_domain->dom_serv_port = saddr->sin_port;
gettimeofday(&(current_domain->lastping),0);
return(TRUE);
}
/*
* This checks to see if a server bound to a named domain is still alive and
* well. If he's not, boundp in the domain structure is set to FALSE.
*/
bool
ypbind_ping(pdom)
struct domain *pdom;
{
struct sockaddr_in addr;
enum clnt_stat clnt_stat;
struct timeval timeout;
struct timeval intertry;
bool new_binding = FALSE;
char *pname;
int true = FALSE;
timeout.tv_sec = PINGTOTTIM;
timeout.tv_usec = intertry.tv_usec = 0;
if (pdom->ping_clnt == (CLIENT *)NULL) {
new_binding = TRUE;
intertry.tv_sec = PINGINTERTRY;
addr.sin_addr = pdom->dom_serv_addr;
addr.sin_family = AF_INET;
addr.sin_port = pdom->dom_serv_port;
bzero(addr.sin_zero, 8);
if ((pdom->ping_clnt = clntudp_bufcreate(&addr, YPPROG,
pdom->dom_vers, intertry, &ping_sock,
RPCSMALLMSGSIZE, RPCSMALLMSGSIZE)) == (CLIENT *)NULL) {
clnt_pcreateerror("ypbind_ping -- clntudp_create error");
pdom->dom_boundp = FALSE;
return (new_binding);
} else {
pdom->dom_serv_port = addr.sin_port;
}
}
pname = pdom->dom_name;
if ((clnt_stat = (enum clnt_stat) clnt_call(pdom->ping_clnt,
YPPROC_DOMAIN, xdr_ypdomain_wrap_string, &pname, xdr_int, &true,
timeout))
!= RPC_SUCCESS || (true != TRUE)) {
new_binding = TRUE;
pdom->dom_boundp = FALSE;
clnt_destroy(pdom->ping_clnt);
pdom->ping_clnt = (CLIENT *)NULL;
}
gettimeofday(&(pdom->lastping),0);
return (new_binding);
}
#ifdef INIT_DEFAULT
/*
* Preloads the default domain's domain binding. Domain binding for the
* local node's default domain for both the current version, and the
* previous version will be set up. Bindings to servers which serve the
* domain for both versions may additionally be made.
*/
static void
ypbind_init_default()
{
char domain[256];
char *pname = domain;
int true;
int binding_tries;
if (getdomainname(domain, 256) == 0) {
current_domain = ypbind_point_to_domain(domain, YPVERS);
if (current_domain == (struct domain *) NULL) {
abort();
}
for (binding_tries = 0;
((!current_domain->dom_boundp) &&
(binding_tries < BINDING_TRIES) ); binding_tries++) {
(void) clnt_broadcast(YPPROG, current_domain->dom_vers,
YPPROC_DOMAIN_NONACK, xdr_ypdomain_wrap_string,
&pname, xdr_int, &true, ypbind_broadcast_ack);
}
current_domain = ypbind_point_to_domain(domain, YPOLDVERS);
if (current_domain == (struct domain *) NULL) {
abort();
}
for (binding_tries = 0;
((!current_domain->dom_boundp) &&
(binding_tries < BINDING_TRIES) ); binding_tries++) {
(void) clnt_broadcast(YPPROG, current_domain->dom_vers,
YPPROC_DOMAIN_NONACK, xdr_ypdomain_wrap_string,
&pname, xdr_int, &true, ypbind_broadcast_ack);
}
}
}
#endif
int
chklocal(taddr)
struct in_addr taddr;
{
struct in_addr addrs;
struct ifconf ifc;
struct ifreq ifreq, *ifr;
struct sockaddr_in *sin;
int n, i,j, sock;
char buf[UDPMSGSIZE];
ifc.ifc_len = UDPMSGSIZE;
ifc.ifc_buf = buf;
sock=socket(PF_INET,SOCK_DGRAM,0);
if (sock<0) return(FALSE);
if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
perror("SIOCGIFCONF");
close(sock);
return (FALSE);
}
ifr = ifc.ifc_req;
j=0;
for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
ifreq = *ifr;
if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
perror("SIOCGIFFLAGS");
continue;
}
if ((ifreq.ifr_flags & IFF_UP) && ifr->ifr_addr.sa_family == AF_INET) {
sin = (struct sockaddr_in *)&ifr->ifr_addr;
if (ioctl(sock, SIOCGIFADDR, (char *)&ifreq) < 0) {
perror("SIOCGIFADDR");
} else {
addrs = ((struct sockaddr_in*)
&ifreq.ifr_addr)->sin_addr;
if (bcmp((char *)&taddr,(char *)&addrs,sizeof(addrs))==0)
{
close(sock);
return(TRUE);
}
}
}
}
close(sock);
return (FALSE);
}
#ifdef LOCK_EX
#define LOCKFILE "/etc/ypbind.lock"
interlock()
{
int pid;
int len;
int status;
int fd;
fd=open(LOCKFILE,O_RDWR+O_CREAT,0600);
if (fd<0) {
fprintf(stderr,"ypbind can't create/open lock file -- ");
perror(LOCKFILE);
abort();
}
if (fd<3) {
if (dup2(fd,8)>0) fd=8;
}
if (flock(fd,LOCK_EX|LOCK_NB)<0) {
fprintf(stderr,"ypbind is already running -- ");
perror(LOCKFILE);
exit (-1);
}
interlock_fd = fd;
}
#endif
unregister(x)
int x;
{
int mask;
(void) pmap_unset(YPBINDPROG, YPBINDVERS);
(void) pmap_unset(YPBINDPROG, YPBINDOLDVERS);
signal(x,SIG_DFL);
fprintf(stderr,"ypbind: going down on signal %d\n",x);
mask=sigblock(sigmask(SIGCHLD)|sigmask(SIGTERM));
killpg(getpid(),SIGTERM); /*kill children*/
sigsetmask(mask & ~(sigmask (x))); /*allow the exception*/
kill(getpid(),x);
sleep(2);
exit(-1);
}