Home | History | Annotate | Line # | Download | only in ypbind
ypbind.c revision 1.36
      1 /*	$NetBSD: ypbind.c,v 1.36 1998/07/13 12:01:03 mrg Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1992, 1993 Theo de Raadt <deraadt (at) fsa.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by Theo de Raadt.
     18  * 4. The name of the author may not be used to endorse or promote
     19  *    products derived from this software without specific prior written
     20  *    permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     23  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     26  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 #include <sys/cdefs.h>
     36 #ifndef LINT
     37 __RCSID("$NetBSD: ypbind.c,v 1.36 1998/07/13 12:01:03 mrg Exp $");
     38 #endif
     39 
     40 #include <sys/param.h>
     41 #include <sys/types.h>
     42 #include <sys/ioctl.h>
     43 #include <sys/signal.h>
     44 #include <sys/socket.h>
     45 #include <sys/file.h>
     46 #include <sys/fcntl.h>
     47 #include <sys/uio.h>
     48 #include <sys/syslog.h>
     49 #include <sys/stat.h>
     50 #include <limits.h>
     51 #include <stdio.h>
     52 #include <stdlib.h>
     53 #include <errno.h>
     54 #include <ctype.h>
     55 #include <dirent.h>
     56 #include <netdb.h>
     57 #include <string.h>
     58 #include <err.h>
     59 #include <rpc/rpc.h>
     60 #include <rpc/xdr.h>
     61 #include <net/if.h>
     62 #include <arpa/inet.h>
     63 #include <rpc/pmap_clnt.h>
     64 #include <rpc/pmap_prot.h>
     65 #include <rpc/pmap_rmt.h>
     66 #include <unistd.h>
     67 #include <rpcsvc/yp_prot.h>
     68 #include <rpcsvc/ypclnt.h>
     69 
     70 #include "pathnames.h"
     71 
     72 #ifndef O_SHLOCK
     73 #define O_SHLOCK 0
     74 #endif
     75 
     76 #define BUFSIZE		1400
     77 
     78 #define YPSERVERSSUFF	".ypservers"
     79 #define BINDINGDIR	__CONCAT(_PATH_VAR_YP, "binding")
     80 
     81 struct _dom_binding {
     82 	struct _dom_binding *dom_pnext;
     83 	char dom_domain[YPMAXDOMAIN + 1];
     84 	struct sockaddr_in dom_server_addr;
     85 	unsigned short int dom_server_port;
     86 	int dom_socket;
     87 	CLIENT *dom_client;
     88 	long dom_vers;
     89 	time_t dom_check_t;
     90 	time_t dom_ask_t;
     91 	int dom_lockfd;
     92 	int dom_alive;
     93 	u_int32_t dom_xid;
     94 };
     95 
     96 static char *domainname;
     97 
     98 static struct _dom_binding *ypbindlist;
     99 static int check;
    100 
    101 typedef enum {
    102 	YPBIND_DIRECT, YPBIND_BROADCAST, YPBIND_SETLOCAL, YPBIND_SETALL
    103 } ypbind_mode_t;
    104 
    105 ypbind_mode_t ypbindmode;
    106 
    107 /*
    108  * If ypbindmode is YPBIND_SETLOCAL or YPBIND_SETALL, this indicates
    109  * whether or not we've been "ypset".  If we haven't, we behave like
    110  * YPBIND_BROADCAST.  If we have, we behave like YPBIND_DIRECT.
    111  */
    112 int been_ypset;
    113 
    114 #ifdef DEBUG
    115 static int debug;
    116 #endif
    117 
    118 static int insecure;
    119 static int rpcsock, pingsock;
    120 static struct rmtcallargs rmtca;
    121 static struct rmtcallres rmtcr;
    122 static bool_t rmtcr_outval;
    123 static u_long rmtcr_port;
    124 static SVCXPRT *udptransp, *tcptransp;
    125 
    126 int	_yp_invalid_domain __P((const char *));		/* from libc */
    127 int	main __P((int, char *[]));
    128 
    129 static void usage __P((void));
    130 static struct _dom_binding *makebinding __P((const char *));
    131 static int makelock __P((struct _dom_binding *));
    132 static void removelock __P((struct _dom_binding *));
    133 static void *ypbindproc_null_2 __P((SVCXPRT *, void *));
    134 static void *ypbindproc_domain_2 __P((SVCXPRT *, void *));
    135 static void *ypbindproc_setdom_2 __P((SVCXPRT *, void *));
    136 static void ypbindprog_2 __P((struct svc_req *, SVCXPRT *));
    137 static void checkwork __P((void));
    138 static int ping __P((struct _dom_binding *));
    139 static int nag_servers __P((struct _dom_binding *));
    140 static enum clnt_stat handle_replies __P((void));
    141 static enum clnt_stat handle_ping __P((void));
    142 static void rpc_received __P((char *, struct sockaddr_in *, int));
    143 static struct _dom_binding *xid2ypdb __P((u_int32_t));
    144 static u_int32_t unique_xid __P((struct _dom_binding *));
    145 static int broadcast __P((char *, int));
    146 static int direct __P((char *, int));
    147 static int direct_set __P((char *, int, struct _dom_binding *));
    148 
    149 static void
    150 usage()
    151 {
    152 	extern char *__progname;
    153 	char *opt = "";
    154 #ifdef DEBUG
    155 	opt = " [-d]";
    156 #endif
    157 
    158 	(void)fprintf(stderr,
    159 	    "Usage: %s [-broadcast] [-insecure] [-ypset] [-ypsetme] %s\n",
    160 	    __progname, opt);
    161 	exit(1);
    162 }
    163 
    164 static struct _dom_binding *
    165 makebinding(dm)
    166 	const char *dm;
    167 {
    168 	struct _dom_binding *ypdb;
    169 
    170 	if ((ypdb = (struct _dom_binding *)malloc(sizeof *ypdb)) == NULL)
    171 		err(1, "makebinding");
    172 
    173 	(void)memset(ypdb, 0, sizeof *ypdb);
    174 	(void)strncpy(ypdb->dom_domain, dm, sizeof ypdb->dom_domain);
    175 	ypdb->dom_domain[sizeof(ypdb->dom_domain) - 1] = '\0';
    176 	return ypdb;
    177 }
    178 
    179 static int
    180 makelock(ypdb)
    181 	struct _dom_binding *ypdb;
    182 {
    183 	int fd;
    184 	char path[MAXPATHLEN];
    185 
    186 	(void)snprintf(path, sizeof(path), "%s/%s.%ld", BINDINGDIR,
    187 	    ypdb->dom_domain, ypdb->dom_vers);
    188 
    189 	if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) {
    190 		(void)mkdir(BINDINGDIR, 0755);
    191 		if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1)
    192 			return -1;
    193 	}
    194 
    195 #if O_SHLOCK == 0
    196 	(void)flock(fd, LOCK_SH);
    197 #endif
    198 	return fd;
    199 }
    200 
    201 static void
    202 removelock(ypdb)
    203 	struct _dom_binding *ypdb;
    204 {
    205 	char path[MAXPATHLEN];
    206 
    207 	(void)snprintf(path, sizeof(path), "%s/%s.%ld",
    208 	    BINDINGDIR, ypdb->dom_domain, ypdb->dom_vers);
    209 	(void)unlink(path);
    210 }
    211 
    212 static void *
    213 ypbindproc_null_2(transp, argp)
    214 	SVCXPRT *transp;
    215 	void *argp;
    216 {
    217 	static char res;
    218 
    219 #ifdef DEBUG
    220 	if (debug)
    221 		printf("ypbindproc_null_2\n");
    222 #endif
    223 	(void)memset(&res, 0, sizeof(res));
    224 	return (void *)&res;
    225 }
    226 
    227 static void *
    228 ypbindproc_domain_2(transp, argp)
    229 	SVCXPRT *transp;
    230 	void *argp;
    231 {
    232 	static struct ypbind_resp res;
    233 	struct _dom_binding *ypdb;
    234 	char *arg = *(char **) argp;
    235 	time_t now;
    236 	int count;
    237 
    238 #ifdef DEBUG
    239 	if (debug)
    240 		printf("ypbindproc_domain_2 %s\n", arg);
    241 #endif
    242 	if (_yp_invalid_domain(arg))
    243 		return NULL;
    244 
    245 	(void)memset(&res, 0, sizeof res);
    246 	res.ypbind_status = YPBIND_FAIL_VAL;
    247 
    248 	for (count = 0, ypdb = ypbindlist;
    249 	    ypdb != NULL;
    250 	    ypdb = ypdb->dom_pnext, count++) {
    251 		if (count > 100)
    252 			return NULL;		/* prevent denial of service */
    253 		if (!strcmp(ypdb->dom_domain, arg))
    254 			break;
    255 	}
    256 
    257 	if (ypdb == NULL) {
    258 		ypdb = makebinding(arg);
    259 		ypdb->dom_vers = YPVERS;
    260 		ypdb->dom_alive = 0;
    261 		ypdb->dom_lockfd = -1;
    262 		removelock(ypdb);
    263 		ypdb->dom_xid = unique_xid(ypdb);
    264 		ypdb->dom_pnext = ypbindlist;
    265 		ypbindlist = ypdb;
    266 		check++;
    267 #ifdef DEBUG
    268 		if (debug)
    269 			printf("unknown domain %s\n", arg);
    270 #endif
    271 		return NULL;
    272 	}
    273 
    274 	if (ypdb->dom_alive == 0) {
    275 #ifdef DEBUG
    276 		if (debug)
    277 			printf("dead domain %s\n", arg);
    278 #endif
    279 		return NULL;
    280 	}
    281 
    282 #ifdef HEURISTIC
    283 	time(&now);
    284 	if (now < ypdb->dom_ask_t + 5) {
    285 		/*
    286 		 * Hmm. More than 2 requests in 5 seconds have indicated
    287 		 * that my binding is possibly incorrect.
    288 		 * Ok, do an immediate poll of the server.
    289 		 */
    290 		if (ypdb->dom_check_t >= now) {
    291 			/* don't flood it */
    292 			ypdb->dom_check_t = 0;
    293 			check++;
    294 		}
    295 	}
    296 	ypdb->dom_ask_t = now;
    297 #endif
    298 
    299 	res.ypbind_status = YPBIND_SUCC_VAL;
    300 	res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr.s_addr =
    301 		ypdb->dom_server_addr.sin_addr.s_addr;
    302 	res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port =
    303 		ypdb->dom_server_port;
    304 #ifdef DEBUG
    305 	if (debug)
    306 		printf("domain %s at %s/%d\n", ypdb->dom_domain,
    307 		    inet_ntoa(ypdb->dom_server_addr.sin_addr),
    308 		    ntohs(ypdb->dom_server_addr.sin_port));
    309 #endif
    310 	return &res;
    311 }
    312 
    313 static void *
    314 ypbindproc_setdom_2(transp, argp)
    315 	SVCXPRT *transp;
    316 	void *argp;
    317 {
    318 	struct ypbind_setdom *sd = argp;
    319 	struct sockaddr_in *fromsin, bindsin;
    320 	static bool_t res;
    321 
    322 #ifdef DEBUG
    323 	if (debug)
    324 		printf("ypbindproc_setdom_2 %s\n", inet_ntoa(bindsin.sin_addr));
    325 #endif
    326 	(void)memset(&res, 0, sizeof(res));
    327 	fromsin = svc_getcaller(transp);
    328 
    329 	switch (ypbindmode) {
    330 	case YPBIND_SETLOCAL:
    331 		if (fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
    332 #ifdef DEBUG
    333 			if (debug)
    334 				printf("ypset from %s denied\n",
    335 				    inet_ntoa(fromsin->sin_addr));
    336 #endif
    337 			return NULL;
    338 		}
    339 		/* FALLTHROUGH */
    340 
    341 	case YPBIND_SETALL:
    342 		been_ypset = 1;
    343 		break;
    344 
    345 	case YPBIND_DIRECT:
    346 	case YPBIND_BROADCAST:
    347 	default:
    348 #ifdef DEBUG
    349 		if (debug)
    350 			printf("ypset denied\n");
    351 #endif
    352 		return NULL;
    353 	}
    354 
    355 	if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) {
    356 #ifdef DEBUG
    357 		if (debug)
    358 			printf("ypset from unpriviledged port denied\n");
    359 #endif
    360 		return &res;
    361 	}
    362 
    363 	if (sd->ypsetdom_vers != YPVERS) {
    364 #ifdef DEBUG
    365 		if (debug)
    366 			printf("ypset with wrong version denied\n");
    367 #endif
    368 		return &res;
    369 	}
    370 
    371 	(void)memset(&bindsin, 0, sizeof bindsin);
    372 	bindsin.sin_family = AF_INET;
    373 	bindsin.sin_len = sizeof(bindsin);
    374 	bindsin.sin_addr = sd->ypsetdom_addr;
    375 	bindsin.sin_port = sd->ypsetdom_port;
    376 	rpc_received(sd->ypsetdom_domain, &bindsin, 1);
    377 
    378 #ifdef DEBUG
    379 	if (debug)
    380 		printf("ypset to %s succeeded\n", inet_ntoa(bindsin.sin_addr));
    381 #endif
    382 	res = 1;
    383 	return &res;
    384 }
    385 
    386 static void
    387 ypbindprog_2(rqstp, transp)
    388 	struct svc_req *rqstp;
    389 	register SVCXPRT *transp;
    390 {
    391 	union {
    392 		char ypbindproc_domain_2_arg[YPMAXDOMAIN + 1];
    393 		struct ypbind_setdom ypbindproc_setdom_2_arg;
    394 	} argument;
    395 	struct authunix_parms *creds;
    396 	char *result;
    397 	xdrproc_t xdr_argument, xdr_result;
    398 	void *(*local) __P((SVCXPRT *, void *));
    399 
    400 	switch (rqstp->rq_proc) {
    401 	case YPBINDPROC_NULL:
    402 		xdr_argument = xdr_void;
    403 		xdr_result = xdr_void;
    404 		local = ypbindproc_null_2;
    405 		break;
    406 
    407 	case YPBINDPROC_DOMAIN:
    408 		xdr_argument = xdr_ypdomain_wrap_string;
    409 		xdr_result = xdr_ypbind_resp;
    410 		local = ypbindproc_domain_2;
    411 		break;
    412 
    413 	case YPBINDPROC_SETDOM:
    414 		switch (rqstp->rq_cred.oa_flavor) {
    415 		case AUTH_UNIX:
    416 			creds = (struct authunix_parms *)rqstp->rq_clntcred;
    417 			if (creds->aup_uid != 0) {
    418 				svcerr_auth(transp, AUTH_BADCRED);
    419 				return;
    420 			}
    421 			break;
    422 		default:
    423 			svcerr_auth(transp, AUTH_TOOWEAK);
    424 			return;
    425 		}
    426 
    427 		xdr_argument = xdr_ypbind_setdom;
    428 		xdr_result = xdr_void;
    429 		local = ypbindproc_setdom_2;
    430 		break;
    431 
    432 	default:
    433 		svcerr_noproc(transp);
    434 		return;
    435 	}
    436 	(void)memset(&argument, 0, sizeof(argument));
    437 	if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
    438 		svcerr_decode(transp);
    439 		return;
    440 	}
    441 	result = (*local)(transp, &argument);
    442 	if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
    443 		svcerr_systemerr(transp);
    444 	}
    445 	return;
    446 }
    447 
    448 int
    449 main(argc, argv)
    450 	int argc;
    451 	char *argv[];
    452 {
    453 	struct timeval tv;
    454 	fd_set fdsr;
    455 	int width, lockfd;
    456 	int evil = 0, one;
    457 	char pathname[MAXPATHLEN];
    458 	struct stat st;
    459 
    460 	yp_get_default_domain(&domainname);
    461 	if (domainname[0] == '\0')
    462 		errx(1, "Domainname not set. Aborting.");
    463 
    464 	/*
    465 	 * Per traditional ypbind(8) semantics, if a ypservers
    466 	 * file does not exist, we default to broadcast mode.
    467 	 * If the file does exist, we default to direct mode.
    468 	 * Note that we can still override direct mode by passing
    469 	 * the -broadcast flag.
    470 	 */
    471 	snprintf(pathname, sizeof(pathname), "%s/%s%s", BINDINGDIR,
    472 	    domainname, YPSERVERSSUFF);
    473 	if (stat(pathname, &st) < 0) {
    474 #ifdef DEBUG
    475 		if (debug)
    476 			fprintf(stderr,
    477 			    "%s does not exist, defaulting to broadcast\n",
    478 			    pathname);
    479 #endif
    480 		ypbindmode = YPBIND_BROADCAST;
    481 	} else
    482 		ypbindmode = YPBIND_DIRECT;
    483 
    484 	while (--argc) {
    485 		++argv;
    486 		if (!strcmp("-insecure", *argv))
    487 			insecure = 1;
    488 		else if (!strcmp("-ypset", *argv))
    489 			ypbindmode = YPBIND_SETALL;
    490 		else if (!strcmp("-ypsetme", *argv))
    491 			ypbindmode = YPBIND_SETLOCAL;
    492 		else if (!strcmp("-broadcast", *argv))
    493 			ypbindmode = YPBIND_BROADCAST;
    494 #ifdef DEBUG
    495 		else if (!strcmp("-d", *argv))
    496 			debug++;
    497 #endif
    498 		else
    499 			usage();
    500 	}
    501 
    502 	/* blow away everything in BINDINGDIR */
    503 
    504 	lockfd = open(_PATH_YPBIND_LOCK, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644);
    505 	if (lockfd == -1)
    506 		err(1, "Cannot create %s", _PATH_YPBIND_LOCK);
    507 
    508 #if O_SHLOCK == 0
    509 	(void)flock(lockfd, LOCK_SH);
    510 #endif
    511 
    512 	(void)pmap_unset(YPBINDPROG, YPBINDVERS);
    513 
    514 	udptransp = svcudp_create(RPC_ANYSOCK);
    515 	if (udptransp == NULL)
    516 		errx(1, "Cannot create udp service.");
    517 
    518 	if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
    519 	    IPPROTO_UDP))
    520 		errx(1, "Unable to register (YPBINDPROG, YPBINDVERS, udp).");
    521 
    522 	tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
    523 	if (tcptransp == NULL)
    524 		errx(1, "Cannot create tcp service.");
    525 
    526 	if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
    527 	    IPPROTO_TCP))
    528 		errx(1, "Unable to register (YPBINDPROG, YPBINDVERS, tcp).");
    529 
    530 	/* XXX use SOCK_STREAM for direct queries? */
    531 	if ((rpcsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
    532 		err(1, "rpc socket");
    533 	if ((pingsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
    534 		err(1, "ping socket");
    535 
    536 	(void)fcntl(rpcsock, F_SETFL, fcntl(rpcsock, F_GETFL, 0) | FNDELAY);
    537 	(void)fcntl(pingsock, F_SETFL, fcntl(pingsock, F_GETFL, 0) | FNDELAY);
    538 
    539 	one = 1;
    540 	(void)setsockopt(rpcsock, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one));
    541 	rmtca.prog = YPPROG;
    542 	rmtca.vers = YPVERS;
    543 	rmtca.proc = YPPROC_DOMAIN_NONACK;
    544 	rmtca.xdr_args = NULL;		/* set at call time */
    545 	rmtca.args_ptr = NULL;		/* set at call time */
    546 	rmtcr.port_ptr = &rmtcr_port;
    547 	rmtcr.xdr_results = xdr_bool;
    548 	rmtcr.results_ptr = (caddr_t)&rmtcr_outval;
    549 
    550 	if (_yp_invalid_domain(domainname))
    551 		errx(1, "bad domainname: %s", domainname);
    552 
    553 	/* build initial domain binding, make it "unsuccessful" */
    554 	ypbindlist = makebinding(domainname);
    555 	ypbindlist->dom_vers = YPVERS;
    556 	ypbindlist->dom_alive = 0;
    557 	ypbindlist->dom_lockfd = -1;
    558 	removelock(ypbindlist);
    559 
    560 	checkwork();
    561 
    562 	width = svc_maxfd;
    563 	if (rpcsock > width)
    564 		width = rpcsock;
    565 	if (pingsock > width)
    566 		width = pingsock;
    567 	width++;
    568 
    569 	for (;;) {
    570 		fdsr = svc_fdset;
    571 		FD_SET(rpcsock, &fdsr);
    572 		FD_SET(pingsock, &fdsr);
    573 		tv.tv_sec = 1;
    574 		tv.tv_usec = 0;
    575 
    576 		switch (select(width, &fdsr, NULL, NULL, &tv)) {
    577 		case 0:
    578 			checkwork();
    579 			break;
    580 		case -1:
    581 			warn("select");
    582 			break;
    583 		default:
    584 			if (FD_ISSET(rpcsock, &fdsr))
    585 				handle_replies();
    586 			if (FD_ISSET(pingsock, &fdsr))
    587 				handle_ping();
    588 			svc_getreqset(&fdsr);
    589 			if (check)
    590 				checkwork();
    591 			break;
    592 		}
    593 
    594 		if (!evil && ypbindlist->dom_alive) {
    595 			evil = 1;
    596 #ifdef DEBUG
    597 			if (!debug)
    598 #endif
    599 				daemon(0, 0);
    600 		}
    601 	}
    602 }
    603 
    604 /*
    605  * State transition is done like this:
    606  *
    607  * STATE	EVENT		ACTION			NEWSTATE	TIMEOUT
    608  * no binding	timeout		broadcast 		no binding	5 sec
    609  * no binding	answer		--			binding		60 sec
    610  * binding	timeout		ping server		checking	5 sec
    611  * checking	timeout		ping server + broadcast	checking	5 sec
    612  * checking	answer		--			binding		60 sec
    613  */
    614 void
    615 checkwork()
    616 {
    617 	struct _dom_binding *ypdb;
    618 	time_t t;
    619 
    620 	check = 0;
    621 
    622 	time(&t);
    623 	for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
    624 		if (ypdb->dom_check_t < t) {
    625 			if (ypdb->dom_alive == 1)
    626 				ping(ypdb);
    627 			else
    628 				nag_servers(ypdb);
    629 			time(&t);
    630 			ypdb->dom_check_t = t + 5;
    631 		}
    632 	}
    633 }
    634 
    635 int
    636 ping(ypdb)
    637 	struct _dom_binding *ypdb;
    638 {
    639 	char *dom = ypdb->dom_domain;
    640 	struct rpc_msg msg;
    641 	char buf[BUFSIZE];
    642 	enum clnt_stat st;
    643 	int outlen;
    644 	AUTH *rpcua;
    645 	XDR xdr;
    646 
    647 	(void)memset(&xdr, 0, sizeof xdr);
    648 	(void)memset(&msg, 0, sizeof msg);
    649 
    650 	rpcua = authunix_create_default();
    651 	if (rpcua == NULL) {
    652 #ifdef DEBUG
    653 		if (debug)
    654 			printf("cannot get unix auth\n");
    655 #endif
    656 		return RPC_SYSTEMERROR;
    657 	}
    658 
    659 	msg.rm_direction = CALL;
    660 	msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
    661 	msg.rm_call.cb_prog = YPPROG;
    662 	msg.rm_call.cb_vers = YPVERS;
    663 	msg.rm_call.cb_proc = YPPROC_DOMAIN_NONACK;
    664 	msg.rm_call.cb_cred = rpcua->ah_cred;
    665 	msg.rm_call.cb_verf = rpcua->ah_verf;
    666 
    667 	msg.rm_xid = ypdb->dom_xid;
    668 	xdrmem_create(&xdr, buf, sizeof buf, XDR_ENCODE);
    669 	if (!xdr_callmsg(&xdr, &msg)) {
    670 		st = RPC_CANTENCODEARGS;
    671 		AUTH_DESTROY(rpcua);
    672 		return st;
    673 	}
    674 	if (!xdr_ypdomain_wrap_string(&xdr, &dom)) {
    675 		st = RPC_CANTENCODEARGS;
    676 		AUTH_DESTROY(rpcua);
    677 		return st;
    678 	}
    679 	outlen = (int)xdr_getpos(&xdr);
    680 	xdr_destroy(&xdr);
    681 	if (outlen < 1) {
    682 		st = RPC_CANTENCODEARGS;
    683 		AUTH_DESTROY(rpcua);
    684 		return st;
    685 	}
    686 	AUTH_DESTROY(rpcua);
    687 
    688 	ypdb->dom_alive = 2;
    689 	if (sendto(pingsock, buf, outlen, 0,
    690 		   (struct sockaddr *)&ypdb->dom_server_addr,
    691 		   sizeof ypdb->dom_server_addr) == -1)
    692 		warn("ping: sendto");
    693 	return 0;
    694 
    695 }
    696 
    697 static int
    698 nag_servers(ypdb)
    699 	struct _dom_binding *ypdb;
    700 {
    701 	char *dom = ypdb->dom_domain;
    702 	struct rpc_msg msg;
    703 	char buf[BUFSIZE];
    704 	enum clnt_stat st;
    705 	int outlen;
    706 	AUTH *rpcua;
    707 	XDR xdr;
    708 
    709 	rmtca.xdr_args = xdr_ypdomain_wrap_string;
    710 	rmtca.args_ptr = (char *)&dom;
    711 
    712 	(void)memset(&xdr, 0, sizeof xdr);
    713 	(void)memset(&msg, 0, sizeof msg);
    714 
    715 	rpcua = authunix_create_default();
    716 	if (rpcua == NULL) {
    717 #ifdef DEBUG
    718 		if (debug)
    719 			printf("cannot get unix auth\n");
    720 #endif
    721 		return RPC_SYSTEMERROR;
    722 	}
    723 	msg.rm_direction = CALL;
    724 	msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
    725 	msg.rm_call.cb_prog = PMAPPROG;
    726 	msg.rm_call.cb_vers = PMAPVERS;
    727 	msg.rm_call.cb_proc = PMAPPROC_CALLIT;
    728 	msg.rm_call.cb_cred = rpcua->ah_cred;
    729 	msg.rm_call.cb_verf = rpcua->ah_verf;
    730 
    731 	msg.rm_xid = ypdb->dom_xid;
    732 	xdrmem_create(&xdr, buf, sizeof buf, XDR_ENCODE);
    733 	if (!xdr_callmsg(&xdr, &msg)) {
    734 		st = RPC_CANTENCODEARGS;
    735 		AUTH_DESTROY(rpcua);
    736 		return st;
    737 	}
    738 	if (!xdr_rmtcall_args(&xdr, &rmtca)) {
    739 		st = RPC_CANTENCODEARGS;
    740 		AUTH_DESTROY(rpcua);
    741 		return st;
    742 	}
    743 	outlen = (int)xdr_getpos(&xdr);
    744 	xdr_destroy(&xdr);
    745 	if (outlen < 1) {
    746 		st = RPC_CANTENCODEARGS;
    747 		AUTH_DESTROY(rpcua);
    748 		return st;
    749 	}
    750 	AUTH_DESTROY(rpcua);
    751 
    752 	if (ypdb->dom_lockfd != -1) {
    753 		(void)close(ypdb->dom_lockfd);
    754 		ypdb->dom_lockfd = -1;
    755 		removelock(ypdb);
    756 	}
    757 
    758 	if (ypdb->dom_alive == 2) {
    759 		/*
    760 		 * This resolves the following situation:
    761 		 * ypserver on other subnet was once bound,
    762 		 * but rebooted and is now using a different port
    763 		 */
    764 		struct sockaddr_in bindsin;
    765 
    766 		memset(&bindsin, 0, sizeof bindsin);
    767 		bindsin.sin_family = AF_INET;
    768 		bindsin.sin_len = sizeof(bindsin);
    769 		bindsin.sin_port = htons(PMAPPORT);
    770 		bindsin.sin_addr = ypdb->dom_server_addr.sin_addr;
    771 
    772 		if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
    773 			   sizeof bindsin) == -1)
    774 			warn("broadcast: sendto");
    775 	}
    776 
    777 	switch (ypbindmode) {
    778 	case YPBIND_SETALL:
    779 	case YPBIND_SETLOCAL:
    780 		if (been_ypset)
    781 			return direct_set(buf, outlen, ypdb);
    782 		/* FALLTHROUGH */
    783 
    784 	case YPBIND_BROADCAST:
    785 		return broadcast(buf, outlen);
    786 
    787 	case YPBIND_DIRECT:
    788 		return direct(buf, outlen);
    789 	}
    790 
    791 	return -1;
    792 }
    793 
    794 static int
    795 broadcast(buf, outlen)
    796 	char *buf;
    797 	int outlen;
    798 {
    799 	struct ifconf ifc;
    800 	struct ifreq ifreq, *ifr;
    801 	struct in_addr in;
    802 	int i, sock, len;
    803 	char inbuf[8192];
    804 	struct sockaddr_in bindsin;
    805 
    806 	/* find all networks and send the RPC packet out them all */
    807 	if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
    808 		warn("broadcast: socket");
    809 		return -1;
    810 	}
    811 
    812 	memset(&bindsin, 0, sizeof bindsin);
    813 	bindsin.sin_family = AF_INET;
    814 	bindsin.sin_len = sizeof(bindsin);
    815 	bindsin.sin_port = htons(PMAPPORT);
    816 
    817 	ifc.ifc_len = sizeof inbuf;
    818 	ifc.ifc_buf = inbuf;
    819 	if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
    820 		(void)close(sock);
    821 		warn("broadcast: ioctl(SIOCGIFCONF)");
    822 		return -1;
    823 	}
    824 	ifr = ifc.ifc_req;
    825 	ifreq.ifr_name[0] = '\0';
    826 	for (i = 0; i < ifc.ifc_len; i += len, ifr = (struct ifreq *)((caddr_t)ifr + len)) {
    827 #if defined(BSD) && BSD >= 199103
    828 		len = sizeof ifr->ifr_name + ifr->ifr_addr.sa_len;
    829 #else
    830 		len = sizeof ifc.ifc_len / sizeof(struct ifreq);
    831 #endif
    832 		ifreq = *ifr;
    833 		if (ifreq.ifr_addr.sa_family != AF_INET)
    834 			continue;
    835 		if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) {
    836 			warn("broadcast: ioctl(SIOCGIFFLAGS)");
    837 			continue;
    838 		}
    839 		if ((ifreq.ifr_flags & IFF_UP) == 0)
    840 			continue;
    841 
    842 		ifreq.ifr_flags &= (IFF_LOOPBACK | IFF_BROADCAST);
    843 		if (ifreq.ifr_flags == IFF_BROADCAST) {
    844 			if (ioctl(sock, SIOCGIFBRDADDR, &ifreq) < 0) {
    845 				warn("broadcast: ioctl(SIOCGIFBRDADDR)");
    846 				continue;
    847 			}
    848 		} else if (ifreq.ifr_flags == IFF_LOOPBACK) {
    849 			if (ioctl(sock, SIOCGIFADDR, &ifreq) < 0) {
    850 				warn("broadcast: ioctl(SIOCGIFADDR)");
    851 				continue;
    852 			}
    853 		} else
    854 			continue;
    855 
    856 		in = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
    857 		bindsin.sin_addr = in;
    858 		if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
    859 			   sizeof bindsin) == -1)
    860 			warn("broadcast: sendto");
    861 	}
    862 	(void)close(sock);
    863 	return 0;
    864 }
    865 
    866 static int
    867 direct(buf, outlen)
    868 	char *buf;
    869 	int outlen;
    870 {
    871 	static FILE *df;
    872 	static char ypservers_path[MAXPATHLEN];
    873 	char line[_POSIX2_LINE_MAX];
    874 	char *p;
    875 	struct hostent *hp;
    876 	struct sockaddr_in bindsin;
    877 	int i, count = 0;
    878 
    879 	if (df)
    880 		rewind(df);
    881 	else {
    882 		snprintf(ypservers_path, sizeof(ypservers_path),
    883 		    "%s/%s%s", BINDINGDIR, domainname, YPSERVERSSUFF);
    884 		df = fopen(ypservers_path, "r");
    885 		if (df == NULL)
    886 			err(1, ypservers_path);
    887 	}
    888 
    889 	memset(&bindsin, 0, sizeof bindsin);
    890 	bindsin.sin_family = AF_INET;
    891 	bindsin.sin_len = sizeof(bindsin);
    892 	bindsin.sin_port = htons(PMAPPORT);
    893 
    894 	while(fgets(line, sizeof(line), df) != NULL) {
    895 		/* skip lines that are too big */
    896 		p = strchr(line, '\n');
    897 		if (p == NULL) {
    898 			int c;
    899 
    900 			while ((c = getc(df)) != '\n' && c != EOF)
    901 				;
    902 			continue;
    903 		}
    904 		*p = '\0';
    905 		p = line;
    906 		while (isspace(*p))
    907 			p++;
    908 		if (*p == '#')
    909 			continue;
    910 		hp = gethostbyname(p);
    911 		if (!hp) {
    912 			herror(p);
    913 			continue;
    914 		}
    915 		/* step through all addresses in case first is unavailable */
    916 		for (i = 0; hp->h_addr_list[i]; i++) {
    917 			memmove(&bindsin.sin_addr, hp->h_addr_list[0],
    918 			    hp->h_length);
    919 			if (sendto(rpcsock, buf, outlen, 0,
    920 			    (struct sockaddr *)&bindsin, sizeof bindsin) < 0) {
    921 				warn("direct: sendto");
    922 				continue;
    923 			} else
    924 				count++;
    925 		}
    926 	}
    927 	if (!count)
    928 		errx(1, "no contactable servers found in %s",
    929 		    ypservers_path);
    930 
    931 	return 0;
    932 }
    933 
    934 static int
    935 direct_set(buf, outlen, ypdb)
    936 	char *buf;
    937 	int outlen;
    938 	struct _dom_binding *ypdb;
    939 {
    940 	struct sockaddr_in bindsin;
    941 	char path[MAXPATHLEN];
    942 	struct iovec iov[2];
    943 	struct ypbind_resp ybr;
    944 	SVCXPRT dummy_svc;
    945 	int fd, bytes;
    946 
    947 	/*
    948 	 * Gack, we lose if binding file went away.  We reset
    949 	 * "been_set" if this happens, otherwise we'll never
    950 	 * bind again.
    951 	 */
    952 	snprintf(path, sizeof(path), "%s/%s.%ld", BINDINGDIR,
    953 	    ypdb->dom_domain, ypdb->dom_vers);
    954 
    955 	if ((fd = open(path, O_SHLOCK|O_RDONLY, 0644)) == -1) {
    956 		warn(path);
    957 		been_ypset = 0;
    958 		return -1;
    959 	}
    960 
    961 #if O_SHLOCK == 0
    962 	(void)flock(fd, LOCK_SH);
    963 #endif
    964 
    965 	/* Read the binding file... */
    966 	iov[0].iov_base = (caddr_t)&(dummy_svc.xp_port);
    967 	iov[0].iov_len = sizeof(dummy_svc.xp_port);
    968 	iov[1].iov_base = (caddr_t)&ybr;
    969 	iov[1].iov_len = sizeof(ybr);
    970 	bytes = readv(fd, iov, 2);
    971 	(void)close(fd);
    972 	if (bytes != (iov[0].iov_len + iov[1].iov_len)) {
    973 		/* Binding file corrupt? */
    974 		warn(path);
    975 		been_ypset = 0;
    976 		return -1;
    977 	}
    978 
    979 	bindsin.sin_addr =
    980 	    ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr;
    981 
    982 	if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
    983 	    sizeof(bindsin)) < 0) {
    984 		warn("direct_set: sendto");
    985 		return -1;
    986 	}
    987 
    988 	return 0;
    989 }
    990 
    991 static enum clnt_stat
    992 handle_replies()
    993 {
    994 	char buf[BUFSIZE];
    995 	int fromlen, inlen;
    996 	struct _dom_binding *ypdb;
    997 	struct sockaddr_in raddr;
    998 	struct rpc_msg msg;
    999 	XDR xdr;
   1000 
   1001 recv_again:
   1002 	(void)memset(&xdr, 0, sizeof(xdr));
   1003 	(void)memset(&msg, 0, sizeof(msg));
   1004 	msg.acpted_rply.ar_verf = _null_auth;
   1005 	msg.acpted_rply.ar_results.where = (caddr_t)&rmtcr;
   1006 	msg.acpted_rply.ar_results.proc = xdr_rmtcallres;
   1007 
   1008 try_again:
   1009 	fromlen = sizeof(struct sockaddr);
   1010 	inlen = recvfrom(rpcsock, buf, sizeof buf, 0,
   1011 		(struct sockaddr *)&raddr, &fromlen);
   1012 	if (inlen < 0) {
   1013 		if (errno == EINTR)
   1014 			goto try_again;
   1015 		return RPC_CANTRECV;
   1016 	}
   1017 	if (inlen < sizeof(u_int32_t))
   1018 		goto recv_again;
   1019 
   1020 	/*
   1021 	 * see if reply transaction id matches sent id.
   1022 	 * If so, decode the results.
   1023 	 */
   1024 	xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE);
   1025 	if (xdr_replymsg(&xdr, &msg)) {
   1026 		if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
   1027 		    (msg.acpted_rply.ar_stat == SUCCESS)) {
   1028 			raddr.sin_port = htons((u_short)rmtcr_port);
   1029 			ypdb = xid2ypdb(msg.rm_xid);
   1030 			if (ypdb != NULL)
   1031 				rpc_received(ypdb->dom_domain, &raddr, 0);
   1032 		}
   1033 	}
   1034 	xdr.x_op = XDR_FREE;
   1035 	msg.acpted_rply.ar_results.proc = xdr_void;
   1036 	xdr_destroy(&xdr);
   1037 
   1038 	return RPC_SUCCESS;
   1039 }
   1040 
   1041 static enum clnt_stat
   1042 handle_ping()
   1043 {
   1044 	char buf[BUFSIZE];
   1045 	int fromlen, inlen;
   1046 	struct _dom_binding *ypdb;
   1047 	struct sockaddr_in raddr;
   1048 	struct rpc_msg msg;
   1049 	XDR xdr;
   1050 	bool_t res;
   1051 
   1052 recv_again:
   1053 	(void)memset(&xdr, 0, sizeof(xdr));
   1054 	(void)memset(&msg, 0, sizeof(msg));
   1055 	msg.acpted_rply.ar_verf = _null_auth;
   1056 	msg.acpted_rply.ar_results.where = (caddr_t)&res;
   1057 	msg.acpted_rply.ar_results.proc = xdr_bool;
   1058 
   1059 try_again:
   1060 	fromlen = sizeof (struct sockaddr);
   1061 	inlen = recvfrom(pingsock, buf, sizeof buf, 0,
   1062 		(struct sockaddr *)&raddr, &fromlen);
   1063 	if (inlen < 0) {
   1064 		if (errno == EINTR)
   1065 			goto try_again;
   1066 		return RPC_CANTRECV;
   1067 	}
   1068 	if (inlen < sizeof(u_int32_t))
   1069 		goto recv_again;
   1070 
   1071 	/*
   1072 	 * see if reply transaction id matches sent id.
   1073 	 * If so, decode the results.
   1074 	 */
   1075 	xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE);
   1076 	if (xdr_replymsg(&xdr, &msg)) {
   1077 		if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
   1078 		    (msg.acpted_rply.ar_stat == SUCCESS)) {
   1079 			ypdb = xid2ypdb(msg.rm_xid);
   1080 			if (ypdb != NULL)
   1081 				rpc_received(ypdb->dom_domain, &raddr, 0);
   1082 		}
   1083 	}
   1084 	xdr.x_op = XDR_FREE;
   1085 	msg.acpted_rply.ar_results.proc = xdr_void;
   1086 	xdr_destroy(&xdr);
   1087 
   1088 	return RPC_SUCCESS;
   1089 }
   1090 
   1091 /*
   1092  * LOOPBACK IS MORE IMPORTANT: PUT IN HACK
   1093  */
   1094 void
   1095 rpc_received(dom, raddrp, force)
   1096 	char *dom;
   1097 	struct sockaddr_in *raddrp;
   1098 	int force;
   1099 {
   1100 	struct _dom_binding *ypdb;
   1101 	struct iovec iov[2];
   1102 	struct ypbind_resp ybr;
   1103 	int fd;
   1104 
   1105 #ifdef DEBUG
   1106 	if (debug)
   1107 		printf("returned from %s about %s\n",
   1108 		    inet_ntoa(raddrp->sin_addr), dom);
   1109 #endif
   1110 
   1111 	if (dom == NULL)
   1112 		return;
   1113 
   1114 	if (_yp_invalid_domain(dom))
   1115 		return;
   1116 
   1117 		/* don't support insecure servers by default */
   1118 	if (!insecure && ntohs(raddrp->sin_port) >= IPPORT_RESERVED)
   1119 		return;
   1120 
   1121 	for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
   1122 		if (!strcmp(ypdb->dom_domain, dom))
   1123 			break;
   1124 
   1125 	if (ypdb == NULL) {
   1126 		if (force == 0)
   1127 			return;
   1128 		ypdb = makebinding(dom);
   1129 		ypdb->dom_lockfd = -1;
   1130 		ypdb->dom_pnext = ypbindlist;
   1131 		ypbindlist = ypdb;
   1132 	}
   1133 
   1134 	/* soft update, alive */
   1135 	if (ypdb->dom_alive == 1 && force == 0) {
   1136 		if (!memcmp(&ypdb->dom_server_addr, raddrp,
   1137 			    sizeof ypdb->dom_server_addr)) {
   1138 			ypdb->dom_alive = 1;
   1139 			/* recheck binding in 60 sec */
   1140 			ypdb->dom_check_t = time(NULL) + 60;
   1141 		}
   1142 		return;
   1143 	}
   1144 
   1145 	(void)memcpy(&ypdb->dom_server_addr, raddrp,
   1146 	    sizeof ypdb->dom_server_addr);
   1147 	/* recheck binding in 60 seconds */
   1148 	ypdb->dom_check_t = time(NULL) + 60;
   1149 	ypdb->dom_vers = YPVERS;
   1150 	ypdb->dom_alive = 1;
   1151 
   1152 	if (ypdb->dom_lockfd != -1)
   1153 		(void)close(ypdb->dom_lockfd);
   1154 
   1155 	if ((fd = makelock(ypdb)) == -1)
   1156 		return;
   1157 
   1158 	/*
   1159 	 * ok, if BINDINGDIR exists, and we can create the binding file,
   1160 	 * then write to it..
   1161 	 */
   1162 	ypdb->dom_lockfd = fd;
   1163 
   1164 	iov[0].iov_base = (caddr_t)&(udptransp->xp_port);
   1165 	iov[0].iov_len = sizeof udptransp->xp_port;
   1166 	iov[1].iov_base = (caddr_t)&ybr;
   1167 	iov[1].iov_len = sizeof ybr;
   1168 
   1169 	(void)memset(&ybr, 0, sizeof ybr);
   1170 	ybr.ypbind_status = YPBIND_SUCC_VAL;
   1171 	ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr =
   1172 	    raddrp->sin_addr;
   1173 	ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port =
   1174 	    raddrp->sin_port;
   1175 
   1176 	if (writev(ypdb->dom_lockfd, iov, 2) !=
   1177 	    iov[0].iov_len + iov[1].iov_len) {
   1178 		warnx("writev");
   1179 		(void)close(ypdb->dom_lockfd);
   1180 		removelock(ypdb);
   1181 		ypdb->dom_lockfd = -1;
   1182 	}
   1183 }
   1184 
   1185 static struct _dom_binding *
   1186 xid2ypdb(xid)
   1187 	u_int32_t xid;
   1188 {
   1189 	struct _dom_binding *ypdb;
   1190 
   1191 	for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
   1192 		if (ypdb->dom_xid == xid)
   1193 			break;
   1194 	return (ypdb);
   1195 }
   1196 
   1197 static u_int32_t
   1198 unique_xid(ypdb)
   1199 	struct _dom_binding *ypdb;
   1200 {
   1201 	u_int32_t tmp_xid;
   1202 
   1203 	tmp_xid = (u_int32_t)(((u_long)ypdb) & 0xffffffff);
   1204 	while (xid2ypdb(tmp_xid) != NULL)
   1205 		tmp_xid++;
   1206 
   1207 	return tmp_xid;
   1208 }
   1209