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