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 }