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