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