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