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