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