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