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