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