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