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