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 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * Portions of this source code were derived from Berkeley 31 * under license from the Regents of the University of 32 * California. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 /* 38 * This is a user command which tells which yp server is being used by a 39 * given machine, or which yp server is the master for a named map. 40 * 41 * Usage is: 42 * ypwhich [-d domain] [-m [mname] [-t] | [-Vn] host] 43 * ypwhich -x 44 * where: the -d switch can be used to specify a domain other than the 45 * default domain. -m tells the master of that map. mname is a mapname 46 * If the -m option is used, ypwhich will act like a vanilla yp client, 47 * and will not attempt to choose a particular yp server. On the 48 * other hand, if no -m switch is used, ypwhich will talk directly to the yp 49 * bind process on the named host, or to the local ypbind process if no host 50 * name is specified. -t switch inhibits nickname translation of map names. 51 * -x is to dump the nickname translation table from file /var/yp/nicknames. 52 * 53 */ 54 55 #include <stdio.h> 56 #include <ctype.h> 57 #include <rpc/rpc.h> 58 #include <rpcsvc/yp_prot.h> 59 #include <rpcsvc/ypclnt.h> 60 #include "yp_b.h" 61 #include "ypv2_bind.h" 62 #include <string.h> 63 #include <netdir.h> 64 #include <unistd.h> 65 #include <netdb.h> 66 #include <arpa/inet.h> 67 #include <inet/ip.h> 68 #include <inet/ip6.h> 69 #include <netinet/ip6.h> 70 #include <sys/utsname.h> 71 72 #define YPSLEEPTIME 5 /* between two tries of bind */ 73 74 #define TIMEOUT 30 /* Total seconds for timeout */ 75 #define INTER_TRY 10 /* Seconds between tries */ 76 77 static int translate = TRUE; 78 static int dodump = FALSE; 79 static char *domain = NULL; 80 static char default_domain_name[YPMAXDOMAIN]; 81 static char *host = NULL; 82 static int vers = YPBINDVERS; 83 static char default_host_name[256]; 84 static bool get_master = FALSE; 85 static bool get_server = FALSE; 86 static char *map = NULL; 87 static char nm[YPMAXMAP+1]; 88 static struct timeval timeout = { 89 TIMEOUT, /* Seconds */ 90 0 /* Microseconds */ 91 }; 92 static char nullstring[] = "\000"; 93 static char err_usage[] = 94 "Usage:\n\ 95 ypwhich [-d domain] [[-t] -m [mname] | [-Vn] host]\n\ 96 ypwhich -x\n\ 97 where\n\ 98 mname may be either a mapname or a nickname for a map.\n\ 99 host if specified, is the machine whose NIS server is to be found.\n\ 100 -t inhibits map nickname translation.\n\ 101 -Vn version of ypbind, V3 is default.\n\ 102 -x dumps the map nickname translation table.\n"; 103 static char err_bad_args[] = 104 "ypwhich: %s argument is bad.\n"; 105 static char err_cant_get_kname[] = 106 "ypwhich: can't get %s back from system call.\n"; 107 static char err_null_kname[] = 108 "ypwhich: the %s hasn't been set on this machine.\n"; 109 static char err_bad_mapname[] = "mapname"; 110 static char err_bad_domainname[] = "domainname"; 111 static char err_bad_hostname[] = "hostname"; 112 113 static void get_command_line_args(); 114 static void getdomain(); 115 static void getlochost(); 116 static void get_server_name(); 117 static int call_binder(); 118 static void get_map_master(); 119 extern void maketable(); 120 extern int getmapname(); 121 #ifdef DEBUG 122 static void dump_response(); 123 #endif 124 static void dump_ypmaps(); 125 static void dumpmaps(); 126 127 static bool xdr_yp_inaddr(); 128 static bool xdr_old_ypbind_resp(); 129 static bool xdr_old_yp_binding(); 130 static int old_call_binder(); 131 static void print_server(); 132 133 /* need these for call to (remote) V2 ypbind */ 134 struct old_ypbind_binding { 135 struct in_addr ypbind_binding_addr; /* In network order */ 136 unsigned short int ypbind_binding_port; /* In network order */ 137 }; 138 139 struct old_ypbind_resp { 140 enum ypbind_resptype ypbind_status; 141 union { 142 unsigned long ypbind_error; 143 struct old_ypbind_binding ypbind_bindinfo; 144 } ypbind_respbody; 145 }; 146 147 /* 148 * This is the main line for the ypwhich process. 149 */ 150 int 151 main(argc, argv) 152 char **argv; 153 { 154 get_command_line_args(argc, argv); 155 156 if (dodump) { 157 maketable(dodump); 158 exit(0); 159 } 160 161 if (!domain) { 162 getdomain(); 163 } 164 165 if (map && translate && (strchr(map, '.') == NULL) && 166 (getmapname(map, nm))) { 167 map = nm; 168 } 169 170 if (get_server) { 171 if (!host) 172 getlochost(); 173 get_server_name(); 174 } else { 175 if (map) 176 get_map_master(); 177 else 178 dump_ypmaps(); 179 } 180 181 return (0); 182 } 183 184 /* 185 * This does the command line argument processing. 186 */ 187 static void 188 get_command_line_args(argc, argv) 189 int argc; 190 char **argv; 191 192 { 193 argv++; 194 195 if (argc == 1) { 196 get_server = TRUE; 197 return; 198 } 199 200 while (--argc) { 201 202 if ((*argv)[0] == '-') { 203 204 switch ((*argv)[1]) { 205 206 case 'V': 207 208 vers = atoi(argv[0]+2); 209 if (vers < 1) { 210 (void) fprintf(stderr, err_usage); 211 exit(1); 212 } 213 argv++; 214 break; 215 216 case 'm': 217 get_master = TRUE; 218 argv++; 219 220 if (argc > 1) { 221 222 if ((*(argv))[0] == '-') { 223 break; 224 } 225 226 argc--; 227 map = *argv; 228 argv++; 229 230 if ((int)strlen(map) > YPMAXMAP) { 231 (void) fprintf(stderr, err_bad_args, 232 err_bad_mapname); 233 exit(1); 234 } 235 236 } 237 238 break; 239 240 case 'd': 241 242 if (argc > 1) { 243 argv++; 244 argc--; 245 domain = *argv; 246 argv++; 247 248 if ((int)strlen(domain) > YPMAXDOMAIN) { 249 (void) fprintf(stderr, err_bad_args, 250 err_bad_domainname); 251 exit(1); 252 } 253 254 } else { 255 (void) fprintf(stderr, err_usage); 256 exit(1); 257 } 258 259 break; 260 261 case 't': 262 translate = FALSE; 263 argv++; 264 break; 265 266 case 'x': 267 dodump = TRUE; 268 argv++; 269 break; 270 271 default: 272 (void) fprintf(stderr, err_usage); 273 exit(1); 274 } 275 276 } else { 277 278 if (get_server) { 279 (void) fprintf(stderr, err_usage); 280 exit(1); 281 } 282 283 get_server = TRUE; 284 host = *argv; 285 argv++; 286 287 if ((int)strlen(host) > 256) { 288 (void) fprintf(stderr, 289 err_bad_args, err_bad_hostname); 290 exit(1); 291 } 292 } 293 } 294 295 if (get_master && get_server) { 296 (void) fprintf(stderr, err_usage); 297 exit(1); 298 } 299 300 if (!get_master && !get_server) { 301 get_server = TRUE; 302 } 303 } 304 305 /* 306 * This gets the local default domainname, and makes sure that it's set 307 * to something reasonable. domain is set here. 308 */ 309 static void 310 getdomain() 311 { 312 if (!getdomainname(default_domain_name, YPMAXDOMAIN)) { 313 domain = default_domain_name; 314 } else { 315 (void) fprintf(stderr, err_cant_get_kname, err_bad_domainname); 316 exit(1); 317 } 318 319 if ((int)strlen(domain) == 0) { 320 (void) fprintf(stderr, err_null_kname, err_bad_domainname); 321 exit(1); 322 } 323 } 324 325 /* 326 * This gets the local hostname back from the kernel 327 */ 328 static void 329 getlochost() 330 { 331 struct utsname utsname; 332 333 if (uname(&utsname) != -1) { 334 strcpy(default_host_name, utsname.nodename); 335 host = default_host_name; 336 } else { 337 (void) fprintf(stderr, err_cant_get_kname, err_bad_hostname); 338 exit(1); 339 } 340 341 } 342 343 /* 344 * This tries to find the name of the server to which the binder in question 345 * is bound. If one of the -Vx flags was specified, it will try only for 346 * that protocol version, otherwise, it will start with the current version, 347 * then drop back to the previous version. 348 */ 349 static void 350 get_server_name() 351 { 352 char *notbound = "Domain %s not bound on %s.\n"; 353 354 if (vers >= 3) { 355 if (!call_binder(vers)) 356 (void) fprintf(stderr, notbound, domain, host); 357 } else { 358 if (!old_call_binder(vers)) 359 (void) fprintf(stderr, notbound, domain, host); 360 } 361 } 362 363 extern CLIENT *__clnt_create_loopback(); 364 365 /* 366 * This sends a message to the ypbind process on the node with 367 * the host name 368 */ 369 static int 370 call_binder(vers) 371 int vers; 372 { 373 CLIENT *client; 374 struct ypbind_resp *response; 375 struct ypbind_domain ypbd; 376 char errstring[256]; 377 extern struct rpc_createerr rpc_createerr; 378 int yperr = 0; 379 struct utsname utsname; 380 const char *str; 381 382 /* 383 * CAUTION: Do not go to NIS if the host is the same as the local host 384 * XXX: Lots of special magic to distinguish between local and remote 385 * case. We want to make sure the local case doesn't hang. 386 */ 387 388 if ((uname(&utsname) != -1) && 389 (strcmp(host, utsname.nodename) == 0)) 390 client = __clnt_create_loopback(YPBINDPROG, vers, &yperr); 391 else 392 client = clnt_create(host, YPBINDPROG, vers, "netpath"); 393 if (client == NULL) { 394 if (yperr) 395 (void) fprintf(stderr, 396 "ypwhich: %s\n", yperr_string(yperr)); 397 else { 398 if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED || 399 rpc_createerr.cf_stat == RPC_PROGUNAVAIL) { 400 (void) fprintf(stderr, 401 "ypwhich: %s is not running ypbind\n", host); 402 } else if (rpc_createerr.cf_stat == RPC_PMAPFAILURE) { 403 (void) fprintf(stderr, 404 "ypwhich: %s is not running rpcbind\n", 405 host); 406 } else 407 (void) clnt_pcreateerror("ypwhich: \ 408 clnt_create error"); 409 } 410 exit(1); 411 } 412 ypbd.ypbind_domainname = domain; 413 ypbd.ypbind_vers = vers; 414 response = ypbindproc_domain_3(&ypbd, client); 415 416 if (response == NULL) { 417 (void) sprintf(errstring, 418 "ypwhich: can't call ypbind on %s", host); 419 (void) clnt_perror(client, errstring); 420 exit(1); 421 } 422 423 clnt_destroy(client); 424 425 if (response->ypbind_status != YPBIND_SUCC_VAL) { 426 return (FALSE); 427 } 428 429 if (response->ypbind_resp_u.ypbind_bindinfo) { 430 char *server = 431 response->ypbind_resp_u.ypbind_bindinfo->ypbind_servername; 432 433 if (strcmp(server, nullstring) == 0) { 434 /* depends on a hack in ypbind */ 435 struct nd_hostservlist *nhs = NULL; 436 struct netconfig *nconf = 437 response->ypbind_resp_u.ypbind_bindinfo->ypbind_nconf; 438 struct netbuf *svcaddr = 439 response->ypbind_resp_u.ypbind_bindinfo->ypbind_svcaddr; 440 441 if (netdir_getbyaddr(nconf, &nhs, svcaddr) != ND_OK) { 442 struct sockaddr_in *sa4; 443 struct sockaddr_in6 *sa6; 444 char buf[INET6_ADDRSTRLEN]; 445 char xbuf[IPV6_ADDR_LEN]; 446 int af; 447 void *addr; 448 XDR xdrs; 449 450 sa4 = (struct sockaddr_in *)svcaddr->buf; 451 af = ntohs(sa4->sin_family); 452 if (af != sa4->sin_family) { 453 xdrmem_create(&xdrs, 454 (caddr_t)xbuf, IPV6_ADDR_LEN, 455 XDR_DECODE); 456 if (af == AF_INET6) { 457 xdr_opaque(&xdrs, 458 (caddr_t)svcaddr->buf, 459 IPV6_ADDR_LEN); 460 sa6 = (struct sockaddr_in6 *) 461 xbuf; 462 addr = &sa6->sin6_addr; 463 } else { 464 xdr_opaque(&xdrs, 465 (caddr_t)svcaddr->buf, 466 IPV4_ADDR_LEN); 467 sa4 = (struct sockaddr_in *) 468 xbuf; 469 addr = &sa4->sin_addr; 470 } 471 } else { 472 if (af == AF_INET6) { 473 sa6 = (struct sockaddr_in6 *) 474 svcaddr->buf; 475 addr = &sa6->sin6_addr; 476 } else { 477 addr = &sa4->sin_addr; 478 } 479 } 480 str = inet_ntop(af, addr, buf, sizeof (buf)); 481 if (str == NULL) 482 perror("inet_ntop"); 483 else 484 fprintf(stdout, "%s\n", str); 485 } else { 486 str = nhs->h_hostservs->h_host; 487 if (str == NULL) 488 str = "<unknown>"; 489 fprintf(stdout, "%s\n", str); 490 } 491 netdir_free((char *)nhs, ND_HOSTSERVLIST); 492 } else { 493 fprintf(stdout, "%s\n", server); 494 } 495 } 496 #ifdef DEBUG 497 dump_response(response); 498 #endif 499 return (TRUE); 500 } 501 502 /* 503 * Serializes/deserializes an in_addr struct. 504 * 505 * Note: There is a data coupling between the "definition" of a struct 506 * in_addr implicit in this xdr routine, and the true data definition in 507 * <netinet/in.h>. 508 */ 509 static bool xdr_yp_inaddr(xdrs, ps) 510 XDR * xdrs; 511 struct in_addr *ps; 512 513 { 514 return (xdr_opaque(xdrs, (caddr_t)&ps->s_addr, 4)); 515 } 516 517 /* 518 * Serializes/deserializes an old ypbind_binding struct. 519 */ 520 static bool xdr_old_yp_binding(xdrs, ps) 521 XDR * xdrs; 522 struct old_ypbind_binding *ps; 523 524 { 525 return (xdr_yp_inaddr(xdrs, &ps->ypbind_binding_addr) && 526 xdr_opaque(xdrs, (caddr_t)&ps->ypbind_binding_port, 2)); 527 } 528 529 /* 530 * Serializes/deserializes a ypbind_resp structure. 531 */ 532 static bool xdr_old_ypbind_resp(xdrs, ps) 533 XDR * xdrs; 534 struct old_ypbind_resp *ps; 535 536 { 537 if (!xdr_enum(xdrs, (enum_t *)&ps->ypbind_status)) { 538 return (FALSE); 539 } 540 switch (ps->ypbind_status) { 541 case YPBIND_SUCC_VAL: 542 return (xdr_old_yp_binding(xdrs, 543 &ps->ypbind_respbody.ypbind_bindinfo)); 544 case YPBIND_FAIL_VAL: 545 return (xdr_u_long(xdrs, 546 &ps->ypbind_respbody.ypbind_error)); 547 } 548 return (FALSE); 549 } 550 /* This sends a message to the old ypbind process on host. */ 551 static int old_call_binder(vers) 552 int vers; 553 { 554 CLIENT *client; 555 struct hostent *hp; 556 int sock = RPC_ANYSOCK; 557 enum clnt_stat rpc_stat; 558 struct old_ypbind_resp response; 559 char errstring[256]; 560 extern struct rpc_createerr rpc_createerr; 561 struct in_addr *server; 562 563 if ((client = clnt_create(host, YPBINDPROG, vers, "udp")) == NULL) { 564 if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) { 565 (void) printf("ypwhich: %s is not running ypbind\n", 566 host); 567 exit(1); 568 } 569 if (rpc_createerr.cf_stat == RPC_PMAPFAILURE) { 570 (void) printf("ypwhich: %s is not running port mapper\n", 571 host); 572 exit(1); 573 } 574 (void) clnt_pcreateerror("ypwhich: clnt_create error"); 575 exit(1); 576 } 577 578 rpc_stat = clnt_call(client, YPBINDPROC_DOMAIN, 579 (xdrproc_t)xdr_ypdomain_wrap_string, (caddr_t)&domain, 580 (xdrproc_t)xdr_old_ypbind_resp, (caddr_t)&response, 581 timeout); 582 583 if ((rpc_stat != RPC_SUCCESS) && 584 (rpc_stat != RPC_PROGVERSMISMATCH)) { 585 (void) sprintf(errstring, 586 "ypwhich: can't call ypbind on %s", host); 587 (void) clnt_perror(client, errstring); 588 exit(1); 589 } 590 591 clnt_destroy(client); 592 close(sock); 593 594 if ((rpc_stat != RPC_SUCCESS) || 595 (response.ypbind_status != YPBIND_SUCC_VAL)) { 596 return (FALSE); 597 } 598 599 server = &response.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr; 600 print_server (server); 601 602 return (TRUE); 603 } 604 605 /* 606 * For old version: 607 * This translates a server address to a name and prints it. 608 * We'll get a name by using the standard library routine. 609 */ 610 static void print_server(server) 611 struct in_addr *server; 612 { 613 char buf[256]; 614 struct hostent *hp; 615 616 strcpy(buf, inet_ntoa(*server)); 617 hp = gethostbyaddr((char *)&server->s_addr, 618 sizeof (struct in_addr), AF_INET); 619 620 printf("%s\n", hp ? hp->h_name : buf); 621 } 622 623 #ifdef DEBUG 624 static void 625 dump_response(which) 626 ypbind_resp * which; 627 { 628 struct netconfig *nc; 629 struct netbuf *ua; 630 ypbind_binding * b; 631 632 int i; 633 634 { 635 b = which->ypbind_resp_u.ypbind_bindinfo; 636 if (b == NULL) 637 (void) fprintf(stderr, "???NO Binding information\n"); 638 else { 639 (void) fprintf(stderr, 640 "server=%s lovers=%ld hivers=%ld\n", 641 b->ypbind_servername, 642 b->ypbind_lo_vers, b->ypbind_hi_vers); 643 nc = b->ypbind_nconf; 644 ua = b->ypbind_svcaddr; 645 if (nc == NULL) 646 (void) fprintf(stderr, 647 "ypwhich: NO netconfig information\n"); 648 else { 649 (void) fprintf(stderr, 650 "ypwhich: id %s device %s flag %x protofmly %s proto %s\n", 651 nc->nc_netid, nc->nc_device, 652 (int)nc->nc_flag, nc->nc_protofmly, 653 nc->nc_proto); 654 } 655 if (ua == NULL) 656 (void) fprintf(stderr, 657 "ypwhich: NO netbuf information available from binder\n"); 658 else { 659 (void) fprintf(stderr, 660 "maxlen=%d len=%d\naddr=", ua->maxlen, ua->len); 661 for (i = 0; i < ua->len; i++) { 662 if (i != (ua->len - 1)) 663 (void) fprintf(stderr, 664 "%d.", ua->buf[i]); 665 else 666 (void) fprintf(stderr, 667 "%d\n", ua->buf[i]); 668 } 669 } 670 } 671 } 672 673 } 674 #endif 675 676 /* 677 * This translates a server address to a name and prints it. If the address 678 * is the same as the local address as returned by get_myaddress, the name 679 * is that retrieved from the kernel. If it's any other address (including 680 * another ip address for the local machine), we'll get a name by using the 681 * standard library routine (which calls the yp). 682 */ 683 684 /* 685 * This asks any yp server for the map's master. 686 */ 687 static void 688 get_map_master() 689 { 690 int err; 691 char *master; 692 693 err = __yp_master_rsvdport(domain, map, &master); 694 695 if (err) { 696 (void) fprintf(stderr, 697 "ypwhich: Can't find the master of %s. Reason: %s.\n", 698 map, yperr_string(err)); 699 exit(1); 700 } else { 701 (void) printf("%s\n", master); 702 } 703 } 704 705 /* 706 * This enumerates the entries within map "ypmaps" in the domain at global 707 * "domain", and prints them out key and value per single line. dump_ypmaps 708 * just decides whether we are (probably) able to speak the new YP protocol, 709 * and dispatches to the appropriate function. 710 */ 711 static void 712 dump_ypmaps() 713 { 714 int err; 715 struct dom_binding *binding; 716 717 if (err = __yp_dobind(domain, &binding)) { 718 (void) fprintf(stderr, 719 "dump_ypmaps: Can't bind for domain %s. Reason: %s\n", 720 domain, yperr_string(err)); 721 return; 722 } 723 724 if (binding->dom_binding->ypbind_hi_vers >= YPVERS) { 725 dumpmaps(binding); 726 } 727 } 728 729 static void 730 dumpmaps(binding) 731 struct dom_binding *binding; 732 { 733 enum clnt_stat rpc_stat; 734 int err; 735 char *master; 736 struct ypmaplist *pmpl; 737 struct ypresp_maplist maplist; 738 739 maplist.list = (struct ypmaplist *)NULL; 740 741 rpc_stat = clnt_call(binding->dom_client, YPPROC_MAPLIST, 742 (xdrproc_t)xdr_ypdomain_wrap_string, (caddr_t)&domain, 743 (xdrproc_t)xdr_ypresp_maplist, (caddr_t)&maplist, 744 timeout); 745 746 if (rpc_stat != RPC_SUCCESS) { 747 (void) clnt_perror(binding->dom_client, 748 "ypwhich(dumpmaps): can't get maplist"); 749 __yp_rel_binding(binding); 750 exit(1); 751 } 752 753 if (maplist.status != YP_TRUE) { 754 (void) fprintf(stderr, 755 "ypwhich: Can't get maplist. Reason: %s.\n", 756 yperr_string(ypprot_err(maplist.status))); 757 exit(1); 758 } 759 __yp_rel_binding(binding); 760 761 for (pmpl = maplist.list; pmpl; pmpl = pmpl->ypml_next) { 762 (void) printf("%s ", pmpl->ypml_name); 763 764 err = __yp_master_rsvdport(domain, pmpl->ypml_name, &master); 765 766 if (err) { 767 (void) printf("????????\n"); 768 (void) fprintf(stderr, 769 "ypwhich: Can't find the master of %s. Reason: %s.\n", 770 pmpl->ypml_name, yperr_string(err)); 771 } else { 772 (void) printf("%s\n", master); 773 } 774 } 775 } 776