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