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