NetBSD/usr.sbin/ypbind/ypbind.c
cgd 8d81e0e116 __CONCAT does token pasting, not string concatnation. if something like:
__CONCAT("foo","bar");
actually works to concantate strings, it's because the preprocessor expands
it into "foo""bar" as separate strings, and then ANSI string concatenation
is performed on that.  It's more straightforward to just use ANSI string
concatenation directly, and newer GCCs complain (rightly) about misuse
of token pasting.
2000-12-20 01:34:39 +00:00

1231 lines
28 KiB
C

/* $NetBSD: ypbind.c,v 1.44 2000/12/20 01:34:39 cgd Exp $ */
/*
* Copyright (c) 1992, 1993 Theo de Raadt <deraadt@fsa.ca>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Theo de Raadt.
* 4. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef LINT
__RCSID("$NetBSD: ypbind.c,v 1.44 2000/12/20 01:34:39 cgd Exp $");
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/syslog.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <syslog.h>
#if __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include <ctype.h>
#include <dirent.h>
#include <netdb.h>
#include <string.h>
#include <err.h>
#include <rpc/rpc.h>
#include <rpc/xdr.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <rpc/pmap_clnt.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_rmt.h>
#include <unistd.h>
#include <util.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#include <ifaddrs.h>
#include "pathnames.h"
#ifndef O_SHLOCK
#define O_SHLOCK 0
#endif
#define BUFSIZE 1400
#define YPSERVERSSUFF ".ypservers"
#define BINDINGDIR (_PATH_VAR_YP "binding")
struct _dom_binding {
struct _dom_binding *dom_pnext;
char dom_domain[YPMAXDOMAIN + 1];
struct sockaddr_in dom_server_addr;
unsigned short int dom_server_port;
int dom_socket;
CLIENT *dom_client;
long dom_vers;
time_t dom_check_t;
time_t dom_ask_t;
int dom_lockfd;
int dom_alive;
u_int32_t dom_xid;
};
static char *domainname;
static struct _dom_binding *ypbindlist;
static int check;
typedef enum {
YPBIND_DIRECT, YPBIND_BROADCAST, YPBIND_SETLOCAL, YPBIND_SETALL
} ypbind_mode_t;
ypbind_mode_t ypbindmode;
/*
* If ypbindmode is YPBIND_SETLOCAL or YPBIND_SETALL, this indicates
* whether or not we've been "ypset". If we haven't, we behave like
* YPBIND_BROADCAST. If we have, we behave like YPBIND_DIRECT.
*/
int been_ypset;
#ifdef DEBUG
static int debug;
#endif
static int insecure;
static int rpcsock, pingsock;
static struct rmtcallargs rmtca;
static struct rmtcallres rmtcr;
static bool_t rmtcr_outval;
static u_long rmtcr_port;
static SVCXPRT *udptransp, *tcptransp;
int _yp_invalid_domain __P((const char *)); /* from libc */
int main __P((int, char *[]));
static void usage __P((void));
static void yp_log __P((int, const char *, ...))
__attribute__((__format__(__printf__, 2, 3)));
static struct _dom_binding *makebinding __P((const char *));
static int makelock __P((struct _dom_binding *));
static void removelock __P((struct _dom_binding *));
static void *ypbindproc_null_2 __P((SVCXPRT *, void *));
static void *ypbindproc_domain_2 __P((SVCXPRT *, void *));
static void *ypbindproc_setdom_2 __P((SVCXPRT *, void *));
static void ypbindprog_2 __P((struct svc_req *, SVCXPRT *));
static void checkwork __P((void));
static int ping __P((struct _dom_binding *));
static int nag_servers __P((struct _dom_binding *));
static enum clnt_stat handle_replies __P((void));
static enum clnt_stat handle_ping __P((void));
static void rpc_received __P((char *, struct sockaddr_in *, int));
static struct _dom_binding *xid2ypdb __P((u_int32_t));
static u_int32_t unique_xid __P((struct _dom_binding *));
static int broadcast __P((char *, int));
static int direct __P((char *, int));
static int direct_set __P((char *, int, struct _dom_binding *));
static void
usage()
{
extern char *__progname;
char *opt = "";
#ifdef DEBUG
opt = " [-d]";
#endif
(void)fprintf(stderr,
"Usage: %s [-broadcast] [-insecure] [-ypset] [-ypsetme] %s\n",
__progname, opt);
exit(1);
}
static void
#if __STDC__
yp_log(int pri, const char *fmt, ...)
#else
yp_log(pri, fmt, va_alist)
int pri;
char *fmt;
va_dcl
#endif
{
va_list ap;
#if __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
#if defined(DEBUG)
if (debug)
vfprintf(stderr, fmt, ap);
else
#endif
vsyslog(pri, fmt, ap);
va_end(ap);
}
static struct _dom_binding *
makebinding(dm)
const char *dm;
{
struct _dom_binding *ypdb;
if ((ypdb = (struct _dom_binding *)malloc(sizeof *ypdb)) == NULL) {
yp_log(LOG_ERR, "makebinding");
exit(1);
}
(void)memset(ypdb, 0, sizeof *ypdb);
(void)strncpy(ypdb->dom_domain, dm, sizeof ypdb->dom_domain);
ypdb->dom_domain[sizeof(ypdb->dom_domain) - 1] = '\0';
return ypdb;
}
static int
makelock(ypdb)
struct _dom_binding *ypdb;
{
int fd;
char path[MAXPATHLEN];
(void)snprintf(path, sizeof(path), "%s/%s.%ld", BINDINGDIR,
ypdb->dom_domain, ypdb->dom_vers);
if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) {
(void)mkdir(BINDINGDIR, 0755);
if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1)
return -1;
}
#if O_SHLOCK == 0
(void)flock(fd, LOCK_SH);
#endif
return fd;
}
static void
removelock(ypdb)
struct _dom_binding *ypdb;
{
char path[MAXPATHLEN];
(void)snprintf(path, sizeof(path), "%s/%s.%ld",
BINDINGDIR, ypdb->dom_domain, ypdb->dom_vers);
(void)unlink(path);
}
static void *
ypbindproc_null_2(transp, argp)
SVCXPRT *transp;
void *argp;
{
static char res;
#ifdef DEBUG
if (debug)
printf("ypbindproc_null_2\n");
#endif
(void)memset(&res, 0, sizeof(res));
return (void *)&res;
}
static void *
ypbindproc_domain_2(transp, argp)
SVCXPRT *transp;
void *argp;
{
static struct ypbind_resp res;
struct _dom_binding *ypdb;
char *arg = *(char **) argp;
time_t now;
int count;
#ifdef DEBUG
if (debug)
printf("ypbindproc_domain_2 %s\n", arg);
#endif
if (_yp_invalid_domain(arg))
return NULL;
(void)memset(&res, 0, sizeof res);
res.ypbind_status = YPBIND_FAIL_VAL;
for (count = 0, ypdb = ypbindlist;
ypdb != NULL;
ypdb = ypdb->dom_pnext, count++) {
if (count > 100)
return NULL; /* prevent denial of service */
if (!strcmp(ypdb->dom_domain, arg))
break;
}
if (ypdb == NULL) {
ypdb = makebinding(arg);
ypdb->dom_vers = YPVERS;
ypdb->dom_alive = 0;
ypdb->dom_lockfd = -1;
removelock(ypdb);
ypdb->dom_xid = unique_xid(ypdb);
ypdb->dom_pnext = ypbindlist;
ypbindlist = ypdb;
check++;
#ifdef DEBUG
if (debug)
printf("unknown domain %s\n", arg);
#endif
return NULL;
}
if (ypdb->dom_alive == 0) {
#ifdef DEBUG
if (debug)
printf("dead domain %s\n", arg);
#endif
return NULL;
}
#ifdef HEURISTIC
time(&now);
if (now < ypdb->dom_ask_t + 5) {
/*
* Hmm. More than 2 requests in 5 seconds have indicated
* that my binding is possibly incorrect.
* Ok, do an immediate poll of the server.
*/
if (ypdb->dom_check_t >= now) {
/* don't flood it */
ypdb->dom_check_t = 0;
check++;
}
}
ypdb->dom_ask_t = now;
#endif
res.ypbind_status = YPBIND_SUCC_VAL;
res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr.s_addr =
ypdb->dom_server_addr.sin_addr.s_addr;
res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port =
ypdb->dom_server_port;
#ifdef DEBUG
if (debug)
printf("domain %s at %s/%d\n", ypdb->dom_domain,
inet_ntoa(ypdb->dom_server_addr.sin_addr),
ntohs(ypdb->dom_server_addr.sin_port));
#endif
return &res;
}
static void *
ypbindproc_setdom_2(transp, argp)
SVCXPRT *transp;
void *argp;
{
struct ypbind_setdom *sd = argp;
struct sockaddr_in *fromsin, bindsin;
static bool_t res;
#ifdef DEBUG
if (debug)
printf("ypbindproc_setdom_2 %s\n", inet_ntoa(bindsin.sin_addr));
#endif
(void)memset(&res, 0, sizeof(res));
fromsin = svc_getcaller(transp);
switch (ypbindmode) {
case YPBIND_SETLOCAL:
if (fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
#ifdef DEBUG
if (debug)
printf("ypset from %s denied\n",
inet_ntoa(fromsin->sin_addr));
#endif
return NULL;
}
/* FALLTHROUGH */
case YPBIND_SETALL:
been_ypset = 1;
break;
case YPBIND_DIRECT:
case YPBIND_BROADCAST:
default:
#ifdef DEBUG
if (debug)
printf("ypset denied\n");
#endif
return NULL;
}
if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) {
#ifdef DEBUG
if (debug)
printf("ypset from unprivileged port denied\n");
#endif
return &res;
}
if (sd->ypsetdom_vers != YPVERS) {
#ifdef DEBUG
if (debug)
printf("ypset with wrong version denied\n");
#endif
return &res;
}
(void)memset(&bindsin, 0, sizeof bindsin);
bindsin.sin_family = AF_INET;
bindsin.sin_len = sizeof(bindsin);
bindsin.sin_addr = sd->ypsetdom_addr;
bindsin.sin_port = sd->ypsetdom_port;
rpc_received(sd->ypsetdom_domain, &bindsin, 1);
#ifdef DEBUG
if (debug)
printf("ypset to %s succeeded\n", inet_ntoa(bindsin.sin_addr));
#endif
res = 1;
return &res;
}
static void
ypbindprog_2(rqstp, transp)
struct svc_req *rqstp;
register SVCXPRT *transp;
{
union {
char ypbindproc_domain_2_arg[YPMAXDOMAIN + 1];
struct ypbind_setdom ypbindproc_setdom_2_arg;
} argument;
struct authunix_parms *creds;
char *result;
xdrproc_t xdr_argument, xdr_result;
void *(*local) __P((SVCXPRT *, void *));
switch (rqstp->rq_proc) {
case YPBINDPROC_NULL:
xdr_argument = xdr_void;
xdr_result = xdr_void;
local = ypbindproc_null_2;
break;
case YPBINDPROC_DOMAIN:
xdr_argument = xdr_ypdomain_wrap_string;
xdr_result = xdr_ypbind_resp;
local = ypbindproc_domain_2;
break;
case YPBINDPROC_SETDOM:
switch (rqstp->rq_cred.oa_flavor) {
case AUTH_UNIX:
creds = (struct authunix_parms *)rqstp->rq_clntcred;
if (creds->aup_uid != 0) {
svcerr_auth(transp, AUTH_BADCRED);
return;
}
break;
default:
svcerr_auth(transp, AUTH_TOOWEAK);
return;
}
xdr_argument = xdr_ypbind_setdom;
xdr_result = xdr_void;
local = ypbindproc_setdom_2;
break;
default:
svcerr_noproc(transp);
return;
}
(void)memset(&argument, 0, sizeof(argument));
if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
svcerr_decode(transp);
return;
}
result = (*local)(transp, &argument);
if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
svcerr_systemerr(transp);
}
return;
}
int
main(argc, argv)
int argc;
char *argv[];
{
struct timeval tv;
fd_set fdsr;
int width, lockfd;
int evil = 0, one;
char pathname[MAXPATHLEN];
struct stat st;
yp_get_default_domain(&domainname);
if (domainname[0] == '\0')
errx(1, "Domainname not set. Aborting.");
/*
* Per traditional ypbind(8) semantics, if a ypservers
* file does not exist, we default to broadcast mode.
* If the file does exist, we default to direct mode.
* Note that we can still override direct mode by passing
* the -broadcast flag.
*/
snprintf(pathname, sizeof(pathname), "%s/%s%s", BINDINGDIR,
domainname, YPSERVERSSUFF);
if (stat(pathname, &st) < 0) {
#ifdef DEBUG
if (debug)
fprintf(stderr,
"%s does not exist, defaulting to broadcast\n",
pathname);
#endif
ypbindmode = YPBIND_BROADCAST;
} else
ypbindmode = YPBIND_DIRECT;
while (--argc) {
++argv;
if (!strcmp("-insecure", *argv))
insecure = 1;
else if (!strcmp("-ypset", *argv))
ypbindmode = YPBIND_SETALL;
else if (!strcmp("-ypsetme", *argv))
ypbindmode = YPBIND_SETLOCAL;
else if (!strcmp("-broadcast", *argv))
ypbindmode = YPBIND_BROADCAST;
#ifdef DEBUG
else if (!strcmp("-d", *argv))
debug++;
#endif
else
usage();
}
/* initialise syslog */
openlog("ypbind", LOG_PERROR | LOG_PID, LOG_DAEMON);
/* blow away everything in BINDINGDIR */
lockfd = open(_PATH_YPBIND_LOCK, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644);
if (lockfd == -1)
err(1, "Cannot create %s", _PATH_YPBIND_LOCK);
#if O_SHLOCK == 0
(void)flock(lockfd, LOCK_SH);
#endif
(void)pmap_unset(YPBINDPROG, YPBINDVERS);
udptransp = svcudp_create(RPC_ANYSOCK);
if (udptransp == NULL)
errx(1, "Cannot create udp service.");
if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
IPPROTO_UDP))
errx(1, "Unable to register (YPBINDPROG, YPBINDVERS, udp).");
tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
if (tcptransp == NULL)
errx(1, "Cannot create tcp service.");
if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
IPPROTO_TCP))
errx(1, "Unable to register (YPBINDPROG, YPBINDVERS, tcp).");
/* XXX use SOCK_STREAM for direct queries? */
if ((rpcsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
err(1, "rpc socket");
if ((pingsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
err(1, "ping socket");
(void)fcntl(rpcsock, F_SETFL, fcntl(rpcsock, F_GETFL, 0) | FNDELAY);
(void)fcntl(pingsock, F_SETFL, fcntl(pingsock, F_GETFL, 0) | FNDELAY);
one = 1;
(void)setsockopt(rpcsock, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one));
rmtca.prog = YPPROG;
rmtca.vers = YPVERS;
rmtca.proc = YPPROC_DOMAIN_NONACK;
rmtca.xdr_args = NULL; /* set at call time */
rmtca.args_ptr = NULL; /* set at call time */
rmtcr.port_ptr = &rmtcr_port;
rmtcr.xdr_results = xdr_bool;
rmtcr.results_ptr = (caddr_t)&rmtcr_outval;
if (_yp_invalid_domain(domainname))
errx(1, "bad domainname: %s", domainname);
/* build initial domain binding, make it "unsuccessful" */
ypbindlist = makebinding(domainname);
ypbindlist->dom_vers = YPVERS;
ypbindlist->dom_alive = 0;
ypbindlist->dom_lockfd = -1;
removelock(ypbindlist);
checkwork();
width = svc_maxfd;
if (rpcsock > width)
width = rpcsock;
if (pingsock > width)
width = pingsock;
width++;
for (;;) {
fdsr = svc_fdset;
FD_SET(rpcsock, &fdsr);
FD_SET(pingsock, &fdsr);
tv.tv_sec = 1;
tv.tv_usec = 0;
switch (select(width, &fdsr, NULL, NULL, &tv)) {
case 0:
checkwork();
break;
case -1:
yp_log(LOG_WARNING, "select: %m");
break;
default:
if (FD_ISSET(rpcsock, &fdsr))
handle_replies();
if (FD_ISSET(pingsock, &fdsr))
handle_ping();
svc_getreqset(&fdsr);
if (check)
checkwork();
break;
}
if (!evil && ypbindlist->dom_alive) {
evil = 1;
#ifdef DEBUG
if (!debug)
#endif
daemon(0, 0);
pidfile(NULL);
}
}
}
/*
* State transition is done like this:
*
* STATE EVENT ACTION NEWSTATE TIMEOUT
* no binding timeout broadcast no binding 5 sec
* no binding answer -- binding 60 sec
* binding timeout ping server checking 5 sec
* checking timeout ping server + broadcast checking 5 sec
* checking answer -- binding 60 sec
*/
void
checkwork()
{
struct _dom_binding *ypdb;
time_t t;
check = 0;
time(&t);
for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
if (ypdb->dom_check_t < t) {
if (ypdb->dom_alive == 1)
ping(ypdb);
else
nag_servers(ypdb);
time(&t);
ypdb->dom_check_t = t + 5;
}
}
}
int
ping(ypdb)
struct _dom_binding *ypdb;
{
char *dom = ypdb->dom_domain;
struct rpc_msg msg;
char buf[BUFSIZE];
enum clnt_stat st;
int outlen;
AUTH *rpcua;
XDR xdr;
(void)memset(&xdr, 0, sizeof xdr);
(void)memset(&msg, 0, sizeof msg);
rpcua = authunix_create_default();
if (rpcua == NULL) {
#ifdef DEBUG
if (debug)
printf("cannot get unix auth\n");
#endif
return RPC_SYSTEMERROR;
}
msg.rm_direction = CALL;
msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
msg.rm_call.cb_prog = YPPROG;
msg.rm_call.cb_vers = YPVERS;
msg.rm_call.cb_proc = YPPROC_DOMAIN_NONACK;
msg.rm_call.cb_cred = rpcua->ah_cred;
msg.rm_call.cb_verf = rpcua->ah_verf;
msg.rm_xid = ypdb->dom_xid;
xdrmem_create(&xdr, buf, sizeof buf, XDR_ENCODE);
if (!xdr_callmsg(&xdr, &msg)) {
st = RPC_CANTENCODEARGS;
AUTH_DESTROY(rpcua);
return st;
}
if (!xdr_ypdomain_wrap_string(&xdr, &dom)) {
st = RPC_CANTENCODEARGS;
AUTH_DESTROY(rpcua);
return st;
}
outlen = (int)xdr_getpos(&xdr);
xdr_destroy(&xdr);
if (outlen < 1) {
st = RPC_CANTENCODEARGS;
AUTH_DESTROY(rpcua);
return st;
}
AUTH_DESTROY(rpcua);
ypdb->dom_alive = 2;
if (sendto(pingsock, buf, outlen, 0,
(struct sockaddr *)&ypdb->dom_server_addr,
sizeof ypdb->dom_server_addr) == -1)
yp_log(LOG_WARNING, "ping: sendto: %m");
return 0;
}
static int
nag_servers(ypdb)
struct _dom_binding *ypdb;
{
char *dom = ypdb->dom_domain;
struct rpc_msg msg;
char buf[BUFSIZE];
enum clnt_stat st;
int outlen;
AUTH *rpcua;
XDR xdr;
rmtca.xdr_args = xdr_ypdomain_wrap_string;
rmtca.args_ptr = (char *)&dom;
(void)memset(&xdr, 0, sizeof xdr);
(void)memset(&msg, 0, sizeof msg);
rpcua = authunix_create_default();
if (rpcua == NULL) {
#ifdef DEBUG
if (debug)
printf("cannot get unix auth\n");
#endif
return RPC_SYSTEMERROR;
}
msg.rm_direction = CALL;
msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
msg.rm_call.cb_prog = PMAPPROG;
msg.rm_call.cb_vers = PMAPVERS;
msg.rm_call.cb_proc = PMAPPROC_CALLIT;
msg.rm_call.cb_cred = rpcua->ah_cred;
msg.rm_call.cb_verf = rpcua->ah_verf;
msg.rm_xid = ypdb->dom_xid;
xdrmem_create(&xdr, buf, sizeof buf, XDR_ENCODE);
if (!xdr_callmsg(&xdr, &msg)) {
st = RPC_CANTENCODEARGS;
AUTH_DESTROY(rpcua);
return st;
}
if (!xdr_rmtcall_args(&xdr, &rmtca)) {
st = RPC_CANTENCODEARGS;
AUTH_DESTROY(rpcua);
return st;
}
outlen = (int)xdr_getpos(&xdr);
xdr_destroy(&xdr);
if (outlen < 1) {
st = RPC_CANTENCODEARGS;
AUTH_DESTROY(rpcua);
return st;
}
AUTH_DESTROY(rpcua);
if (ypdb->dom_lockfd != -1) {
(void)close(ypdb->dom_lockfd);
ypdb->dom_lockfd = -1;
removelock(ypdb);
}
if (ypdb->dom_alive == 2) {
/*
* This resolves the following situation:
* ypserver on other subnet was once bound,
* but rebooted and is now using a different port
*/
struct sockaddr_in bindsin;
memset(&bindsin, 0, sizeof bindsin);
bindsin.sin_family = AF_INET;
bindsin.sin_len = sizeof(bindsin);
bindsin.sin_port = htons(PMAPPORT);
bindsin.sin_addr = ypdb->dom_server_addr.sin_addr;
if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
sizeof bindsin) == -1)
yp_log(LOG_WARNING, "broadcast: sendto: %m");
}
switch (ypbindmode) {
case YPBIND_SETALL:
case YPBIND_SETLOCAL:
if (been_ypset)
return direct_set(buf, outlen, ypdb);
/* FALLTHROUGH */
case YPBIND_BROADCAST:
return broadcast(buf, outlen);
case YPBIND_DIRECT:
return direct(buf, outlen);
}
return -1;
}
static int
broadcast(buf, outlen)
char *buf;
int outlen;
{
struct ifaddrs *ifap, *ifa;
struct sockaddr_in bindsin;
struct in_addr in;
memset(&bindsin, 0, sizeof bindsin);
bindsin.sin_family = AF_INET;
bindsin.sin_len = sizeof(bindsin);
bindsin.sin_port = htons(PMAPPORT);
if (getifaddrs(&ifap) != 0) {
yp_log(LOG_WARNING, "broadcast: getifaddrs: %m");
return (-1);
}
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
if ((ifa->ifa_flags & IFF_UP) == 0)
continue;
switch (ifa->ifa_flags & (IFF_LOOPBACK | IFF_BROADCAST)) {
case IFF_BROADCAST:
if (!ifa->ifa_broadaddr)
continue;
if (ifa->ifa_broadaddr->sa_family != AF_INET)
continue;
in = ((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr;
break;
case IFF_LOOPBACK:
in = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
break;
default:
continue;
}
bindsin.sin_addr = in;
if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
bindsin.sin_len) == -1)
yp_log(LOG_WARNING, "broadcast: sendto: %m");
}
freeifaddrs(ifap);
return (0);
}
static int
direct(buf, outlen)
char *buf;
int outlen;
{
static FILE *df;
static char ypservers_path[MAXPATHLEN];
char line[_POSIX2_LINE_MAX];
char *p;
struct hostent *hp;
struct sockaddr_in bindsin;
int i, count = 0;
if (df)
rewind(df);
else {
snprintf(ypservers_path, sizeof(ypservers_path),
"%s/%s%s", BINDINGDIR, domainname, YPSERVERSSUFF);
df = fopen(ypservers_path, "r");
if (df == NULL) {
yp_log(LOG_WARNING, "%s: ", ypservers_path);
exit(1);
}
}
memset(&bindsin, 0, sizeof bindsin);
bindsin.sin_family = AF_INET;
bindsin.sin_len = sizeof(bindsin);
bindsin.sin_port = htons(PMAPPORT);
while(fgets(line, sizeof(line), df) != NULL) {
/* skip lines that are too big */
p = strchr(line, '\n');
if (p == NULL) {
int c;
while ((c = getc(df)) != '\n' && c != EOF)
;
continue;
}
*p = '\0';
p = line;
while (isspace(*p))
p++;
if (*p == '#')
continue;
hp = gethostbyname(p);
if (!hp) {
yp_log(LOG_ERR, "%s: %s", p, hstrerror(h_errno));
continue;
}
/* step through all addresses in case first is unavailable */
for (i = 0; hp->h_addr_list[i]; i++) {
memmove(&bindsin.sin_addr, hp->h_addr_list[0],
hp->h_length);
if (sendto(rpcsock, buf, outlen, 0,
(struct sockaddr *)&bindsin, sizeof bindsin) < 0) {
yp_log(LOG_WARNING, "direct: sendto: %m");
continue;
} else
count++;
}
}
if (!count) {
yp_log(LOG_ERR, "no contactable servers found in %s",
ypservers_path);
return -1;
}
return 0;
}
static int
direct_set(buf, outlen, ypdb)
char *buf;
int outlen;
struct _dom_binding *ypdb;
{
struct sockaddr_in bindsin;
char path[MAXPATHLEN];
struct iovec iov[2];
struct ypbind_resp ybr;
SVCXPRT dummy_svc;
int fd, bytes;
/*
* Gack, we lose if binding file went away. We reset
* "been_set" if this happens, otherwise we'll never
* bind again.
*/
snprintf(path, sizeof(path), "%s/%s.%ld", BINDINGDIR,
ypdb->dom_domain, ypdb->dom_vers);
if ((fd = open(path, O_SHLOCK|O_RDONLY, 0644)) == -1) {
yp_log(LOG_WARNING, "%s: %m", path);
been_ypset = 0;
return -1;
}
#if O_SHLOCK == 0
(void)flock(fd, LOCK_SH);
#endif
/* Read the binding file... */
iov[0].iov_base = (caddr_t)&(dummy_svc.xp_port);
iov[0].iov_len = sizeof(dummy_svc.xp_port);
iov[1].iov_base = (caddr_t)&ybr;
iov[1].iov_len = sizeof(ybr);
bytes = readv(fd, iov, 2);
(void)close(fd);
if (bytes != (iov[0].iov_len + iov[1].iov_len)) {
/* Binding file corrupt? */
yp_log(LOG_WARNING, "%s: %m", path);
been_ypset = 0;
return -1;
}
bindsin.sin_addr =
ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr;
if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
sizeof(bindsin)) < 0) {
yp_log(LOG_WARNING, "direct_set: sendto: %m");
return -1;
}
return 0;
}
static enum clnt_stat
handle_replies()
{
char buf[BUFSIZE];
int fromlen, inlen;
struct _dom_binding *ypdb;
struct sockaddr_in raddr;
struct rpc_msg msg;
XDR xdr;
recv_again:
(void)memset(&xdr, 0, sizeof(xdr));
(void)memset(&msg, 0, sizeof(msg));
msg.acpted_rply.ar_verf = _null_auth;
msg.acpted_rply.ar_results.where = (caddr_t)&rmtcr;
msg.acpted_rply.ar_results.proc = xdr_rmtcallres;
try_again:
fromlen = sizeof(struct sockaddr);
inlen = recvfrom(rpcsock, buf, sizeof buf, 0,
(struct sockaddr *)&raddr, &fromlen);
if (inlen < 0) {
if (errno == EINTR)
goto try_again;
return RPC_CANTRECV;
}
if (inlen < sizeof(u_int32_t))
goto recv_again;
/*
* see if reply transaction id matches sent id.
* If so, decode the results.
*/
xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE);
if (xdr_replymsg(&xdr, &msg)) {
if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
(msg.acpted_rply.ar_stat == SUCCESS)) {
raddr.sin_port = htons((u_short)rmtcr_port);
ypdb = xid2ypdb(msg.rm_xid);
if (ypdb != NULL)
rpc_received(ypdb->dom_domain, &raddr, 0);
}
}
xdr.x_op = XDR_FREE;
msg.acpted_rply.ar_results.proc = xdr_void;
xdr_destroy(&xdr);
return RPC_SUCCESS;
}
static enum clnt_stat
handle_ping()
{
char buf[BUFSIZE];
int fromlen, inlen;
struct _dom_binding *ypdb;
struct sockaddr_in raddr;
struct rpc_msg msg;
XDR xdr;
bool_t res;
recv_again:
(void)memset(&xdr, 0, sizeof(xdr));
(void)memset(&msg, 0, sizeof(msg));
msg.acpted_rply.ar_verf = _null_auth;
msg.acpted_rply.ar_results.where = (caddr_t)&res;
msg.acpted_rply.ar_results.proc = xdr_bool;
try_again:
fromlen = sizeof (struct sockaddr);
inlen = recvfrom(pingsock, buf, sizeof buf, 0,
(struct sockaddr *)&raddr, &fromlen);
if (inlen < 0) {
if (errno == EINTR)
goto try_again;
return RPC_CANTRECV;
}
if (inlen < sizeof(u_int32_t))
goto recv_again;
/*
* see if reply transaction id matches sent id.
* If so, decode the results.
*/
xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE);
if (xdr_replymsg(&xdr, &msg)) {
if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
(msg.acpted_rply.ar_stat == SUCCESS)) {
ypdb = xid2ypdb(msg.rm_xid);
if (ypdb != NULL)
rpc_received(ypdb->dom_domain, &raddr, 0);
}
}
xdr.x_op = XDR_FREE;
msg.acpted_rply.ar_results.proc = xdr_void;
xdr_destroy(&xdr);
return RPC_SUCCESS;
}
/*
* LOOPBACK IS MORE IMPORTANT: PUT IN HACK
*/
void
rpc_received(dom, raddrp, force)
char *dom;
struct sockaddr_in *raddrp;
int force;
{
struct _dom_binding *ypdb;
struct iovec iov[2];
struct ypbind_resp ybr;
int fd;
#ifdef DEBUG
if (debug)
printf("returned from %s about %s\n",
inet_ntoa(raddrp->sin_addr), dom);
#endif
if (dom == NULL)
return;
if (_yp_invalid_domain(dom))
return;
/* don't support insecure servers by default */
if (!insecure && ntohs(raddrp->sin_port) >= IPPORT_RESERVED)
return;
for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
if (!strcmp(ypdb->dom_domain, dom))
break;
if (ypdb == NULL) {
if (force == 0)
return;
ypdb = makebinding(dom);
ypdb->dom_lockfd = -1;
ypdb->dom_pnext = ypbindlist;
ypbindlist = ypdb;
}
/* soft update, alive */
if (ypdb->dom_alive == 1 && force == 0) {
if (!memcmp(&ypdb->dom_server_addr, raddrp,
sizeof ypdb->dom_server_addr)) {
ypdb->dom_alive = 1;
/* recheck binding in 60 sec */
ypdb->dom_check_t = time(NULL) + 60;
}
return;
}
(void)memcpy(&ypdb->dom_server_addr, raddrp,
sizeof ypdb->dom_server_addr);
/* recheck binding in 60 seconds */
ypdb->dom_check_t = time(NULL) + 60;
ypdb->dom_vers = YPVERS;
ypdb->dom_alive = 1;
if (ypdb->dom_lockfd != -1)
(void)close(ypdb->dom_lockfd);
if ((fd = makelock(ypdb)) == -1)
return;
/*
* ok, if BINDINGDIR exists, and we can create the binding file,
* then write to it..
*/
ypdb->dom_lockfd = fd;
iov[0].iov_base = (caddr_t)&(udptransp->xp_port);
iov[0].iov_len = sizeof udptransp->xp_port;
iov[1].iov_base = (caddr_t)&ybr;
iov[1].iov_len = sizeof ybr;
(void)memset(&ybr, 0, sizeof ybr);
ybr.ypbind_status = YPBIND_SUCC_VAL;
ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr =
raddrp->sin_addr;
ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port =
raddrp->sin_port;
if (writev(ypdb->dom_lockfd, iov, 2) !=
iov[0].iov_len + iov[1].iov_len) {
yp_log(LOG_WARNING, "writev: %m");
(void)close(ypdb->dom_lockfd);
removelock(ypdb);
ypdb->dom_lockfd = -1;
}
}
static struct _dom_binding *
xid2ypdb(xid)
u_int32_t xid;
{
struct _dom_binding *ypdb;
for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
if (ypdb->dom_xid == xid)
break;
return (ypdb);
}
static u_int32_t
unique_xid(ypdb)
struct _dom_binding *ypdb;
{
u_int32_t tmp_xid;
tmp_xid = (u_int32_t)(((u_long)ypdb) & 0xffffffff);
while (xid2ypdb(tmp_xid) != NULL)
tmp_xid++;
return tmp_xid;
}