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