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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <locale.h> 27 #include <stdio.h> 28 #include <string.h> 29 #include <memory.h> 30 #include <varargs.h> 31 #include <unistd.h> 32 #include <ctype.h> 33 #include <stdlib.h> 34 #include <signal.h> 35 #include <sys/param.h> 36 #include <rpc/rpc.h> 37 #include <errno.h> 38 #include <sys/stat.h> 39 #include <netdb.h> 40 #include <sys/pathconf.h> 41 #include <netdir.h> 42 #include <netconfig.h> 43 #include <sys/sockio.h> 44 #include <net/if.h> 45 #include <syslog.h> 46 #include <netinet/in.h> 47 #include <nfs/nfs_sec.h> 48 #include <strings.h> 49 #include <sys/nsctl/rdc_prot.h> 50 #include <nsctl.h> 51 52 #include "librdc.h" 53 54 #define MAXIFS 32 55 56 /* number of transports to try */ 57 #define MNT_PREF_LISTLEN 2 58 #define FIRST_TRY 1 59 #define SECOND_TRY 2 60 61 62 int 63 Is_ipv6present(void) 64 { 65 #ifdef AF_INET6 66 int sock; 67 struct lifnum lifn; 68 69 sock = socket(AF_INET6, SOCK_DGRAM, 0); 70 if (sock < 0) 71 return (0); 72 73 lifn.lifn_family = AF_INET6; 74 lifn.lifn_flags = 0; 75 if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) { 76 close(sock); 77 return (0); 78 } 79 close(sock); 80 if (lifn.lifn_count == 0) 81 return (0); 82 return (1); 83 #else 84 return (0); 85 #endif 86 } 87 88 /* 89 * The following is stolen from autod_nfs.c 90 */ 91 static void 92 getmyaddrs(struct ifconf *ifc) 93 { 94 int sock; 95 int numifs; 96 char *buf; 97 int family; 98 99 ifc->ifc_buf = NULL; 100 ifc->ifc_len = 0; 101 102 #ifdef AF_INET6 103 family = AF_INET6; 104 #else 105 family = AF_INET; 106 #endif 107 if ((sock = socket(family, SOCK_DGRAM, 0)) < 0) { 108 #ifdef DEBUG 109 perror("getmyaddrs(): socket"); 110 #endif 111 return; 112 } 113 114 if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) { 115 #ifdef DEBUG 116 perror("getmyaddrs(): SIOCGIFNUM"); 117 #endif 118 numifs = MAXIFS; 119 } 120 121 buf = (char *)malloc(numifs * sizeof (struct ifreq)); 122 if (buf == NULL) { 123 #ifdef DEBUG 124 fprintf(stderr, "getmyaddrs(): malloc failed\n"); 125 #endif 126 (void) close(sock); 127 return; 128 } 129 130 ifc->ifc_buf = buf; 131 ifc->ifc_len = numifs * sizeof (struct ifreq); 132 133 if (ioctl(sock, SIOCGIFCONF, (char *)ifc) < 0) { 134 #ifdef DEBUG 135 perror("getmyaddrs(): SIOCGIFCONF"); 136 #else 137 ; 138 /*EMPTY*/ 139 #endif 140 } 141 142 (void) close(sock); 143 } 144 145 int 146 self_check(char *hostname) 147 { 148 int n; 149 struct sockaddr_in *s1, *s2; 150 struct ifreq *ifr; 151 struct nd_hostserv hs; 152 struct nd_addrlist *retaddrs; 153 struct netconfig *nconfp; 154 struct ifconf *ifc; 155 int retval; 156 157 ifc = malloc(sizeof (struct ifconf)); 158 if (ifc == NULL) 159 return (0); 160 memset((char *)ifc, 0, sizeof (struct ifconf)); 161 getmyaddrs(ifc); 162 /* 163 * Get the IP address for hostname 164 */ 165 nconfp = getnetconfigent("udp"); 166 if (nconfp == NULL) { 167 #ifdef DEBUG 168 fprintf(stderr, "self_check(): getnetconfigent failed\n"); 169 #endif 170 retval = 0; 171 goto out; 172 } 173 hs.h_host = hostname; 174 hs.h_serv = "rpcbind"; 175 if (netdir_getbyname(nconfp, &hs, &retaddrs) != ND_OK) { 176 freenetconfigent(nconfp); 177 retval = 0; 178 goto out; 179 } 180 freenetconfigent(nconfp); 181 /* LINTED pointer alignment */ 182 s1 = (struct sockaddr_in *)retaddrs->n_addrs->buf; 183 184 /* 185 * Now compare it against the list of 186 * addresses for the interfaces on this 187 * host. 188 */ 189 ifr = ifc->ifc_req; 190 n = ifc->ifc_len / sizeof (struct ifreq); 191 s2 = NULL; 192 for (; n > 0; n--, ifr++) { 193 if (ifr->ifr_addr.sa_family != AF_INET) 194 continue; 195 196 /* LINTED pointer alignment */ 197 s2 = (struct sockaddr_in *)&ifr->ifr_addr; 198 199 if (memcmp((char *)&s2->sin_addr, 200 (char *)&s1->sin_addr, sizeof (s1->sin_addr)) == 0) { 201 netdir_free((void *)retaddrs, ND_ADDRLIST); 202 retval = 1; 203 goto out; /* it's me */ 204 } 205 } 206 netdir_free((void *)retaddrs, ND_ADDRLIST); 207 retval = 0; 208 209 out: 210 if (ifc->ifc_buf != NULL) 211 free(ifc->ifc_buf); 212 free(ifc); 213 return (retval); 214 } 215 216 217 int 218 convert_nconf_to_knconf(struct netconfig *nconf, struct knetconfig *knconf) 219 { 220 struct stat sb; 221 222 if (stat(nconf->nc_device, &sb) < 0) { 223 (void) syslog(LOG_ERR, "can't find device for transport %s\n", 224 nconf->nc_device); 225 return (-1); 226 } 227 #ifdef DEBUG_ADDR 228 printf("lib knconf %x %s %s %x\n", nconf->nc_semantics, 229 nconf->nc_protofmly, nconf->nc_proto, sb.st_rdev); 230 #endif 231 232 knconf->knc_semantics = nconf->nc_semantics; 233 knconf->knc_protofmly = nconf->nc_protofmly; 234 knconf->knc_proto = nconf->nc_proto; 235 knconf->knc_rdev = sb.st_rdev; 236 237 return (0); 238 } 239 240 struct hostent * 241 gethost_byname(const char *name) 242 { 243 int errnum; 244 #ifdef AF_INET6 245 return (getipnodebyname(name, AF_INET6, AI_DEFAULT, &errnum)); 246 #else /* !AF_INET6 */ 247 return (gethostbyname(name)); 248 #endif /* AF_INET6 */ 249 } 250 251 int 252 gethost_netaddrs(char *fromhost, char *tohost, 253 char *fromnetaddr, char *tonetaddr) 254 { 255 struct hostent *host; 256 int j; 257 int errnum; 258 259 #ifdef AF_INET6 260 host = getipnodebyname(fromhost, AF_INET6, AI_DEFAULT, &errnum); 261 if (host == NULL) { 262 #ifdef DEBUG 263 (void) fprintf(stderr, dgettext("sndr", 264 "Could not find host %s"), fromhost); 265 #endif 266 return (-1); 267 } 268 for (j = 0; j < host->h_length; j++) 269 fromnetaddr[j] = host->h_addr[j]; 270 freehostent(host); 271 #else /* !AF_INET6 */ 272 host = gethostbyname(fromhost); 273 if (host == NULL) { 274 #ifdef DEBUG 275 (void) fprintf(stderr, dgettext("sndr", 276 "Could not find host %s"), fromhost); 277 #endif 278 return (-1); 279 } 280 281 if (host->h_length < 4) { 282 #ifdef DEBUG 283 fprintf(stderr, "host->h_length(%d) < 4!\n", host->h_length); 284 #endif 285 return (-1); 286 } 287 288 for (j = 0; j < host->h_length; j++) 289 fromnetaddr[j] = host->h_addr[j]; 290 #endif /* AF_INET6 */ 291 292 #ifdef AF_INET6 293 host = getipnodebyname(tohost, AF_INET6, AI_DEFAULT, &errnum); 294 if (host == NULL) { 295 #ifdef DEBUG 296 (void) fprintf(stderr, dgettext("sndr", 297 "Could not find host %s"), tohost); 298 #endif 299 return (-1); 300 } 301 for (j = 0; j < host->h_length; j++) 302 tonetaddr[j] = host->h_addr[j]; 303 freehostent(host); 304 #else /* !AF_INET6 */ 305 host = gethostbyname(tohost); 306 if (host == NULL) { 307 #ifdef DEBUG 308 (void) fprintf(stderr, dgettext("sndr", 309 "Could not find host %s"), tohost); 310 #endif 311 return (-1); 312 } 313 314 if (host->h_length < 4) { 315 #ifdef DEBUG 316 fprintf(stderr, "host->h_length(%d) < 4!\n", host->h_length); 317 #endif 318 return (-1); 319 } 320 321 for (j = 0; j < host->h_length; j++) 322 tonetaddr[j] = host->h_addr[j]; 323 #endif /* AF_INET6 */ 324 return (0); 325 } 326 327 /* 328 * Get the network address on "hostname" for program "prog" 329 * with version "vers" by using the nconf configuration data 330 * passed in. 331 * 332 * If the address of a netconfig pointer is null then 333 * information is not sufficient and no netbuf will be returned. 334 * 335 * Finally, ping the null procedure of that service. 336 * 337 */ 338 static struct netbuf * 339 get_the_addr(char *hostname, ulong_t prog, ulong_t vers, 340 struct netconfig *nconf, ushort_t port, struct t_info *tinfo, 341 int portmap) 342 { 343 struct netbuf *nb = NULL; 344 struct t_bind *tbind = NULL; 345 CLIENT *cl = NULL; 346 struct timeval tv; 347 int fd = -1; 348 AUTH *ah = NULL; 349 350 if (nconf == NULL) 351 return (NULL); 352 353 if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) == -1) 354 goto done; 355 356 /* LINTED pointer alignment */ 357 if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL) 358 goto done; 359 360 if (portmap) { /* contact rpcbind */ 361 if (rpcb_getaddr(prog, vers, nconf, &tbind->addr, 362 hostname) == FALSE) { 363 goto done; 364 } 365 366 if (port) { 367 if (strcmp(nconf->nc_protofmly, NC_INET) == 0) 368 /* LINTED pointer alignment */ 369 ((struct sockaddr_in *)tbind->addr.buf)->sin_port 370 = port; 371 #ifdef NC_INET6 372 else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) 373 /* LINTED pointer alignment */ 374 ((struct sockaddr_in6 *)tbind->addr.buf)->sin6_port 375 = port; 376 #endif 377 } 378 379 /* Simon -- we never use the client we create?! */ 380 cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0); 381 if (cl == NULL) 382 goto done; 383 384 ah = authsys_create_default(); 385 if (ah != NULL) 386 cl->cl_auth = ah; 387 388 tv.tv_sec = 5; 389 tv.tv_usec = 0; 390 391 (void) clnt_control(cl, CLSET_TIMEOUT, (char *)&tv); 392 } else { /* create our own address and skip rpcbind */ 393 struct netbuf *nb; 394 struct hostent *hp; 395 int j; 396 int errnum; 397 unsigned short family; 398 nb = &(tbind->addr); 399 400 #ifdef AF_INET6 401 if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) { 402 hp = getipnodebyname(hostname, AF_INET6, 0, &errnum); 403 family = AF_INET6; 404 nb->len = nb->maxlen = sizeof (struct sockaddr_in6); 405 } else { 406 hp = getipnodebyname(hostname, AF_INET, 0, &errnum); 407 family = AF_INET; 408 nb->len = nb->maxlen = sizeof (struct sockaddr_in); 409 } 410 if (hp == NULL) { 411 #ifdef DEBUG_ADDR 412 (void) fprintf(stderr, dgettext("sndr", 413 "Could not find host %s\n"), hostname); 414 #endif 415 goto done; 416 } 417 nb->buf = (char *)calloc(1, nb->maxlen); 418 if (nb->buf == NULL) { 419 (void) printf(dgettext("sndr", "no memory\n")); 420 goto done; 421 } 422 423 if (family == AF_INET) { 424 for (j = 0; j < hp->h_length; j++) 425 nb->buf[j+4] = hp->h_addr[j]; 426 /* LINTED pointer alignment */ 427 ((struct sockaddr_in *)(nb->buf))->sin_port = port; 428 /* LINTED pointer alignment */ 429 ((struct sockaddr_in *)(nb->buf))->sin_family = AF_INET; 430 } else { 431 for (j = 0; j < hp->h_length; j++) 432 nb->buf[j+8] = hp->h_addr[j]; 433 /* LINTED pointer alignment */ 434 ((struct sockaddr_in6 *)(nb->buf))->sin6_port = port; 435 /* LINTED pointer alignment */ 436 ((struct sockaddr_in6 *)(nb->buf))->sin6_family = 437 AF_INET6; 438 } 439 freehostent(hp); 440 #else 441 hp = gethostbyname(hostname); 442 if (hp == NULL) { 443 #ifdef DEBUG 444 (void) fprintf(stderr, dgettext("sndr", 445 "Could not find host %s"), hostname); 446 #endif 447 goto done; 448 } 449 450 nb->len = nb->maxlen = sizeof (struct sockaddr_in); 451 nb->buf = (char *)calloc(1, nb->maxlen); 452 if (nb->buf == NULL) { 453 (void) printf(dgettext("sndr", "no memory\n")); 454 free(nb); 455 nb = NULL; 456 goto done; 457 } 458 459 for (j = 0; j < hp->h_length; j++) 460 nb->buf[j+4] = hp->h_addr[j]; 461 462 if (hp->h_addrtype == AF_INET) { 463 ((struct sockaddr_in *)(nb->buf))->sin_port = port; 464 ((struct sockaddr_in *)(nb->buf))->sin_family = AF_INET; 465 } 466 #endif 467 } 468 469 /* 470 * Make a copy of the netbuf to return 471 */ 472 nb = (struct netbuf *)calloc(1, sizeof (*nb)); 473 if (nb == NULL) { 474 (void) printf(dgettext("sndr", "no memory\n")); 475 goto done; 476 } 477 478 *nb = tbind->addr; /* structure copy */ 479 480 nb->buf = (char *)calloc(1, nb->maxlen); 481 if (nb->buf == NULL) { 482 (void) printf(dgettext("sndr", "no memory\n")); 483 free(nb); 484 nb = NULL; 485 goto done; 486 } 487 488 (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len); 489 490 done: 491 if (cl) { 492 if (ah != NULL) { 493 AUTH_DESTROY(cl->cl_auth); 494 cl->cl_auth = NULL; 495 } 496 497 clnt_destroy(cl); 498 cl = NULL; 499 } 500 501 if (tbind) { 502 t_free((char *)tbind, T_BIND); 503 tbind = NULL; 504 } 505 506 if (fd >= 0) 507 (void) t_close(fd); 508 return (nb); 509 } 510 511 /* 512 * Get a network address on "hostname" for program "prog" 513 * with version "vers". If the port number is specified (non zero) 514 * then try for a TCP/UDP transport and set the port number of the 515 * resulting IP address. 516 * 517 * If the address of a netconfig pointer was passed and 518 * if it's not null, use it as the netconfig otherwise 519 * assign the address of the netconfig that was used to 520 * establish contact with the service. 521 * If portmap is false, we return a similiar address and we do not 522 * contact rpcbind 523 * 524 */ 525 struct netbuf * 526 get_addr(char *hostname, ulong_t prog, ulong_t vers, struct netconfig **nconfp, 527 char *proto, char *srvport, struct t_info *tinfo, int portmap) 528 { 529 struct netbuf *nb = NULL; 530 struct netconfig *nconf = NULL; 531 NCONF_HANDLE *nc = NULL; 532 int nthtry = FIRST_TRY; 533 struct servent *svp; 534 ushort_t port; 535 536 /* 537 * First lets get the requested port 538 */ 539 540 if ((svp = getservbyname(srvport, proto)) == NULL) 541 goto done; 542 port = svp->s_port; 543 /* 544 * No nconf passed in. 545 * 546 * Try to get a nconf from /etc/netconfig filtered by 547 * the NETPATH environment variable. 548 * First search for COTS, second for CLTS unless proto 549 * is specified. When we retry, we reset the 550 * netconfig list so that we would search the whole list 551 * all over again. 552 */ 553 if ((nc = setnetpath()) == NULL) 554 goto done; 555 556 /* 557 * If proto is specified, then only search for the match, 558 * otherwise try COTS first, if failed, try CLTS. 559 */ 560 if (proto) { 561 while (nconf = getnetpath(nc)) { 562 if (strcmp(nconf->nc_netid, proto) == 0) { 563 /* 564 * If the port number is specified then TCP/UDP 565 * is needed. Otherwise any cots/clts will do. 566 */ 567 if (port == 0) 568 break; 569 570 if ((strcmp(nconf->nc_protofmly, NC_INET) == 0 571 #ifdef NC_INET6 572 /* CSTYLED */ 573 || strcmp(nconf->nc_protofmly, NC_INET6) == 0 574 #endif 575 /* CSTYLED */ 576 ) && 577 (strcmp(nconf->nc_proto, NC_TCP) == 0 || 578 strcmp(nconf->nc_proto, NC_UDP) == 0)) 579 break; 580 else { 581 nconf = NULL; 582 break; 583 } 584 } 585 } 586 if (nconf == NULL) 587 goto done; 588 if ((nb = get_the_addr(hostname, prog, vers, nconf, port, 589 tinfo, portmap)) == NULL) { 590 goto done; 591 } 592 } else { 593 retry: 594 while (nconf = getnetpath(nc)) { 595 if (nconf->nc_flag & NC_VISIBLE) { 596 if (nthtry == FIRST_TRY) { 597 if ((nconf->nc_semantics == NC_TPI_COTS_ORD) || 598 (nconf->nc_semantics == NC_TPI_COTS)) { 599 if (port == 0) 600 break; 601 if ((strcmp(nconf->nc_protofmly, 602 NC_INET) == 0 603 #ifdef NC_INET6 604 /* CSTYLED */ 605 || strcmp(nconf->nc_protofmly, 606 NC_INET6) == 0 607 #endif 608 /* CSTYLED */ 609 ) && 610 (strcmp(nconf->nc_proto, NC_TCP) == 0)) 611 break; 612 } 613 } 614 } 615 } /* while */ 616 if (nconf == NULL) { 617 if (++nthtry <= MNT_PREF_LISTLEN) { 618 endnetpath(nc); 619 if ((nc = setnetpath()) == NULL) 620 goto done; 621 goto retry; 622 } else 623 goto done; 624 } else { 625 if ((nb = get_the_addr(hostname, prog, vers, nconf, 626 port, tinfo, portmap)) == NULL) { 627 /* 628 * Continue the same search path in the 629 * netconfig db until no more matched 630 * nconf (nconf == NULL). 631 */ 632 goto retry; 633 } 634 #ifdef AF_INET6 635 if ((nb->len == 8) && 636 (strcmp(nconf->nc_protofmly, NC_INET6) == 0)) { 637 /* 638 * We have a mismatch in the netconfig retry 639 */ 640 free(nb); 641 goto retry; 642 } 643 #endif 644 } 645 } 646 647 /* 648 * Got nconf and nb. Now dup the netconfig structure (nconf) 649 * and return it thru nconfp. 650 */ 651 *nconfp = getnetconfigent(nconf->nc_netid); 652 if (*nconfp == NULL) { 653 syslog(LOG_ERR, "no memory\n"); 654 free(nb); 655 nb = NULL; 656 } 657 done: 658 if (nc) 659 endnetpath(nc); 660 return (nb); 661 } 662 663 664 /* return values as for nsc_check_release() */ 665 int 666 rdc_check_release(char **reqd) 667 { 668 /* librdc.so must be built on the runtime OS release */ 669 return (nsc_check_release(BUILD_REV_STR, NULL, reqd)); 670 }