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