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