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