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