ypbind.c revision 1.47 1 /* $NetBSD: ypbind.c,v 1.47 2002/07/06 21:44:40 wiz 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.47 2002/07/06 21:44:40 wiz 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 width = svc_maxfd;
576 if (rpcsock > width)
577 width = rpcsock;
578 if (pingsock > width)
579 width = pingsock;
580 width++;
581
582 for (;;) {
583 fdsr = svc_fdset;
584 FD_SET(rpcsock, &fdsr);
585 FD_SET(pingsock, &fdsr);
586 tv.tv_sec = 1;
587 tv.tv_usec = 0;
588
589 switch (select(width, &fdsr, NULL, NULL, &tv)) {
590 case 0:
591 checkwork();
592 break;
593 case -1:
594 yp_log(LOG_WARNING, "select: %m");
595 break;
596 default:
597 if (FD_ISSET(rpcsock, &fdsr))
598 handle_replies();
599 if (FD_ISSET(pingsock, &fdsr))
600 handle_ping();
601 svc_getreqset(&fdsr);
602 if (check)
603 checkwork();
604 break;
605 }
606
607 if (!evil && ypbindlist->dom_alive) {
608 evil = 1;
609 #ifdef DEBUG
610 if (!debug)
611 #endif
612 daemon(0, 0);
613 pidfile(NULL);
614 }
615 }
616 }
617
618 /*
619 * State transition is done like this:
620 *
621 * STATE EVENT ACTION NEWSTATE TIMEOUT
622 * no binding timeout broadcast no binding 5 sec
623 * no binding answer -- binding 60 sec
624 * binding timeout ping server checking 5 sec
625 * checking timeout ping server + broadcast checking 5 sec
626 * checking answer -- binding 60 sec
627 */
628 void
629 checkwork(void)
630 {
631 struct _dom_binding *ypdb;
632 time_t t;
633
634 check = 0;
635
636 time(&t);
637 for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
638 if (ypdb->dom_check_t < t) {
639 if (ypdb->dom_alive == 1)
640 ping(ypdb);
641 else
642 nag_servers(ypdb);
643 time(&t);
644 ypdb->dom_check_t = t + 5;
645 }
646 }
647 }
648
649 int
650 ping(struct _dom_binding *ypdb)
651 {
652 char *dom = ypdb->dom_domain;
653 struct rpc_msg msg;
654 char buf[BUFSIZE];
655 enum clnt_stat st;
656 int outlen;
657 AUTH *rpcua;
658 XDR xdr;
659
660 (void)memset(&xdr, 0, sizeof xdr);
661 (void)memset(&msg, 0, sizeof msg);
662
663 rpcua = authunix_create_default();
664 if (rpcua == NULL) {
665 #ifdef DEBUG
666 if (debug)
667 printf("cannot get unix auth\n");
668 #endif
669 return RPC_SYSTEMERROR;
670 }
671
672 msg.rm_direction = CALL;
673 msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
674 msg.rm_call.cb_prog = YPPROG;
675 msg.rm_call.cb_vers = YPVERS;
676 msg.rm_call.cb_proc = YPPROC_DOMAIN_NONACK;
677 msg.rm_call.cb_cred = rpcua->ah_cred;
678 msg.rm_call.cb_verf = rpcua->ah_verf;
679
680 msg.rm_xid = ypdb->dom_xid;
681 xdrmem_create(&xdr, buf, sizeof buf, XDR_ENCODE);
682 if (!xdr_callmsg(&xdr, &msg)) {
683 st = RPC_CANTENCODEARGS;
684 AUTH_DESTROY(rpcua);
685 return st;
686 }
687 if (!xdr_ypdomain_wrap_string(&xdr, &dom)) {
688 st = RPC_CANTENCODEARGS;
689 AUTH_DESTROY(rpcua);
690 return st;
691 }
692 outlen = (int)xdr_getpos(&xdr);
693 xdr_destroy(&xdr);
694 if (outlen < 1) {
695 st = RPC_CANTENCODEARGS;
696 AUTH_DESTROY(rpcua);
697 return st;
698 }
699 AUTH_DESTROY(rpcua);
700
701 ypdb->dom_alive = 2;
702 if (sendto(pingsock, buf, outlen, 0,
703 (struct sockaddr *)&ypdb->dom_server_addr,
704 sizeof ypdb->dom_server_addr) == -1)
705 yp_log(LOG_WARNING, "ping: sendto: %m");
706 return 0;
707
708 }
709
710 static int
711 nag_servers(struct _dom_binding *ypdb)
712 {
713 char *dom = ypdb->dom_domain;
714 struct rpc_msg msg;
715 char buf[BUFSIZE];
716 enum clnt_stat st;
717 int outlen;
718 AUTH *rpcua;
719 XDR xdr;
720
721 rmtca.xdr_args = xdr_ypdomain_wrap_string;
722 rmtca.args_ptr = (char *)&dom;
723
724 (void)memset(&xdr, 0, sizeof xdr);
725 (void)memset(&msg, 0, sizeof msg);
726
727 rpcua = authunix_create_default();
728 if (rpcua == NULL) {
729 #ifdef DEBUG
730 if (debug)
731 printf("cannot get unix auth\n");
732 #endif
733 return RPC_SYSTEMERROR;
734 }
735 msg.rm_direction = CALL;
736 msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
737 msg.rm_call.cb_prog = PMAPPROG;
738 msg.rm_call.cb_vers = PMAPVERS;
739 msg.rm_call.cb_proc = PMAPPROC_CALLIT;
740 msg.rm_call.cb_cred = rpcua->ah_cred;
741 msg.rm_call.cb_verf = rpcua->ah_verf;
742
743 msg.rm_xid = ypdb->dom_xid;
744 xdrmem_create(&xdr, buf, sizeof buf, XDR_ENCODE);
745 if (!xdr_callmsg(&xdr, &msg)) {
746 st = RPC_CANTENCODEARGS;
747 AUTH_DESTROY(rpcua);
748 return st;
749 }
750 if (!xdr_rmtcall_args(&xdr, &rmtca)) {
751 st = RPC_CANTENCODEARGS;
752 AUTH_DESTROY(rpcua);
753 return st;
754 }
755 outlen = (int)xdr_getpos(&xdr);
756 xdr_destroy(&xdr);
757 if (outlen < 1) {
758 st = RPC_CANTENCODEARGS;
759 AUTH_DESTROY(rpcua);
760 return st;
761 }
762 AUTH_DESTROY(rpcua);
763
764 if (ypdb->dom_lockfd != -1) {
765 (void)close(ypdb->dom_lockfd);
766 ypdb->dom_lockfd = -1;
767 removelock(ypdb);
768 }
769
770 if (ypdb->dom_alive == 2) {
771 /*
772 * This resolves the following situation:
773 * ypserver on other subnet was once bound,
774 * but rebooted and is now using a different port
775 */
776 struct sockaddr_in bindsin;
777
778 memset(&bindsin, 0, sizeof bindsin);
779 bindsin.sin_family = AF_INET;
780 bindsin.sin_len = sizeof(bindsin);
781 bindsin.sin_port = htons(PMAPPORT);
782 bindsin.sin_addr = ypdb->dom_server_addr.sin_addr;
783
784 if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
785 sizeof bindsin) == -1)
786 yp_log(LOG_WARNING, "broadcast: sendto: %m");
787 }
788
789 switch (ypbindmode) {
790 case YPBIND_SETALL:
791 case YPBIND_SETLOCAL:
792 if (been_ypset)
793 return direct_set(buf, outlen, ypdb);
794 /* FALLTHROUGH */
795
796 case YPBIND_BROADCAST:
797 return broadcast(buf, outlen);
798
799 case YPBIND_DIRECT:
800 return direct(buf, outlen);
801 }
802
803 return -1;
804 }
805
806 static int
807 broadcast(char *buf, int outlen)
808 {
809 struct ifaddrs *ifap, *ifa;
810 struct sockaddr_in bindsin;
811 struct in_addr in;
812
813 memset(&bindsin, 0, sizeof bindsin);
814 bindsin.sin_family = AF_INET;
815 bindsin.sin_len = sizeof(bindsin);
816 bindsin.sin_port = htons(PMAPPORT);
817
818 if (getifaddrs(&ifap) != 0) {
819 yp_log(LOG_WARNING, "broadcast: getifaddrs: %m");
820 return (-1);
821 }
822 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
823 if (ifa->ifa_addr->sa_family != AF_INET)
824 continue;
825 if ((ifa->ifa_flags & IFF_UP) == 0)
826 continue;
827
828 switch (ifa->ifa_flags & (IFF_LOOPBACK | IFF_BROADCAST)) {
829 case IFF_BROADCAST:
830 if (!ifa->ifa_broadaddr)
831 continue;
832 if (ifa->ifa_broadaddr->sa_family != AF_INET)
833 continue;
834 in = ((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr;
835 break;
836 case IFF_LOOPBACK:
837 in = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
838 break;
839 default:
840 continue;
841 }
842
843 bindsin.sin_addr = in;
844 if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
845 bindsin.sin_len) == -1)
846 yp_log(LOG_WARNING, "broadcast: sendto: %m");
847 }
848 freeifaddrs(ifap);
849 return (0);
850 }
851
852 static int
853 direct(char *buf, int outlen)
854 {
855 static FILE *df;
856 static char ypservers_path[MAXPATHLEN];
857 char line[_POSIX2_LINE_MAX];
858 char *p;
859 struct hostent *hp;
860 struct sockaddr_in bindsin;
861 int i, count = 0;
862
863 if (df)
864 rewind(df);
865 else {
866 snprintf(ypservers_path, sizeof(ypservers_path),
867 "%s/%s%s", BINDINGDIR, domainname, YPSERVERSSUFF);
868 df = fopen(ypservers_path, "r");
869 if (df == NULL) {
870 yp_log(LOG_ERR, "%s: ", ypservers_path);
871 exit(1);
872 }
873 }
874
875 memset(&bindsin, 0, sizeof bindsin);
876 bindsin.sin_family = AF_INET;
877 bindsin.sin_len = sizeof(bindsin);
878 bindsin.sin_port = htons(PMAPPORT);
879
880 while(fgets(line, sizeof(line), df) != NULL) {
881 /* skip lines that are too big */
882 p = strchr(line, '\n');
883 if (p == NULL) {
884 int c;
885
886 while ((c = getc(df)) != '\n' && c != EOF)
887 ;
888 continue;
889 }
890 *p = '\0';
891 p = line;
892 while (isspace(*p))
893 p++;
894 if (*p == '#')
895 continue;
896 hp = gethostbyname(p);
897 if (!hp) {
898 yp_log(LOG_WARNING, "%s: %s", p, hstrerror(h_errno));
899 continue;
900 }
901 /* step through all addresses in case first is unavailable */
902 for (i = 0; hp->h_addr_list[i]; i++) {
903 memmove(&bindsin.sin_addr, hp->h_addr_list[0],
904 hp->h_length);
905 if (sendto(rpcsock, buf, outlen, 0,
906 (struct sockaddr *)&bindsin, sizeof bindsin) < 0) {
907 yp_log(LOG_WARNING, "direct: sendto: %m");
908 continue;
909 } else
910 count++;
911 }
912 }
913 if (!count) {
914 yp_log(LOG_WARNING, "no contactable servers found in %s",
915 ypservers_path);
916 return -1;
917 }
918 return 0;
919 }
920
921 static int
922 direct_set(char *buf, int outlen, struct _dom_binding *ypdb)
923 {
924 struct sockaddr_in bindsin;
925 char path[MAXPATHLEN];
926 struct iovec iov[2];
927 struct ypbind_resp ybr;
928 SVCXPRT dummy_svc;
929 int fd, bytes;
930
931 /*
932 * Gack, we lose if binding file went away. We reset
933 * "been_set" if this happens, otherwise we'll never
934 * bind again.
935 */
936 snprintf(path, sizeof(path), "%s/%s.%ld", BINDINGDIR,
937 ypdb->dom_domain, ypdb->dom_vers);
938
939 if ((fd = open(path, O_SHLOCK|O_RDONLY, 0644)) == -1) {
940 yp_log(LOG_WARNING, "%s: %m", path);
941 been_ypset = 0;
942 return -1;
943 }
944
945 #if O_SHLOCK == 0
946 (void)flock(fd, LOCK_SH);
947 #endif
948
949 /* Read the binding file... */
950 iov[0].iov_base = (caddr_t)&(dummy_svc.xp_port);
951 iov[0].iov_len = sizeof(dummy_svc.xp_port);
952 iov[1].iov_base = (caddr_t)&ybr;
953 iov[1].iov_len = sizeof(ybr);
954 bytes = readv(fd, iov, 2);
955 (void)close(fd);
956 if (bytes != (iov[0].iov_len + iov[1].iov_len)) {
957 /* Binding file corrupt? */
958 yp_log(LOG_WARNING, "%s: %m", path);
959 been_ypset = 0;
960 return -1;
961 }
962
963 bindsin.sin_addr =
964 ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr;
965
966 if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
967 sizeof(bindsin)) < 0) {
968 yp_log(LOG_WARNING, "direct_set: sendto: %m");
969 return -1;
970 }
971
972 return 0;
973 }
974
975 static enum clnt_stat
976 handle_replies(void)
977 {
978 char buf[BUFSIZE];
979 int fromlen, inlen;
980 struct _dom_binding *ypdb;
981 struct sockaddr_in raddr;
982 struct rpc_msg msg;
983 XDR xdr;
984
985 recv_again:
986 (void)memset(&xdr, 0, sizeof(xdr));
987 (void)memset(&msg, 0, sizeof(msg));
988 msg.acpted_rply.ar_verf = _null_auth;
989 msg.acpted_rply.ar_results.where = (caddr_t)&rmtcr;
990 msg.acpted_rply.ar_results.proc = xdr_rmtcallres;
991
992 try_again:
993 fromlen = sizeof(struct sockaddr);
994 inlen = recvfrom(rpcsock, buf, sizeof buf, 0,
995 (struct sockaddr *)&raddr, &fromlen);
996 if (inlen < 0) {
997 if (errno == EINTR)
998 goto try_again;
999 return RPC_CANTRECV;
1000 }
1001 if (inlen < sizeof(u_int32_t))
1002 goto recv_again;
1003
1004 /*
1005 * see if reply transaction id matches sent id.
1006 * If so, decode the results.
1007 */
1008 xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE);
1009 if (xdr_replymsg(&xdr, &msg)) {
1010 if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
1011 (msg.acpted_rply.ar_stat == SUCCESS)) {
1012 raddr.sin_port = htons((u_short)rmtcr_port);
1013 ypdb = xid2ypdb(msg.rm_xid);
1014 if (ypdb != NULL)
1015 rpc_received(ypdb->dom_domain, &raddr, 0);
1016 }
1017 }
1018 xdr.x_op = XDR_FREE;
1019 msg.acpted_rply.ar_results.proc = xdr_void;
1020 xdr_destroy(&xdr);
1021
1022 return RPC_SUCCESS;
1023 }
1024
1025 static enum clnt_stat
1026 handle_ping(void)
1027 {
1028 char buf[BUFSIZE];
1029 int fromlen, inlen;
1030 struct _dom_binding *ypdb;
1031 struct sockaddr_in raddr;
1032 struct rpc_msg msg;
1033 XDR xdr;
1034 bool_t res;
1035
1036 recv_again:
1037 (void)memset(&xdr, 0, sizeof(xdr));
1038 (void)memset(&msg, 0, sizeof(msg));
1039 msg.acpted_rply.ar_verf = _null_auth;
1040 msg.acpted_rply.ar_results.where = (caddr_t)&res;
1041 msg.acpted_rply.ar_results.proc = xdr_bool;
1042
1043 try_again:
1044 fromlen = sizeof (struct sockaddr);
1045 inlen = recvfrom(pingsock, buf, sizeof buf, 0,
1046 (struct sockaddr *)&raddr, &fromlen);
1047 if (inlen < 0) {
1048 if (errno == EINTR)
1049 goto try_again;
1050 return RPC_CANTRECV;
1051 }
1052 if (inlen < sizeof(u_int32_t))
1053 goto recv_again;
1054
1055 /*
1056 * see if reply transaction id matches sent id.
1057 * If so, decode the results.
1058 */
1059 xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE);
1060 if (xdr_replymsg(&xdr, &msg)) {
1061 if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
1062 (msg.acpted_rply.ar_stat == SUCCESS)) {
1063 ypdb = xid2ypdb(msg.rm_xid);
1064 if (ypdb != NULL)
1065 rpc_received(ypdb->dom_domain, &raddr, 0);
1066 }
1067 }
1068 xdr.x_op = XDR_FREE;
1069 msg.acpted_rply.ar_results.proc = xdr_void;
1070 xdr_destroy(&xdr);
1071
1072 return RPC_SUCCESS;
1073 }
1074
1075 /*
1076 * LOOPBACK IS MORE IMPORTANT: PUT IN HACK
1077 */
1078 void
1079 rpc_received(char *dom, struct sockaddr_in *raddrp, int force)
1080 {
1081 struct _dom_binding *ypdb;
1082 struct iovec iov[2];
1083 struct ypbind_resp ybr;
1084 int fd;
1085
1086 #ifdef DEBUG
1087 if (debug)
1088 printf("returned from %s about %s\n",
1089 inet_ntoa(raddrp->sin_addr), dom);
1090 #endif
1091
1092 if (dom == NULL)
1093 return;
1094
1095 if (_yp_invalid_domain(dom))
1096 return;
1097
1098 /* don't support insecure servers by default */
1099 if (!insecure && ntohs(raddrp->sin_port) >= IPPORT_RESERVED)
1100 return;
1101
1102 for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
1103 if (!strcmp(ypdb->dom_domain, dom))
1104 break;
1105
1106 if (ypdb == NULL) {
1107 if (force == 0)
1108 return;
1109 ypdb = makebinding(dom);
1110 ypdb->dom_lockfd = -1;
1111 ypdb->dom_pnext = ypbindlist;
1112 ypbindlist = ypdb;
1113 }
1114
1115 /* soft update, alive */
1116 if (ypdb->dom_alive == 1 && force == 0) {
1117 if (!memcmp(&ypdb->dom_server_addr, raddrp,
1118 sizeof ypdb->dom_server_addr)) {
1119 ypdb->dom_alive = 1;
1120 /* recheck binding in 60 sec */
1121 ypdb->dom_check_t = time(NULL) + 60;
1122 }
1123 return;
1124 }
1125
1126 (void)memcpy(&ypdb->dom_server_addr, raddrp,
1127 sizeof ypdb->dom_server_addr);
1128 /* recheck binding in 60 seconds */
1129 ypdb->dom_check_t = time(NULL) + 60;
1130 ypdb->dom_vers = YPVERS;
1131 ypdb->dom_alive = 1;
1132
1133 if (ypdb->dom_lockfd != -1)
1134 (void)close(ypdb->dom_lockfd);
1135
1136 if ((fd = makelock(ypdb)) == -1)
1137 return;
1138
1139 /*
1140 * ok, if BINDINGDIR exists, and we can create the binding file,
1141 * then write to it..
1142 */
1143 ypdb->dom_lockfd = fd;
1144
1145 iov[0].iov_base = (caddr_t)&(udptransp->xp_port);
1146 iov[0].iov_len = sizeof udptransp->xp_port;
1147 iov[1].iov_base = (caddr_t)&ybr;
1148 iov[1].iov_len = sizeof ybr;
1149
1150 (void)memset(&ybr, 0, sizeof ybr);
1151 ybr.ypbind_status = YPBIND_SUCC_VAL;
1152 ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr =
1153 raddrp->sin_addr;
1154 ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port =
1155 raddrp->sin_port;
1156
1157 if (writev(ypdb->dom_lockfd, iov, 2) !=
1158 iov[0].iov_len + iov[1].iov_len) {
1159 yp_log(LOG_WARNING, "writev: %m");
1160 (void)close(ypdb->dom_lockfd);
1161 removelock(ypdb);
1162 ypdb->dom_lockfd = -1;
1163 }
1164 }
1165
1166 static struct _dom_binding *
1167 xid2ypdb(u_int32_t xid)
1168 {
1169 struct _dom_binding *ypdb;
1170
1171 for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
1172 if (ypdb->dom_xid == xid)
1173 break;
1174 return (ypdb);
1175 }
1176
1177 static u_int32_t
1178 unique_xid(struct _dom_binding *ypdb)
1179 {
1180 u_int32_t tmp_xid;
1181
1182 tmp_xid = (u_int32_t)(((u_long)ypdb) & 0xffffffff);
1183 while (xid2ypdb(tmp_xid) != NULL)
1184 tmp_xid++;
1185
1186 return tmp_xid;
1187 }
1188