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