1022 lines
25 KiB
C
1022 lines
25 KiB
C
#if 0
|
|
static char sccsid[] = "@(#)ypbind.c 1.4 88/07/29 4.0NFSSRC"; /* from 1.31 88/02/07 SMI Copyr 1985 Sun Micro */
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1985 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/*
|
|
* This constructs a list of servers by domains, and keeps more-or-less up to
|
|
* date track of those server's reachability.
|
|
*/
|
|
|
|
#define _BSD_SIGNALS
|
|
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <bstring.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <syslog.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 <arpa/inet.h>
|
|
#include <netinet/in.h> /* for ntohx() */
|
|
#include <net/if.h> /* to find local addresses */
|
|
#include <net/route.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/file.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/sysctl.h>
|
|
#include <rpcsvc/yp_prot.h>
|
|
#include <rpcsvc/ypclnt.h>
|
|
#include <rpc/pmap_clnt.h>
|
|
|
|
/*
|
|
* 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];
|
|
bool_t dom_boundp;
|
|
bool_t dom_new; /* no broadcaster yet */
|
|
bool_t dom_perm; /* permanently bound? */
|
|
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;
|
|
bool_t dom_set; /* ypset */
|
|
FILE *broadcaster_pipe; /* to get answer from broadcaster */
|
|
XDR broadcaster_xdr;
|
|
};
|
|
static int ping_sock = RPC_ANYSOCK;
|
|
struct domain init_domain = {
|
|
0, /* dom_pnext */
|
|
{ 0 }, /* dom_name */
|
|
1, /* dom_boundp */
|
|
1, /* dom_new */
|
|
0, /* dom_perm */
|
|
0, /* ping_clnt */
|
|
{ 0 }, 0, /* dom_serv_addr, dom_serv_port */
|
|
-1, /* dom_report_success */
|
|
0, /* dom_broadcaster_pid */
|
|
0, /* dom_set */
|
|
NULL, /* broadcaster_pipe */
|
|
};
|
|
struct domain *known_domains = &init_domain;
|
|
struct domain *current_domain; /* Used by ypbind_broadcast_ack, set
|
|
* by all callers of clnt_broadcast */
|
|
SVCXPRT *tcphandle;
|
|
SVCXPRT *udphandle;
|
|
|
|
#define PINGTOTTIM 20 /* Total seconds for ping timeout */
|
|
#define PINGINTERTRY 10
|
|
#define SETDOMINTERTRY 20
|
|
#define SETDOMTOTTIM 60
|
|
|
|
static int debug = FALSE;
|
|
static int verbose = FALSE;
|
|
static int secure = FALSE; /* running c2 secure for -s */
|
|
static u_long ttl = 2; /* initial multicast TTL */
|
|
|
|
static enum {YPSETANY, YPSETLOCAL, YPSETNONE} setok = YPSETNONE;
|
|
|
|
void
|
|
cleanup()
|
|
{
|
|
(void) pmap_unset(YPBINDPROG, YPBINDVERS);
|
|
exit(0);
|
|
}
|
|
|
|
|
|
void dispatch();
|
|
void ypbind_dispatch();
|
|
void ypbind_get_binding();
|
|
void ypbind_set_binding();
|
|
void ypbind_pipe_setdom();
|
|
static struct domain *ypbind_point_to_domain(char *, unsigned short);
|
|
static bool_t ypbind_broadcast_ack(void *, struct sockaddr_in *);
|
|
static void ypbind_broadcast_bind(unsigned short);
|
|
static int ypbind_ping(struct domain *, bool_t);
|
|
void broadcast_proc_exit();
|
|
static bool_t chklocal(struct in_addr);
|
|
static void getlocal(void);
|
|
|
|
const char prog[] = "ypbind";
|
|
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
fd_set readfds;
|
|
int oldmask;
|
|
int i;
|
|
char *p;
|
|
|
|
openlog(prog, LOG_PID|LOG_NOWAIT|LOG_CONS, LOG_DAEMON);
|
|
|
|
if (0 > getdomainname(init_domain.dom_name,
|
|
sizeof(init_domain.dom_name))) {
|
|
syslog(LOG_ERR, "getdomainname: %m");
|
|
exit(1);
|
|
}
|
|
/* User started us by hand, so make error visible */
|
|
if (init_domain.dom_name[0] == '\0') {
|
|
fprintf(stderr, "ypbind: error: domain name not set\n");
|
|
exit(1);
|
|
}
|
|
if (getuid() != 0) {
|
|
fprintf(stderr, "ypbind: You are not superuser\n");
|
|
exit(1);
|
|
}
|
|
|
|
(void) signal(SIGHUP, SIG_IGN);
|
|
(void) signal(SIGTERM, cleanup);
|
|
|
|
(void) pmap_unset(YPBINDPROG, YPBINDVERS);
|
|
|
|
opterr = 0;
|
|
while ((i = getopt(argc,argv,"svy:DT:")) != -1) {
|
|
switch (i) {
|
|
case 's': /* -s[ecure] */
|
|
secure = TRUE;
|
|
(void) syslog(LOG_INFO, "running secure");
|
|
break;
|
|
case 'v':
|
|
verbose = TRUE;
|
|
(void) syslog(LOG_INFO, "running verbose");
|
|
break;
|
|
case 'y':
|
|
/*
|
|
* kludge to handle "-ypset" and "-ypsetme"
|
|
* and still allow "-svt0"
|
|
*/
|
|
if (!strcmp(optarg,"pset"))
|
|
setok = YPSETANY;
|
|
else if (!strcmp(optarg, "psetme"))
|
|
setok = YPSETLOCAL;
|
|
else
|
|
goto usage;
|
|
break;
|
|
case 'D':
|
|
debug = TRUE;
|
|
(void) syslog(LOG_INFO, "debugging");
|
|
break;
|
|
case 'T':
|
|
ttl = strtoul(optarg, &p, 0);
|
|
if (*p != '\0' || ttl > 32) {
|
|
ttl = 2;
|
|
(void) syslog(LOG_ERR,"bad ttl: %s",optarg);
|
|
}
|
|
break;
|
|
default:
|
|
usage:
|
|
(void) syslog(LOG_ERR, "usage: %s [-sv] [-T ttl]"
|
|
" [-ypset | -ypsetme] [server]",
|
|
prog);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* Try ourself, unless a permanent server was specified */
|
|
init_domain.dom_serv_addr.s_addr = INADDR_LOOPBACK;
|
|
if (argc > optind) {
|
|
struct hostent *hp;
|
|
|
|
if ((hp = gethostbyname(argv[optind])) != NULL) {
|
|
bcopy(hp->h_addr, &init_domain.dom_serv_addr,
|
|
hp->h_length);
|
|
init_domain.dom_perm = TRUE;
|
|
} else
|
|
syslog(LOG_ERR, "can't find address for %s",
|
|
argv[optind]);
|
|
}
|
|
|
|
if (!debug) {
|
|
int t, pid;
|
|
|
|
pid = fork();
|
|
|
|
if (pid == -1) {
|
|
(void) syslog(LOG_ERR, "fork failed: %m");
|
|
exit(1);
|
|
}
|
|
|
|
if (pid != 0) {
|
|
exit(0);
|
|
}
|
|
|
|
closelog();
|
|
for (t = getdtablehi(); --t > 0; )
|
|
(void) close(t);
|
|
|
|
t = open("/dev/tty", 2);
|
|
if (t >= 0) {
|
|
(void) ioctl(t, TIOCNOTTY, (char *)0);
|
|
(void) close(t);
|
|
}
|
|
|
|
openlog(prog, LOG_PID|LOG_NOWAIT|LOG_CONS, LOG_DAEMON);
|
|
}
|
|
|
|
if (signal(SIGCHLD, broadcast_proc_exit) == SIG_ERR) {
|
|
(void) syslog(LOG_ERR,"Can't establish reaper signal handler.");
|
|
exit(1);
|
|
}
|
|
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
|
(void) syslog(LOG_ERR,"Can't establish pipe signal handler.");
|
|
exit(1);
|
|
}
|
|
/* Open a socket for pinging everyone can use */
|
|
ping_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (ping_sock < 0) {
|
|
(void) syslog(LOG_ERR, "Cannot create socket for pinging: %m");
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* if not running c2 secure, don't use priviledged ports.
|
|
* Accomplished by side effect of not being root when creating
|
|
* rpc based sockets.
|
|
*/
|
|
if (! secure) {
|
|
(void) setreuid(-1, 3);
|
|
}
|
|
|
|
if ((tcphandle = svctcp_create(RPC_ANYSOCK,
|
|
RPCSMALLMSGSIZE, RPCSMALLMSGSIZE)) == NULL) {
|
|
(void) syslog(LOG_ERR, "Can't create tcp service.");
|
|
exit(1);
|
|
}
|
|
|
|
if (!svc_register(tcphandle, YPBINDPROG, YPBINDVERS,
|
|
ypbind_dispatch, IPPROTO_TCP) ) {
|
|
(void) syslog(LOG_ERR, "Can't register tcp service.");
|
|
exit(1);
|
|
}
|
|
|
|
if ((udphandle = svcudp_bufcreate(RPC_ANYSOCK,
|
|
RPCSMALLMSGSIZE, RPCSMALLMSGSIZE)) == (SVCXPRT *) NULL) {
|
|
(void) syslog(LOG_ERR, "Can't create udp service.");
|
|
exit(1);
|
|
}
|
|
|
|
if (!svc_register(udphandle, YPBINDPROG, YPBINDVERS,
|
|
ypbind_dispatch, IPPROTO_UDP) ) {
|
|
(void) syslog(LOG_ERR, "Can't register udp service.");
|
|
exit(1);
|
|
}
|
|
|
|
/* undo the gross hack associated with c2 security */
|
|
if (! secure)
|
|
(void) setreuid(-1, 0);
|
|
|
|
/* See if we guessed right about our initial server */
|
|
current_domain = &init_domain;
|
|
if (ypbind_ping(current_domain, FALSE) == FALSE) {
|
|
init_domain.dom_boundp = FALSE;
|
|
oldmask = sigblock(sigmask(SIGCHLD));
|
|
ypbind_broadcast_bind(YPVERS);
|
|
(void) sigsetmask(oldmask);
|
|
}
|
|
|
|
for (;;) {
|
|
readfds = svc_fdset;
|
|
errno = 0;
|
|
|
|
switch (select(FD_SETSIZE, &readfds, NULL, NULL, NULL)) {
|
|
|
|
case -1: {
|
|
|
|
if (errno != EINTR) {
|
|
(void) syslog(LOG_ERR, "main loop select: %m");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 0: {
|
|
(void) syslog(LOG_ERR,
|
|
"Invalid timeout in main loop select.");
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
oldmask = sigblock(sigmask(SIGCHLD));
|
|
svc_getreqset (&readfds);
|
|
(void) sigsetmask(oldmask);
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ypbind_dispatch is a wrapper 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);
|
|
}
|
|
|
|
/*
|
|
* 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) syslog(LOG_ERR, "Can't reply to rpc call.");
|
|
}
|
|
|
|
break;
|
|
|
|
case YPBINDPROC_DOMAIN:
|
|
ypbind_get_binding(rqstp, transp, vers);
|
|
break;
|
|
|
|
case YPBINDPROC_SETDOM:
|
|
ypbind_set_binding(rqstp, transp, vers);
|
|
break;
|
|
|
|
default:
|
|
svcerr_noproc(transp);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
close_pipe(struct domain *pdom)
|
|
{
|
|
if (pdom->broadcaster_pipe) {
|
|
xdr_destroy(&pdom->broadcaster_xdr);
|
|
(void) fclose(pdom->broadcaster_pipe);
|
|
pdom->broadcaster_pipe = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
int wait_status;
|
|
register struct domain *pdom;
|
|
struct ypbind_setdom req;
|
|
|
|
while ((pid = wait3(&wait_status,WNOHANG,NULL)) > 0) {
|
|
for (pdom = known_domains; pdom != (struct domain *)NULL;
|
|
pdom = pdom->dom_pnext) {
|
|
|
|
if (pdom->dom_broadcaster_pid == pid) {
|
|
pdom->dom_broadcaster_pid = 0;
|
|
pdom->dom_new = FALSE;
|
|
|
|
if ((WTERMSIG(wait_status) == 0) &&
|
|
(WEXITSTATUS(wait_status) == 0)) {
|
|
|
|
if (pdom->broadcaster_pipe) {
|
|
pdom->dom_report_success = -1;
|
|
|
|
if (xdr_ypbind_setdom(&pdom->broadcaster_xdr,&req)){
|
|
pdom->dom_serv_addr = req.ypsetdom_addr;
|
|
pdom->dom_serv_port = req.ypsetdom_port;
|
|
pdom->dom_boundp = TRUE;
|
|
if (pdom->ping_clnt != (CLIENT *)NULL) {
|
|
clnt_destroy(pdom->ping_clnt);
|
|
pdom->ping_clnt = (CLIENT *)NULL;
|
|
}
|
|
if (verbose)
|
|
syslog(LOG_INFO, "bound to %s for %s",
|
|
inet_ntoa(req.ypsetdom_addr),
|
|
pdom->dom_name);
|
|
} else {
|
|
syslog(LOG_ERR, "xdr_ypbind_setdom failed");
|
|
}
|
|
} else {
|
|
syslog(LOG_ERR,
|
|
"internal error: no broadcaster pipe");
|
|
}
|
|
} else if (!pdom->dom_set)
|
|
pdom->dom_boundp = FALSE;
|
|
|
|
pdom->dom_set = FALSE;
|
|
|
|
/* Always free the pipe */
|
|
close_pipe(pdom);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This returns the current binding for a passed domain.
|
|
*/
|
|
void
|
|
ypbind_get_binding(rqstp, transp, vers)
|
|
struct svc_req *rqstp;
|
|
register SVCXPRT *transp;
|
|
unsigned short vers;
|
|
{
|
|
char *pdomain_name = NULL;
|
|
struct ypbind_resp response;
|
|
|
|
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) {
|
|
|
|
/*
|
|
* Bound or not, return the current state of the binding.
|
|
*/
|
|
|
|
if (current_domain->dom_boundp || current_domain->dom_perm) {
|
|
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 if (current_domain->dom_new) {
|
|
response.ypbind_status = YPBIND_FAIL_VAL;
|
|
response.ypbind_respbody.ypbind_error =
|
|
YPBIND_ERR_ERR;
|
|
} 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;
|
|
}
|
|
|
|
if (!svc_sendreply(transp, xdr_ypbind_resp, &response) ) {
|
|
(void) syslog(LOG_ERR, "Can't respond to rpc request.");
|
|
}
|
|
|
|
if (!svc_freeargs(transp, xdr_ypdomain_wrap_string, &pdomain_name) ) {
|
|
(void) syslog(LOG_ERR, "ypbind_get_binding can't free args.");
|
|
}
|
|
|
|
if (FALSE == ypbind_ping(current_domain, TRUE))
|
|
ypbind_broadcast_bind(vers);
|
|
}
|
|
|
|
static void
|
|
conslog(int pri, const char *fmt, ...)
|
|
{
|
|
FILE *f;
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
vsyslog(pri, fmt, ap);
|
|
if ((f = fopen("/dev/console", "w")) != NULL) {
|
|
vfprintf(f, fmt, ap);
|
|
putc('\n', f);
|
|
(void) fclose(f);
|
|
}
|
|
va_end(ap);
|
|
}
|
|
|
|
static void
|
|
ypbind_broadcast_bind(unsigned short vers)
|
|
{
|
|
char *pname;
|
|
int broadcaster_pid = -2;
|
|
int fildes[2];
|
|
|
|
if ((current_domain) && (!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.
|
|
*/
|
|
current_domain->dom_report_success++;
|
|
pname = current_domain->dom_name;
|
|
|
|
|
|
if (pipe(fildes) >= 0 && (broadcaster_pid = fork()) == 0) {
|
|
int true = 1;
|
|
|
|
if (verbose)
|
|
syslog(LOG_INFO,
|
|
"broadcasting%s for %s (v.%d)",
|
|
ttl == 0 ? "" : " and multicasting",
|
|
pname, vers);
|
|
|
|
/* Allow "set domain" proc to kill this process */
|
|
(void) signal(SIGTERM, SIG_DFL);
|
|
|
|
current_domain->broadcaster_pipe= fdopen(fildes[1],"w");
|
|
if (current_domain->broadcaster_pipe) {
|
|
xdrstdio_create(
|
|
¤t_domain->broadcaster_xdr,
|
|
current_domain->broadcaster_pipe,
|
|
XDR_ENCODE);
|
|
} else {
|
|
syslog(LOG_ERR, "fdopen pipe");
|
|
exit(1);
|
|
}
|
|
|
|
if (ttl == 0)
|
|
(void) clnt_broadcast(YPPROG, vers,
|
|
YPPROC_DOMAIN_NONACK,
|
|
xdr_ypdomain_wrap_string,
|
|
&pname, xdr_int, &true,
|
|
ypbind_broadcast_ack);
|
|
else
|
|
(void) clnt_broadmulti(YPPROG, vers,
|
|
YPPROC_DOMAIN_NONACK,
|
|
xdr_ypdomain_wrap_string,
|
|
&pname, xdr_int, &true,
|
|
ypbind_broadcast_ack,
|
|
ttl, 8);
|
|
|
|
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) {
|
|
conslog(LOG_INFO,
|
|
"NIS server for domain \"%s\" OK",
|
|
pname);
|
|
}
|
|
|
|
exit(0);
|
|
} else {
|
|
|
|
conslog(LOG_WARNING,
|
|
"NIS v.%d server not responding for domain \"%s\"; still trying",
|
|
vers, pname);
|
|
exit(1);
|
|
}
|
|
|
|
} else if (broadcaster_pid < 0) {
|
|
if (broadcaster_pid == -2) {
|
|
(void) syslog(LOG_ERR,
|
|
"broadcaster pipe failure: %m");
|
|
} else {
|
|
(void) syslog(LOG_ERR,
|
|
"broadcaster fork failure: %m");
|
|
(void) close(fildes[0]);
|
|
(void) close(fildes[1]);
|
|
}
|
|
} else {
|
|
/* Parent */
|
|
(void) close(fildes[1]);
|
|
current_domain->broadcaster_pipe= fdopen(fildes[0],"r");
|
|
if (current_domain->broadcaster_pipe)
|
|
xdrstdio_create(
|
|
¤t_domain->broadcaster_xdr,
|
|
current_domain->broadcaster_pipe,
|
|
XDR_DECODE);
|
|
current_domain->dom_broadcaster_pid = broadcaster_pid;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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));
|
|
(void) fclose(dp->broadcaster_pipe);
|
|
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;
|
|
unsigned short version;
|
|
struct in_addr addr;
|
|
struct sockaddr_in *who;
|
|
unsigned short int port;
|
|
char *domain;
|
|
char a[17];
|
|
|
|
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;
|
|
|
|
/* find out who originated the request */
|
|
who = svc_getcaller(transp);
|
|
|
|
if (setok == YPSETNONE) {
|
|
strcpy(a, inet_ntoa(addr));
|
|
syslog(LOG_ERR,
|
|
"Set domain request to host %s, domain %s, from host %s, failed (ypset not allowed)!",
|
|
a, domain, inet_ntoa(who->sin_addr));
|
|
svcerr_systemerr(transp);
|
|
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.
|
|
*/
|
|
|
|
if (ntohs(who->sin_port) >= IPPORT_RESERVED) {
|
|
strcpy(a, inet_ntoa(addr));
|
|
syslog(LOG_ERR,
|
|
"Set domain request to host %s, domain %s, from host %s, failed (bad port %d)",
|
|
a, domain, inet_ntoa(who->sin_addr), ntohs(who->sin_port));
|
|
svcerr_systemerr(transp);
|
|
return;
|
|
}
|
|
|
|
if (setok == YPSETLOCAL && !chklocal(who->sin_addr)) {
|
|
strcpy(a, inet_ntoa(addr));
|
|
syslog(LOG_ERR,
|
|
"Set domain request to host %s, domain %s, from host %s, failed (not local).",
|
|
a, domain, 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) {
|
|
strcpy(a, inet_ntoa(addr));
|
|
|
|
(void) syslog(LOG_ERR,
|
|
"Set domain request to host %s, domain %s, from host %s, failed (not root).",
|
|
a, domain, inet_ntoa(who->sin_addr));
|
|
svcerr_systemerr(transp);
|
|
return;
|
|
}
|
|
} else {
|
|
strcpy(a, inet_ntoa(addr));
|
|
syslog(LOG_ERR,
|
|
"Set domain request to host %s, domain %s, from host %s, failed (bad credentials).",
|
|
a, domain, inet_ntoa(who->sin_addr));
|
|
svcerr_weakauth(transp);
|
|
return;
|
|
}
|
|
|
|
if (!svc_sendreply(transp, xdr_void, 0) ) {
|
|
syslog(LOG_ERR, "Can't reply to rpc call.");
|
|
}
|
|
|
|
if (verbose) {
|
|
strcpy(a, inet_ntoa(addr));
|
|
syslog(LOG_INFO,
|
|
"Set domain request to host %s, domain %s, from host %s, succeeded",
|
|
a, domain, inet_ntoa(who->sin_addr));
|
|
}
|
|
|
|
if ( (current_domain = ypbind_point_to_domain(domain,
|
|
version) ) != (struct domain *) NULL) {
|
|
/*
|
|
* Kill the broadcaster because we've got a server.
|
|
* Set a flag to prevent the reaper from unbinding the domain.
|
|
*/
|
|
if (current_domain->dom_broadcaster_pid) {
|
|
current_domain->dom_set = TRUE;
|
|
kill(current_domain->dom_broadcaster_pid, SIGTERM);
|
|
}
|
|
current_domain->dom_serv_addr = addr;
|
|
current_domain->dom_serv_port = port;
|
|
current_domain->dom_boundp = TRUE;
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* 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(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 == YPVERS)
|
|
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_boundp = FALSE;
|
|
pdom->dom_perm = FALSE;
|
|
pdom->ping_clnt = (CLIENT *)NULL;
|
|
pdom->dom_report_success = -1;
|
|
pdom->dom_broadcaster_pid = 0;
|
|
pdom->dom_new = TRUE;
|
|
pdom->dom_set = FALSE;
|
|
pdom->broadcaster_pipe = 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.
|
|
*/
|
|
/* ARGSUSED */
|
|
bool_t
|
|
ypbind_broadcast_ack(void *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;
|
|
return(TRUE);
|
|
}
|
|
|
|
/*
|
|
* This checks to see if a server bound to a named domain is still alive and
|
|
* well.
|
|
*/
|
|
static int /* TRUE=server ok */
|
|
ypbind_ping(struct domain *pdom,
|
|
bool_t complain)
|
|
{
|
|
struct sockaddr_in addr;
|
|
struct timeval timeout;
|
|
struct timeval intertry;
|
|
|
|
if (!pdom)
|
|
return FALSE;
|
|
|
|
/* Assume broadcaster will figure it all out */
|
|
if (pdom->dom_broadcaster_pid != 0 || pdom->dom_perm)
|
|
return TRUE;
|
|
|
|
/* Someone should start broadcasting */
|
|
if (!pdom->dom_boundp)
|
|
return FALSE;
|
|
|
|
timeout.tv_sec = PINGTOTTIM;
|
|
timeout.tv_usec = intertry.tv_usec = 0;
|
|
if (pdom->ping_clnt == (CLIENT *)NULL) {
|
|
intertry.tv_sec = PINGINTERTRY;
|
|
bzero(&addr, sizeof(addr));
|
|
addr.sin_addr = pdom->dom_serv_addr;
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = pdom->dom_serv_port;
|
|
if ((pdom->ping_clnt = clntudp_bufcreate(&addr, YPPROG,
|
|
YPVERS, intertry, &ping_sock,
|
|
RPCSMALLMSGSIZE, RPCSMALLMSGSIZE)) == (CLIENT *)NULL) {
|
|
if (complain)
|
|
syslog(LOG_ERR, "ypbind_ping %s: %s",
|
|
inet_ntoa(addr.sin_addr),
|
|
clnt_spcreateerror("clntudp_create error"));
|
|
return FALSE;
|
|
} else {
|
|
pdom->dom_serv_port = addr.sin_port;
|
|
}
|
|
}
|
|
if (clnt_call(pdom->ping_clnt, YPPROC_NULL, xdr_void, 0,
|
|
xdr_void, 0, timeout) != RPC_SUCCESS) {
|
|
pdom->dom_boundp = FALSE;
|
|
clnt_destroy(pdom->ping_clnt);
|
|
pdom->ping_clnt = (CLIENT *)NULL;
|
|
}
|
|
|
|
return pdom->dom_boundp;
|
|
}
|
|
|
|
static struct timeval when_local;
|
|
static int num_local = 0;
|
|
static union addrs {
|
|
char buf[1];
|
|
struct ifa_msghdr ifam;
|
|
struct in_addr a[1];
|
|
} *addrs;
|
|
static size_t addrs_size = 0;
|
|
|
|
static void
|
|
getlocal(void)
|
|
{
|
|
int i;
|
|
size_t needed;
|
|
struct timeval now;
|
|
int mib[6];
|
|
struct if_msghdr *ifm;
|
|
struct ifa_msghdr *ifam, *ifam_lim, *ifam2;
|
|
struct sockaddr *sa;
|
|
|
|
/*
|
|
* Get current addresses periodically, in case interfaces change.
|
|
*/
|
|
gettimeofday(&now,0);
|
|
if (addrs_size > 0 && now.tv_sec < when_local.tv_sec+60)
|
|
return;
|
|
when_local = now;
|
|
num_local = 0;
|
|
|
|
/*
|
|
* Fetch the interface list, without too many system calls
|
|
* since we do it repeatedly.
|
|
*/
|
|
mib[0] = CTL_NET;
|
|
mib[1] = PF_ROUTE;
|
|
mib[2] = 0;
|
|
mib[3] = AF_INET;
|
|
mib[4] = NET_RT_IFLIST;
|
|
mib[5] = 0;
|
|
for (;;) {
|
|
if ((needed = addrs_size) != 0) {
|
|
if (sysctl(mib, 6, addrs,&needed, 0, 0) >= 0)
|
|
break;
|
|
|
|
if (errno != ENOMEM && errno != EFAULT) {
|
|
syslog(LOG_ERR, "sysctl: %m");
|
|
exit(1);
|
|
}
|
|
free(addrs);
|
|
needed = 0;
|
|
}
|
|
if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) {
|
|
syslog(LOG_ERR, "sysctl-estimate: %m");
|
|
exit(1);
|
|
}
|
|
addrs = (union addrs *)malloc(addrs_size = needed);
|
|
}
|
|
|
|
ifam_lim = (struct ifa_msghdr *)(addrs->buf + needed);
|
|
for (ifam = &addrs->ifam; ifam < ifam_lim; ifam = ifam2) {
|
|
ifam2 = (struct ifa_msghdr*)((char*)ifam + ifam->ifam_msglen);
|
|
|
|
if (ifam->ifam_type == RTM_IFINFO) {
|
|
ifm = (struct if_msghdr *)ifam;
|
|
continue;
|
|
}
|
|
if (ifam->ifam_type != RTM_NEWADDR) {
|
|
syslog(LOG_ERR, "out of sync");
|
|
abort();
|
|
}
|
|
|
|
if (!(ifm->ifm_flags & IFF_UP))
|
|
continue;
|
|
|
|
/*
|
|
* Find the interface address among the other addresses.
|
|
*/
|
|
sa = (struct sockaddr *)(ifam+1);
|
|
for (i = 0;
|
|
i <= RTAX_IFA && sa < (struct sockaddr *)ifam2;
|
|
i++) {
|
|
if ((ifam->ifam_addrs & (1 << i)) == 0)
|
|
continue;
|
|
if (i == RTAX_IFA)
|
|
break;
|
|
#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) \
|
|
: sizeof(__uint64_t))
|
|
#ifdef _HAVE_SA_LEN
|
|
sa = (struct sockaddr *)((char*)sa
|
|
+ ROUNDUP(sa->sa_len));
|
|
#else
|
|
sa = (struct sockaddr *
|
|
)((char*)sa + ROUNDUP(_FAKE_SA_LEN_DST(sa)));
|
|
#endif
|
|
}
|
|
if (i > RTAX_IFA
|
|
#ifdef _HAVE_SA_LEN
|
|
|| sa->sa_len == 0
|
|
#endif
|
|
|| sa >= (struct sockaddr *)ifam2
|
|
|| sa->sa_family != AF_INET)
|
|
continue;
|
|
|
|
addrs->a[num_local++] = ((struct sockaddr_in *)sa)->sin_addr;
|
|
}
|
|
}
|
|
|
|
|
|
static bool_t
|
|
chklocal(struct in_addr addr)
|
|
{
|
|
int i;
|
|
|
|
if (addr.s_addr == INADDR_LOOPBACK)
|
|
return (TRUE);
|
|
|
|
getlocal();
|
|
for (i = 0; i < num_local; i++) {
|
|
if (addr.s_addr == addrs->a[i].s_addr)
|
|
return (TRUE);
|
|
}
|
|
return (FALSE);
|
|
}
|