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