1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 29 /* All Rights Reserved */ 30 31 /* 32 * Portions of this source code were derived from Berkeley 33 * under license from the Regents of the University of 34 * California. 35 */ 36 37 #pragma ident "%Z%%M% %I% %E% SMI" 38 39 #include "mt.h" 40 #include "../rpc/rpc_mt.h" 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <sys/types.h> 45 #include <sys/stat.h> 46 #include <errno.h> 47 #include <unistd.h> 48 #include <rpc/rpc.h> 49 #include <netconfig.h> 50 #include <netdir.h> 51 #include <syslog.h> 52 #include "yp_b.h" 53 #include <rpcsvc/yp_prot.h> 54 #include <rpcsvc/ypclnt.h> 55 #include <sys/tiuser.h> 56 #include "nsl_stdio_prv.h" 57 58 #define BFSIZE (YPMAXDOMAIN + 32) /* size of binding file */ 59 int __ypipbufsize = 8192; /* size used for clnt_tli_create */ 60 61 /* This should match the one in ypbind.c */ 62 63 extern int getdomainname(char *, int); 64 65 static CLIENT *getclnt(rpcprog_t, rpcvers_t, struct netconfig *, int *); 66 static struct dom_binding *load_dom_binding(struct ypbind_resp *, char *, 67 int *); 68 static ypbind_resp *get_cached_domain(char *); 69 static int get_cached_transport(struct netconfig *, int, char *, int); 70 static int ypbind_running(int, int); 71 static void set_rdev(struct dom_binding *); 72 static int check_rdev(struct dom_binding *); 73 74 static char nullstring[] = ""; 75 /* 76 * Time parameters when talking to the ypbind and pmap processes 77 */ 78 79 #define YPSLEEPTIME 5 /* Time to sleep between tries */ 80 unsigned int _ypsleeptime = YPSLEEPTIME; 81 82 /* 83 * Time parameters when talking to the ypserv process 84 */ 85 86 #ifdef DEBUG 87 #define YPTIMEOUT 120 /* Total seconds for timeout */ 88 #define YPINTER_TRY 60 /* Seconds between tries */ 89 #else 90 #define YPTIMEOUT 20 /* Total seconds for timeout */ 91 #define YPINTER_TRY 5 /* Seconds between tries */ 92 #endif 93 94 #define MAX_TRIES_FOR_NEW_YP 1 /* Number of times we'll try to */ 95 /* get a new YP server before */ 96 /* we'll settle for an old one. */ 97 struct timeval _ypserv_timeout = { 98 YPTIMEOUT, /* Seconds */ 99 0 /* Microseconds */ 100 }; 101 102 static mutex_t default_domain_lock = DEFAULTMUTEX; 103 static char *default_domain; 104 105 /* 106 * The bound_domains_lock serializes all action in yp_unbind(), __yp_dobind(), 107 * newborn(), check_binding() and laod_dom_binding(), not just the direct 108 * manipulation of the bound_domains list. 109 * It also protects all of the fields within a domain binding except 110 * the server_name field (which is protected by the server_name_lock). 111 * A better implementation might try to serialize each domain separately, 112 * but normally we're only dealing with one domain (the default) anyway. 113 * To avoid one thread freeing a domain binding while another is using 114 * the binding, we maintain a reference count for each binding. The 115 * reference count is incremented in __yp_dobind. The thread calls 116 * __yp_rel_binding() when it has finished using the binding (which 117 * decrements the reference count). If the reference count is non-zero 118 * when a thread tries to free a binding, the need_free flag is set and 119 * the free is delayed. The __yp_rel_binding() routine checks the flag 120 * and calls the free routine if the flag is set and the reference 121 * count is zero. 122 */ 123 static mutex_t bound_domains_lock = DEFAULTMUTEX; 124 static struct dom_binding *bound_domains; /* List of bound domains */ 125 126 127 /* 128 * Must be called with bound_domains_lock held or with a dom_binding 129 * that cannot be referenced by another thread. 130 */ 131 void 132 free_dom_binding(struct dom_binding *p) 133 { 134 if (p->ref_count != 0) { 135 p->need_free = 1; 136 return; 137 } 138 (void) check_rdev(p); 139 clnt_destroy(p->dom_client); 140 free(p->dom_domain); 141 free(p); 142 } 143 144 /* 145 * Attempts to find a dom_binding in the list at bound_domains having the 146 * domain name field equal to the passed domain name, and removes it if found. 147 * The domain-server binding will not exist after the call to this function. 148 * All resources associated with the binding will be freed. 149 * 150 * yp_unbind is MT-safe because it serializes on bound_domains_lock. 151 */ 152 153 static void 154 __yp_unbind_nolock(char *domain) 155 { 156 struct dom_binding *p; 157 struct dom_binding **prev; 158 159 if ((domain == NULL) || (strlen(domain) == 0)) { 160 return; 161 } 162 163 /* 164 * If we used a cache file to bind, then we will mark the 165 * cache bad. This will cause a subsequent call to __yp_dobind 166 * to ignore the cache and talk to ypbind. Otherwise, we 167 * have already gotten a binding by talking to ypbind and 168 * the binding is not good. 169 * 170 * An optimization could be to check to see if the cache 171 * file has changed (ypbind is pointing at a new server) and 172 * reload the binding from it. But that is too much work 173 * for now. 174 */ 175 for (prev = &bound_domains; (p = *prev) != 0; prev = &p->dom_pnext) { 176 177 if (strcmp(domain, p->dom_domain) == 0) { 178 if (!p->cache_bad) { 179 p->cache_bad = 1; 180 break; 181 } 182 *prev = p->dom_pnext; 183 free_dom_binding(p); 184 break; 185 } 186 187 } 188 } 189 190 191 void 192 yp_unbind(char *domain) 193 { 194 (void) mutex_lock(&bound_domains_lock); 195 __yp_unbind_nolock(domain); 196 (void) mutex_unlock(&bound_domains_lock); 197 } 198 199 200 /* 201 * This checks to see if this is a new process incarnation which has 202 * inherited bindings from a parent, and unbinds the world if so. 203 * 204 * MT-safe because it is only invoked from __yp_dobind(), which serializes 205 * all requests. 206 */ 207 static void 208 newborn(void) 209 { 210 static pid_t mypid; /* Cached to detect forks */ 211 pid_t testpid; 212 struct dom_binding *p, *q; 213 214 if ((testpid = getpid()) != mypid) { 215 216 mypid = testpid; 217 218 for (p = bound_domains; p != 0; p = q) { 219 q = p->dom_pnext; 220 free_dom_binding(p); 221 } 222 bound_domains = 0; 223 } 224 } 225 226 /* 227 * This checks that the socket for a domain which has already been bound 228 * hasn't been closed or changed under us. If it has, unbind the domain 229 * without closing the socket, which may be in use by some higher level 230 * code. This returns TRUE and points the binding parameter at the found 231 * dom_binding if the binding is found and the socket looks OK, and FALSE 232 * otherwise. 233 * 234 * MT-safe because it is only invoked from __yp_dobind(), which serializes 235 * all requests. 236 */ 237 static bool 238 check_binding(char *domain, struct dom_binding **binding) 239 { 240 struct dom_binding *pdomb; 241 struct ypbind_resp *ypbind_resp; 242 int status; 243 244 for (pdomb = bound_domains; pdomb != NULL; pdomb = pdomb->dom_pnext) { 245 246 if (strcmp(domain, pdomb->dom_domain) == 0) { 247 /* 248 * XXX How do we really make sure the udp connection hasn't 249 * changes under us ? If it happens and we can't detect it, 250 * the appliction is doomed ! 251 * POLICY: Let nobody do a yp_bind or __yp_dobind explicitly 252 * and forget to to yp_unbind it. All apps should go 253 * through the standard yp_match/first etc. functions. 254 */ 255 256 *binding = pdomb; 257 return (TRUE); 258 } 259 } 260 261 /* 262 * We check to see if we can do a quick bind to ypserv. 263 * If we can, then we load the binding (i.e., add it to our 264 * cache of bindings) and then return it. 265 */ 266 if ((ypbind_resp = get_cached_domain(domain)) != 0) { 267 pdomb = load_dom_binding(ypbind_resp, domain, &status); 268 if (pdomb == 0) 269 return (FALSE); 270 *binding = pdomb; 271 return (TRUE); 272 } 273 return (FALSE); 274 } 275 276 /* 277 * This routine adds a binding for a particular server to our 278 * list of bound domains. We check to see if there is actually 279 * a yp server at the given address. If not, or if there is 280 * any other error, we return 0. We have to malloc the binding 281 * structure because that is what a call to ypbind returns and 282 * we are basically doing what a call to ypbind would do. 283 */ 284 285 #define SOCKADDR_SIZE (sizeof (struct sockaddr_in6)) 286 static int 287 __yp_add_binding_netid(char *domain, char *addr, char *netid) 288 { 289 struct netconfig *nconf = 0; 290 struct netbuf *svcaddr = 0; 291 struct ypbind_binding *binding = 0; 292 int status; 293 struct ypbind_resp resp; 294 struct dom_binding *pdomb; 295 296 nconf = getnetconfigent(netid); 297 if (nconf == 0) 298 goto err; 299 300 svcaddr = malloc(sizeof (struct netbuf)); 301 if (svcaddr == 0) 302 goto err; 303 304 svcaddr->maxlen = SOCKADDR_SIZE; 305 svcaddr->buf = malloc(SOCKADDR_SIZE); 306 if (svcaddr->buf == 0) 307 goto err; 308 309 if (!rpcb_getaddr(YPPROG, YPVERS, nconf, svcaddr, addr)) 310 goto err; 311 312 binding = malloc(sizeof (struct ypbind_binding)); 313 if (binding == 0) 314 goto err; 315 316 binding->ypbind_hi_vers = YPVERS; 317 binding->ypbind_lo_vers = YPVERS; 318 binding->ypbind_nconf = nconf; 319 binding->ypbind_svcaddr = svcaddr; 320 binding->ypbind_servername = (char *)strdup(addr); 321 if (binding->ypbind_servername == 0) 322 goto err; 323 324 resp.ypbind_status = YPBIND_SUCC_VAL; 325 resp.ypbind_resp_u.ypbind_bindinfo = binding; 326 327 (void) mutex_lock(&bound_domains_lock); 328 newborn(); 329 pdomb = load_dom_binding(&resp, domain, &status); 330 (void) mutex_unlock(&bound_domains_lock); 331 332 return (pdomb != 0); 333 334 err: 335 if (nconf) 336 freenetconfigent(nconf); 337 if (svcaddr) { 338 if (svcaddr->buf) 339 free(svcaddr->buf); 340 free(svcaddr); 341 } 342 if (binding) { 343 if (binding->ypbind_servername) 344 free(binding->ypbind_servername); 345 free(binding); 346 } 347 return (0); 348 } 349 350 351 int 352 __yp_add_binding(char *domain, char *addr) { 353 354 int ret = __yp_add_binding_netid(domain, addr, "udp6"); 355 356 if (ret == 0) 357 ret = __yp_add_binding_netid(domain, addr, "udp"); 358 359 return (ret); 360 } 361 362 363 /* 364 * This allocates some memory for a domain binding, initialize it, and 365 * returns a pointer to it. Based on the program version we ended up 366 * talking to ypbind with, fill out an opvector of appropriate protocol 367 * modules. 368 * 369 * MT-safe because it is only invoked from __yp_dobind(), which serializes 370 * all requests. 371 */ 372 static struct dom_binding * 373 load_dom_binding(struct ypbind_resp *ypbind_res, char *domain, int *err) 374 { 375 int fd; 376 struct dom_binding *pdomb; 377 378 pdomb = NULL; 379 380 if ((pdomb = malloc(sizeof (struct dom_binding))) == NULL) { 381 syslog(LOG_ERR, "load_dom_binding: malloc failure."); 382 *err = YPERR_RESRC; 383 return (NULL); 384 } 385 386 pdomb->dom_binding = ypbind_res->ypbind_resp_u.ypbind_bindinfo; 387 /* 388 * Open up a path to the server, which will remain active globally. 389 */ 390 pdomb->dom_client = clnt_tli_create(RPC_ANYFD, 391 pdomb->dom_binding->ypbind_nconf, 392 pdomb->dom_binding->ypbind_svcaddr, 393 YPPROG, YPVERS, __ypipbufsize, 394 __ypipbufsize); 395 if (pdomb->dom_client == NULL) { 396 clnt_pcreateerror("yp_bind: clnt_tli_create"); 397 free(pdomb); 398 *err = YPERR_RPC; 399 return (NULL); 400 } 401 #ifdef DEBUG 402 (void) printf("yp_bind: clnt_tli_create suceeded\n"); 403 #endif 404 405 pdomb->dom_pnext = bound_domains; /* Link this to the list as */ 406 pdomb->dom_domain = malloc(strlen(domain) + (unsigned)1); 407 if (pdomb->dom_domain == NULL) { 408 clnt_destroy(pdomb->dom_client); 409 free(pdomb); 410 *err = YPERR_RESRC; 411 return (NULL); 412 } 413 /* 414 * We may not have loaded from a cache file, but we assume the 415 * cache is good until we find out otherwise. 416 */ 417 pdomb->cache_bad = 0; 418 set_rdev(pdomb); 419 if (clnt_control(pdomb->dom_client, CLGET_FD, (char *)&fd)) 420 (void) fcntl(fd, F_SETFD, 1); /* make it "close on exec" */ 421 422 (void) strcpy(pdomb->dom_domain, domain); /* Remember the domain name */ 423 pdomb->ref_count = 0; 424 pdomb->need_free = 0; 425 (void) mutex_init(&pdomb->server_name_lock, USYNC_THREAD, 0); 426 bound_domains = pdomb; /* ... the head entry */ 427 return (pdomb); 428 } 429 430 /* 431 * XXX special code for handling C2 (passwd.adjunct) lookups when we need 432 * a reserved port. 433 */ 434 static int 435 tli_open_rsvdport(struct netconfig *nconf) 436 { 437 int fd; 438 439 if (nconf == NULL) 440 return (-1); 441 442 fd = t_open(nconf->nc_device, O_RDWR, NULL); 443 if (fd == -1) 444 return (-1); 445 446 if (netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL) == -1) { 447 if (t_bind(fd, NULL, NULL) == -1) { 448 (void) t_close(fd); 449 return (-1); 450 } 451 } 452 return (fd); 453 } 454 455 /* 456 * This allocates some memory for a domain binding, initialize it, and 457 * returns a pointer to it. Based on the program version we ended up 458 * talking to ypbind with, fill out an opvector of appropriate protocol 459 * modules. 460 * 461 * MT-safe because it is only invoked from __yp_dobind(), which serializes 462 * all requests. 463 * 464 * XXX special version for handling C2 (passwd.adjunct) lookups when we need 465 * a reserved port. 466 * 467 * Note that the binding is not cached. The caller has to free the binding 468 * using free_dom_binding(). 469 */ 470 static struct dom_binding * 471 load_dom_binding_rsvdport(struct ypbind_binding *dom_binding, char *domain, 472 int *err) 473 { 474 struct dom_binding *pdomb; 475 int fd; 476 477 pdomb = NULL; 478 479 if ((pdomb = malloc(sizeof (struct dom_binding))) == NULL) { 480 syslog(LOG_ERR, "load_dom_binding_rsvdport: malloc failure."); 481 *err = YPERR_RESRC; 482 return (NULL); 483 } 484 485 pdomb->dom_binding = dom_binding; 486 /* 487 * Open up a path to the server, which will remain active globally. 488 */ 489 fd = tli_open_rsvdport(pdomb->dom_binding->ypbind_nconf); 490 if (fd < 0) { 491 clnt_pcreateerror("yp_bind: tli_open_rsvdport"); 492 free(pdomb); 493 *err = YPERR_RPC; 494 return (NULL); 495 } 496 pdomb->dom_client = clnt_tli_create(fd, 497 pdomb->dom_binding->ypbind_nconf, 498 pdomb->dom_binding->ypbind_svcaddr, 499 YPPROG, YPVERS, __ypipbufsize, 500 __ypipbufsize); 501 if (pdomb->dom_client == NULL) { 502 clnt_pcreateerror("yp_bind: clnt_tli_create"); 503 free(pdomb); 504 *err = YPERR_RPC; 505 return (NULL); 506 } 507 #ifdef DEBUG 508 (void) printf("yp_bind: clnt_tli_create suceeded\n"); 509 #endif 510 (void) CLNT_CONTROL(pdomb->dom_client, CLSET_FD_CLOSE, NULL); 511 512 pdomb->dom_domain = malloc(strlen(domain) + (unsigned)1); 513 if (pdomb->dom_domain == NULL) { 514 clnt_destroy(pdomb->dom_client); 515 free(pdomb); 516 *err = YPERR_RESRC; 517 return (NULL); 518 } 519 520 (void) strcpy(pdomb->dom_domain, domain); /* Remember the domain name */ 521 pdomb->ref_count = 0; 522 pdomb->need_free = 0; 523 set_rdev(pdomb); 524 (void) mutex_init(&pdomb->server_name_lock, USYNC_THREAD, 0); 525 return (pdomb); 526 } 527 528 /* 529 * Attempts to locate a yellow pages server that serves a passed domain. If 530 * one is found, an entry is created on the static list of domain-server pairs 531 * pointed to by cell bound_domains, a udp path to the server is created and 532 * the function returns 0. Otherwise, the function returns a defined errorcode 533 * YPERR_xxxx. 534 * 535 * MT-safe because it serializes on bound_domains_lock. 536 * 537 * If hardlookup is set then loop forever until success, else try 4 538 * times (each try is relatively short) max. 539 */ 540 int 541 __yp_dobind_cflookup( 542 char *domain, 543 struct dom_binding **binding, /* if result==0, ptr to dom_binding */ 544 int hardlookup) 545 546 { 547 struct dom_binding *pdomb; /* Ptr to new domain binding */ 548 struct ypbind_resp *ypbind_resp; /* Response from local ypbinder */ 549 struct ypbind_domain ypbd; 550 int status, err = YPERR_DOMAIN; 551 int tries = 4; /* if not hardlookup, try 4 times max to bind */ 552 int first_try = 1; 553 CLIENT *tb = NULL; 554 555 if ((domain == NULL) ||(strlen(domain) == 0)) 556 return (YPERR_BADARGS); 557 558 (void) mutex_lock(&bound_domains_lock); 559 /* 560 * ===> 561 * If someone managed to fork() while we were holding this lock, 562 * we'll probably end up hanging on the lock. Tant pis. 563 */ 564 newborn(); 565 566 if (check_binding(domain, binding)) { 567 /* 568 * If the cache is okay and if the underlying file 569 * descriptor is okay (application did not close it). 570 * then use the binding. 571 */ 572 if (!(*binding)->cache_bad && check_rdev(*binding)) { 573 (*binding)->ref_count += 1; 574 (void) mutex_unlock(&bound_domains_lock); 575 return (0); /* We are bound */ 576 } 577 578 /* 579 * If we get here, one of two things happened: the 580 * cache is bad, or the underlying file descriptor 581 * had changed. 582 * 583 * If the cache is bad, then we call yp_unbind to remove 584 * the binding. 585 * 586 * If the file descriptor has changed, then we call 587 * yp_unbind to remove the binding (we set cache_bad 588 * to force yp_unbind to do the remove), and then 589 * call check_binding to reload the binding from the 590 * cache again. 591 */ 592 if ((*binding)->cache_bad) { 593 __yp_unbind_nolock(domain); 594 } else { 595 (*binding)->cache_bad = 1; 596 (void) mutex_unlock(&bound_domains_lock); 597 yp_unbind(domain); 598 (void) mutex_lock(&bound_domains_lock); 599 if (check_binding(domain, binding)) { 600 (*binding)->ref_count += 1; 601 (void) mutex_unlock(&bound_domains_lock); 602 return (0); 603 } 604 } 605 } 606 607 while (hardlookup ? 1 : tries--) { 608 if (first_try) 609 first_try = 0; 610 else { 611 /* 612 * ===> sleep() -- Ugh. And with the lock held, too. 613 */ 614 (void) sleep(_ypsleeptime); 615 } 616 tb = __clnt_create_loopback(YPBINDPROG, YPBINDVERS, &err); 617 if (tb == NULL) { 618 if (ypbind_running(err, rpc_createerr.cf_stat)) 619 continue; 620 break; 621 } 622 ypbd.ypbind_domainname = domain; 623 ypbd.ypbind_vers = YPVERS; 624 /* 625 * The interface to ypbindproc_domain_3 is MT-unsafe, but we're 626 * OK as long as we're the only ones who call it and we 627 * serialize all requests (for all domains). Otherwise, 628 * change the interface (pass in the ypbind_resp struct). 629 */ 630 ypbind_resp = ypbindproc_domain_3(&ypbd, tb); 631 /* 632 * Although we talk to ypbind on loopback, 633 * it gives us a udp address for the ypserv. 634 */ 635 if (ypbind_resp == NULL) { 636 /* lost ypbind? */ 637 clnt_perror(tb, 638 "ypbindproc_domain_3: can't contact ypbind"); 639 clnt_destroy(tb); 640 tb = NULL; 641 continue; 642 } 643 if (ypbind_resp->ypbind_status == YPBIND_SUCC_VAL) { 644 /* 645 * Local ypbind has let us in on the ypserv's address, 646 * go get in touch with it ! 647 */ 648 pdomb = load_dom_binding(ypbind_resp, domain, &status); 649 if (pdomb == 0) { 650 err = status; 651 clnt_destroy(tb); 652 tb = NULL; 653 continue; 654 } 655 clnt_destroy(tb); 656 pdomb->ref_count += 1; 657 (void) mutex_unlock(&bound_domains_lock); 658 *binding = pdomb; /* Return ptr to the binding entry */ 659 return (0); /* This is the go path */ 660 } 661 if (ypbind_resp->ypbind_resp_u.ypbind_error == 662 YPBIND_ERR_NOSERV) 663 err = YPERR_DOMAIN; 664 else 665 err = YPERR_YPBIND; 666 clnt_destroy(tb); 667 tb = NULL; 668 } 669 if (tb != NULL) 670 clnt_destroy(tb); 671 (void) mutex_unlock(&bound_domains_lock); 672 if (err) 673 return (err); 674 return (YPERR_DOMAIN); 675 } 676 677 int 678 __yp_dobind( 679 char *domain, 680 struct dom_binding **binding) /* if result == 0, ptr to dom_binding */ 681 { 682 /* traditional __yp_dobind loops forever so set hardlookup */ 683 return (__yp_dobind_cflookup(domain, binding, 1)); 684 } 685 686 void 687 __yp_rel_binding(struct dom_binding *binding) 688 { 689 (void) mutex_lock(&bound_domains_lock); 690 binding->ref_count -= 1; 691 if (binding->need_free && binding->ref_count == 0) 692 free_dom_binding(binding); 693 (void) mutex_unlock(&bound_domains_lock); 694 } 695 696 /* 697 * Attempts to locate a yellow pages server that serves a passed domain. If 698 * one is found, an entry is created on the static list of domain-server pairs 699 * pointed to by cell bound_domains, a udp path to the server is created and 700 * the function returns 0. Otherwise, the function returns a defined errorcode 701 * YPERR_xxxx. 702 * 703 * MT-safe because it serializes on bound_domains_lock. 704 * 705 * XXX special version for handling C2 (passwd.adjunct) lookups when we need 706 * a reserved port. 707 * This returns an uncached binding which the caller has to free using 708 * free_dom_binding(). 709 */ 710 int 711 __yp_dobind_rsvdport_cflookup( 712 char *domain, 713 struct dom_binding **binding, /* if result==0, ptr to dom_binding */ 714 int hardlookup) 715 { 716 struct dom_binding *pdomb; /* Ptr to new domain binding */ 717 struct ypbind_resp *ypbind_resp; /* Response from local ypbinder */ 718 struct ypbind_domain ypbd; 719 int status, err = YPERR_DOMAIN; 720 int tries = 4; /* if not hardlookup, try a few times to bind */ 721 int first_try = 1; 722 CLIENT *tb = NULL; 723 724 if ((domain == NULL) ||(strlen(domain) == 0)) 725 return (YPERR_BADARGS); 726 727 (void) mutex_lock(&bound_domains_lock); 728 /* 729 * ===> 730 * If someone managed to fork() while we were holding this lock, 731 * we'll probably end up hanging on the lock. Tant pis. 732 */ 733 newborn(); 734 735 /* 736 * Check for existing bindings and use the information in the binding 737 * to create a transport endpoint with a reserved port. 738 */ 739 if (check_binding(domain, binding)) { 740 /* 741 * If the cache is bad, yp_unbind() the entry again and then 742 * talk to ypbind. 743 */ 744 if ((*binding)->cache_bad) { 745 __yp_unbind_nolock(domain); 746 } else { 747 pdomb = load_dom_binding_rsvdport( 748 (*binding)->dom_binding, 749 domain, &status); 750 if (pdomb == 0) { 751 (void) mutex_unlock(&bound_domains_lock); 752 return (status); 753 } 754 pdomb->ref_count += 1; 755 (void) mutex_unlock(&bound_domains_lock); 756 *binding = pdomb; /* Return ptr to the binding entry */ 757 return (0); 758 } 759 } 760 761 while (hardlookup ? 1 : tries--) { 762 if (first_try) 763 first_try = 0; 764 else { 765 /* 766 * ===> sleep() -- Ugh. And with the lock held, too. 767 */ 768 (void) sleep(_ypsleeptime*tries); 769 } 770 tb = __clnt_create_loopback(YPBINDPROG, YPBINDVERS, &err); 771 if (tb == NULL) { 772 if (ypbind_running(err, rpc_createerr.cf_stat)) 773 continue; 774 break; 775 } 776 ypbd.ypbind_domainname = domain; 777 ypbd.ypbind_vers = YPVERS; 778 /* 779 * The interface to ypbindproc_domain_3 is MT-unsafe, but we're 780 * OK as long as we're the only ones who call it and we 781 * serialize all requests (for all domains). Otherwise, 782 * change the interface (pass in the ypbind_resp struct). 783 */ 784 ypbind_resp = ypbindproc_domain_3(&ypbd, tb); 785 /* 786 * Although we talk to ypbind on loopback, 787 * it gives us a udp address for the ypserv. 788 */ 789 if (ypbind_resp == NULL) { 790 /* lost ypbind? */ 791 clnt_perror(tb, 792 "ypbindproc_domain_3: can't contact ypbind"); 793 clnt_destroy(tb); 794 tb = NULL; 795 continue; 796 } 797 if (ypbind_resp->ypbind_status == YPBIND_SUCC_VAL) { 798 /* 799 * Local ypbind has let us in on the ypserv's address, 800 * go get in touch with it ! 801 */ 802 pdomb = load_dom_binding_rsvdport( 803 ypbind_resp->ypbind_resp_u.ypbind_bindinfo, 804 domain, &status); 805 if (pdomb == 0) { 806 err = status; 807 clnt_destroy(tb); 808 tb = NULL; 809 continue; 810 } 811 clnt_destroy(tb); 812 pdomb->ref_count += 1; 813 (void) mutex_unlock(&bound_domains_lock); 814 *binding = pdomb; /* Return ptr to the binding entry */ 815 return (0); /* This is the go path */ 816 } 817 if (ypbind_resp->ypbind_resp_u.ypbind_error == 818 YPBIND_ERR_NOSERV) 819 err = YPERR_DOMAIN; 820 else 821 err = YPERR_YPBIND; 822 clnt_destroy(tb); 823 tb = NULL; 824 } 825 if (tb != NULL) 826 clnt_destroy(tb); 827 (void) mutex_unlock(&bound_domains_lock); 828 if (err) 829 return (err); 830 return (YPERR_DOMAIN); 831 } 832 833 int 834 __yp_dobind_rsvdport( 835 char *domain, 836 struct dom_binding **binding) /* if result==0, ptr to dom_binding */ 837 { 838 /* traditional __yp_dobind_rsvdport loops forever so set hardlookup */ 839 return (__yp_dobind_rsvdport_cflookup(domain, binding, 1)); 840 } 841 842 /* 843 * This is a "wrapper" function for __yp_dobind for vanilla user-level 844 * functions which neither know nor care about struct dom_bindings. 845 */ 846 int 847 yp_bind(char *domain) 848 { 849 850 struct dom_binding *binding; 851 int res; 852 853 res = __yp_dobind(domain, &binding); 854 if (res == 0) 855 __yp_rel_binding(binding); 856 return (res); 857 } 858 859 static char * 860 __default_domain(void) 861 { 862 char temp[256]; 863 864 (void) mutex_lock(&default_domain_lock); 865 866 if (default_domain) { 867 (void) mutex_unlock(&default_domain_lock); 868 return (default_domain); 869 } 870 if (getdomainname(temp, sizeof (temp)) < 0) { 871 (void) mutex_unlock(&default_domain_lock); 872 return (0); 873 } 874 if (strlen(temp) > 0) { 875 default_domain = malloc((strlen(temp) + 1)); 876 if (default_domain == 0) { 877 (void) mutex_unlock(&default_domain_lock); 878 return (0); 879 } 880 (void) strcpy(default_domain, temp); 881 (void) mutex_unlock(&default_domain_lock); 882 return (default_domain); 883 } 884 (void) mutex_unlock(&default_domain_lock); 885 return (0); 886 } 887 888 /* 889 * This is a wrapper for the system call getdomainname which returns a 890 * ypclnt.h error code in the failure case. It also checks to see that 891 * the domain name is non-null, knowing that the null string is going to 892 * get rejected elsewhere in the yp client package. 893 */ 894 int 895 yp_get_default_domain(char **domain) 896 { 897 if ((*domain = __default_domain()) != 0) 898 return (0); 899 return (YPERR_YPERR); 900 } 901 902 /* 903 * ===> Nobody uses this, do they? Can we nuke it? 904 */ 905 int 906 usingypmap(char **ddn, char *map) 907 { 908 char in, *outval = NULL; 909 int outvallen, stat; 910 char *domain; 911 912 if ((domain = __default_domain()) == 0) 913 return (FALSE); 914 *ddn = domain; 915 /* does the map exist ? */ 916 in = (char)0xff; 917 stat = yp_match(domain, map, &in, 1, &outval, &outvallen); 918 if (outval != NULL) 919 free(outval); 920 switch (stat) { 921 922 case 0: /* it actually succeeded! */ 923 case YPERR_KEY: /* no such key in map */ 924 case YPERR_NOMORE: 925 case YPERR_BUSY: 926 return (TRUE); 927 } 928 return (FALSE); 929 } 930 931 /* 932 * Creates a quick connection on a connection oriented loopback 933 * transport. Fails quickly without timeout. Only naming service 934 * it goes to is straddr.so. 935 */ 936 CLIENT * 937 __clnt_create_loopback(rpcprog_t prog, rpcvers_t vers, int *err) 938 { 939 struct netconfig *nconf; 940 CLIENT *clnt = NULL; 941 void *nc_handle; /* Net config handle */ 942 943 *err = 0; 944 nc_handle = setnetconfig(); 945 if (nc_handle == NULL) { 946 /* fails to open netconfig file */ 947 rpc_createerr.cf_stat = RPC_FAILED; 948 *err = YPERR_RPC; 949 return (NULL); 950 } 951 while (nconf = getnetconfig(nc_handle)) 952 /* Try only one connection oriented loopback transport */ 953 if ((strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) && 954 ((nconf->nc_semantics == NC_TPI_COTS) || 955 (nconf->nc_semantics == NC_TPI_COTS_ORD))) { 956 clnt = getclnt(prog, vers, nconf, err); 957 break; 958 } 959 (void) endnetconfig(nc_handle); 960 961 if (clnt == NULL) { /* no loopback transport available */ 962 if (rpc_createerr.cf_stat == 0) 963 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 964 if (*err == 0) *err = YPERR_RPC; 965 } 966 return (clnt); 967 } 968 969 static CLIENT * 970 getclnt(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, int *err) 971 { 972 int fd; 973 struct netbuf *svcaddr; /* servers address */ 974 CLIENT *cl; 975 struct nd_addrlist *nas; 976 struct nd_hostserv rpcbind_hs; 977 struct t_call sndcall; 978 char uaddress[1024]; /* XXX maxlen ?? */ 979 RPCB parms; 980 enum clnt_stat clnt_st; 981 char *ua; 982 struct timeval tv = { 30, 0 }; 983 984 if (nconf == NULL) { 985 rpc_createerr.cf_stat = RPC_TLIERROR; 986 *err = YPERR_RPC; 987 return (NULL); 988 } 989 990 /* 991 * The ypbind process might cache its transport address. 992 * If we can get at it, then we will use it and avoid 993 * wasting time talking to rpcbind. 994 */ 995 996 if (get_cached_transport(nconf, vers, uaddress, sizeof (uaddress))) { 997 goto create_client; 998 } 999 1000 /* 1001 * Check to see if local rpcbind is up or not. If it 1002 * isn't, it is best that the application should realize 1003 * yp is not up and take a remedial action. This is to 1004 * avoid the minute long timeout incurred by rpcbind_getaddr. 1005 * Looks like the only way to accomplish this it is to unfold 1006 * rpcb_getaddr and make a few changes. Alas ! 1007 */ 1008 rpcbind_hs.h_host = HOST_SELF_CONNECT; 1009 rpcbind_hs.h_serv = "rpcbind"; 1010 if (netdir_getbyname(nconf, &rpcbind_hs, &nas) != ND_OK) { 1011 rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; 1012 *err = YPERR_RPC; 1013 return (NULL); 1014 } 1015 if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) == -1) { 1016 rpc_createerr.cf_stat = RPC_TLIERROR; 1017 *err = YPERR_RPC; 1018 return (NULL); 1019 } 1020 if (t_bind(fd, NULL, NULL) == -1) { 1021 rpc_createerr.cf_stat = RPC_TLIERROR; 1022 *err = YPERR_RPC; 1023 (void) t_close(fd); 1024 return (NULL); 1025 } 1026 sndcall.addr = *(nas->n_addrs); 1027 sndcall.opt.len = 0; 1028 sndcall.udata.len = 0; 1029 if (t_connect(fd, &sndcall, NULL) == -1) { 1030 netdir_free((char *)nas, ND_ADDRLIST); 1031 rpc_createerr.cf_stat = RPC_TLIERROR; 1032 (void) t_close(fd); 1033 *err = YPERR_PMAP; 1034 return (NULL); 1035 } 1036 1037 /* 1038 * Get the address of the server 1039 */ 1040 cl = clnt_tli_create(fd, nconf, nas->n_addrs, 1041 RPCBPROG, RPCBVERS, __ypipbufsize, __ypipbufsize); 1042 netdir_free((char *)nas, ND_ADDRLIST); 1043 if (cl == NULL) { 1044 (void) t_close(fd); 1045 *err = YPERR_PMAP; 1046 return (NULL); 1047 } 1048 parms.r_prog = prog; 1049 parms.r_vers = vers; 1050 parms.r_netid = nconf->nc_netid; 1051 parms.r_addr = nullstring; 1052 parms.r_owner = nullstring; 1053 ua = uaddress; 1054 clnt_st = CLNT_CALL(cl, RPCBPROC_GETADDR, xdr_rpcb, (char *)&parms, 1055 xdr_wrapstring, (char *)&ua, tv); 1056 (void) t_close(fd); 1057 clnt_destroy(cl); 1058 if (clnt_st != RPC_SUCCESS) { 1059 *err = YPERR_YPBIND; 1060 return (NULL); 1061 } 1062 if (strlen(uaddress) == 0) { 1063 *err = YPERR_YPBIND; 1064 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; 1065 return (NULL); 1066 } 1067 1068 create_client: 1069 svcaddr = uaddr2taddr(nconf, uaddress); 1070 cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr, prog, vers, 1071 __ypipbufsize, __ypipbufsize); 1072 netdir_free((char *)svcaddr, ND_ADDR); 1073 if (cl == NULL) { 1074 *err = YPERR_YPBIND; 1075 return (NULL); 1076 } 1077 /* 1078 * The fd should be closed while destroying the handle. 1079 */ 1080 return (cl); 1081 } 1082 1083 static int 1084 get_cached_transport(struct netconfig *nconf, int vers, char *uaddress, 1085 int ulen) 1086 { 1087 ssize_t st; 1088 int fd; 1089 1090 (void) snprintf(uaddress, ulen, 1091 "%s/xprt.%s.%d", BINDING, nconf->nc_netid, vers); 1092 fd = open(uaddress, O_RDONLY); 1093 if (fd == -1) 1094 return (0); 1095 1096 /* if first byte is not locked, then ypbind must not be running */ 1097 st = lockf(fd, F_TEST, 1); 1098 if (st != -1 || (errno != EAGAIN && errno != EACCES)) { 1099 (void) close(fd); 1100 return (0); 1101 } 1102 1103 st = read(fd, uaddress, ulen); 1104 if (st == -1) { 1105 (void) close(fd); 1106 return (0); 1107 } 1108 1109 (void) close(fd); 1110 return (1); 1111 } 1112 1113 static ypbind_resp * 1114 get_cached_domain(char *domain) 1115 { 1116 __NSL_FILE *fp; 1117 int st; 1118 char filename[300]; 1119 static ypbind_resp res; 1120 XDR xdrs; 1121 1122 (void) snprintf(filename, sizeof (filename), 1123 "%s/%s/cache_binding", BINDING, domain); 1124 fp = __nsl_fopen(filename, "r"); 1125 if (fp == 0) 1126 return (0); 1127 1128 /* if first byte is not locked, then ypbind must not be running */ 1129 st = lockf(__nsl_fileno(fp), F_TEST, 1); 1130 if (st != -1 || (errno != EAGAIN && errno != EACCES)) { 1131 (void) __nsl_fclose(fp); 1132 return (0); 1133 } 1134 1135 __nsl_xdrstdio_create(&xdrs, fp, XDR_DECODE); 1136 1137 (void) memset((char *)&res, 0, sizeof (res)); 1138 st = xdr_ypbind_resp(&xdrs, &res); 1139 1140 xdr_destroy(&xdrs); 1141 (void) __nsl_fclose(fp); 1142 1143 if (st) 1144 return (&res); 1145 1146 return (0); 1147 } 1148 1149 static int 1150 ypbind_running(int err, int status) 1151 { 1152 char filename[300]; 1153 int st; 1154 int fd; 1155 1156 (void) snprintf(filename, sizeof (filename), "%s/ypbind.pid", BINDING); 1157 fd = open(filename, O_RDONLY); 1158 if (fd == -1) { 1159 if ((err == YPERR_YPBIND) && (status != RPC_PROGNOTREGISTERED)) 1160 return (1); 1161 return (0); 1162 } 1163 1164 /* if first byte is not locked, then ypbind must not be running */ 1165 st = lockf(fd, F_TEST, 1); 1166 if (st != -1 || (errno != EAGAIN && errno != EACCES)) { 1167 (void) close(fd); 1168 return (0); 1169 } 1170 1171 (void) close(fd); 1172 return (1); 1173 } 1174 1175 static void 1176 set_rdev(struct dom_binding *pdomb) 1177 { 1178 int fd; 1179 struct stat stbuf; 1180 1181 if (clnt_control(pdomb->dom_client, CLGET_FD, (char *)&fd) != TRUE || 1182 fstat(fd, &stbuf) == -1) { 1183 syslog(LOG_DEBUG, "ypbind client: can't get rdev"); 1184 pdomb->fd = -1; 1185 return; 1186 } 1187 pdomb->fd = fd; 1188 pdomb->rdev = stbuf.st_rdev; 1189 } 1190 1191 static int 1192 check_rdev(struct dom_binding *pdomb) 1193 { 1194 struct stat stbuf; 1195 1196 if (pdomb->fd == -1) 1197 return (1); /* can't check it, assume it is okay */ 1198 1199 if (fstat(pdomb->fd, &stbuf) == -1) { 1200 syslog(LOG_DEBUG, "yp_bind client: can't stat %d", pdomb->fd); 1201 /* could be because file descriptor was closed */ 1202 /* it's not our file descriptor, so don't try to close it */ 1203 clnt_control(pdomb->dom_client, CLSET_FD_NCLOSE, NULL); 1204 return (0); 1205 } 1206 if (pdomb->rdev != stbuf.st_rdev) { 1207 syslog(LOG_DEBUG, 1208 "yp_bind client: fd %d changed, old=0x%x, new=0x%x", 1209 pdomb->fd, pdomb->rdev, stbuf.st_rdev); 1210 /* it's not our file descriptor, so don't try to close it */ 1211 clnt_control(pdomb->dom_client, CLSET_FD_NCLOSE, NULL); 1212 return (0); 1213 } 1214 return (1); /* fd is okay */ 1215 } 1216