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