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