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