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