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