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