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