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