diff options
Diffstat (limited to 'usr/src/lib/librdc/common/netaddrs.c')
-rw-r--r-- | usr/src/lib/librdc/common/netaddrs.c | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/usr/src/lib/librdc/common/netaddrs.c b/usr/src/lib/librdc/common/netaddrs.c new file mode 100644 index 0000000000..ed90358618 --- /dev/null +++ b/usr/src/lib/librdc/common/netaddrs.c @@ -0,0 +1,670 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <locale.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <varargs.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/param.h> +#include <rpc/rpc.h> +#include <errno.h> +#include <sys/stat.h> +#include <netdb.h> +#include <sys/pathconf.h> +#include <netdir.h> +#include <netconfig.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <syslog.h> +#include <netinet/in.h> +#include <nfs/nfs_sec.h> +#include <strings.h> +#include <sys/nsctl/rdc_prot.h> +#include <nsctl.h> + +#include "librdc.h" + +#define MAXIFS 32 + +/* number of transports to try */ +#define MNT_PREF_LISTLEN 2 +#define FIRST_TRY 1 +#define SECOND_TRY 2 + + +int +Is_ipv6present(void) +{ +#ifdef AF_INET6 + int sock; + struct lifnum lifn; + + sock = socket(AF_INET6, SOCK_DGRAM, 0); + if (sock < 0) + return (0); + + lifn.lifn_family = AF_INET6; + lifn.lifn_flags = 0; + if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) { + close(sock); + return (0); + } + close(sock); + if (lifn.lifn_count == 0) + return (0); + return (1); +#else + return (0); +#endif +} + +/* + * The following is stolen from autod_nfs.c + */ +static void +getmyaddrs(struct ifconf *ifc) +{ + int sock; + int numifs; + char *buf; + int family; + + ifc->ifc_buf = NULL; + ifc->ifc_len = 0; + +#ifdef AF_INET6 + family = AF_INET6; +#else + family = AF_INET; +#endif + if ((sock = socket(family, SOCK_DGRAM, 0)) < 0) { +#ifdef DEBUG + perror("getmyaddrs(): socket"); +#endif + return; + } + + if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) { +#ifdef DEBUG + perror("getmyaddrs(): SIOCGIFNUM"); +#endif + numifs = MAXIFS; + } + + buf = (char *)malloc(numifs * sizeof (struct ifreq)); + if (buf == NULL) { +#ifdef DEBUG + fprintf(stderr, "getmyaddrs(): malloc failed\n"); +#endif + (void) close(sock); + return; + } + + ifc->ifc_buf = buf; + ifc->ifc_len = numifs * sizeof (struct ifreq); + + if (ioctl(sock, SIOCGIFCONF, (char *)ifc) < 0) { +#ifdef DEBUG + perror("getmyaddrs(): SIOCGIFCONF"); +#else + ; + /*EMPTY*/ +#endif + } + + (void) close(sock); +} + +int +self_check(char *hostname) +{ + int n; + struct sockaddr_in *s1, *s2; + struct ifreq *ifr; + struct nd_hostserv hs; + struct nd_addrlist *retaddrs; + struct netconfig *nconfp; + struct ifconf *ifc; + int retval; + + ifc = malloc(sizeof (struct ifconf)); + if (ifc == NULL) + return (0); + memset((char *)ifc, 0, sizeof (struct ifconf)); + getmyaddrs(ifc); + /* + * Get the IP address for hostname + */ + nconfp = getnetconfigent("udp"); + if (nconfp == NULL) { +#ifdef DEBUG + fprintf(stderr, "self_check(): getnetconfigent failed\n"); +#endif + retval = 0; + goto out; + } + hs.h_host = hostname; + hs.h_serv = "rpcbind"; + if (netdir_getbyname(nconfp, &hs, &retaddrs) != ND_OK) { + freenetconfigent(nconfp); + retval = 0; + goto out; + } + freenetconfigent(nconfp); + /* LINTED pointer alignment */ + s1 = (struct sockaddr_in *)retaddrs->n_addrs->buf; + + /* + * Now compare it against the list of + * addresses for the interfaces on this + * host. + */ + ifr = ifc->ifc_req; + n = ifc->ifc_len / sizeof (struct ifreq); + s2 = NULL; + for (; n > 0; n--, ifr++) { + if (ifr->ifr_addr.sa_family != AF_INET) + continue; + + /* LINTED pointer alignment */ + s2 = (struct sockaddr_in *)&ifr->ifr_addr; + + if (memcmp((char *)&s2->sin_addr, + (char *)&s1->sin_addr, sizeof (s1->sin_addr)) == 0) { + netdir_free((void *)retaddrs, ND_ADDRLIST); + retval = 1; + goto out; /* it's me */ + } + } + netdir_free((void *)retaddrs, ND_ADDRLIST); + retval = 0; + +out: + if (ifc->ifc_buf != NULL) + free(ifc->ifc_buf); + free(ifc); + return (retval); +} + + +int +convert_nconf_to_knconf(struct netconfig *nconf, struct knetconfig *knconf) +{ + struct stat sb; + + if (stat(nconf->nc_device, &sb) < 0) { + (void) syslog(LOG_ERR, "can't find device for transport %s\n", + nconf->nc_device); + return (-1); + } +#ifdef DEBUG_ADDR + printf("lib knconf %x %s %s %x\n", nconf->nc_semantics, + nconf->nc_protofmly, nconf->nc_proto, sb.st_rdev); +#endif + + knconf->knc_semantics = nconf->nc_semantics; + knconf->knc_protofmly = nconf->nc_protofmly; + knconf->knc_proto = nconf->nc_proto; + knconf->knc_rdev = sb.st_rdev; + + return (0); +} + +struct hostent * +gethost_byname(const char *name) +{ + int errnum; +#ifdef AF_INET6 + return (getipnodebyname(name, AF_INET6, AI_DEFAULT, &errnum)); +#else /* !AF_INET6 */ + return (gethostbyname(name)); +#endif /* AF_INET6 */ +} + +int +gethost_netaddrs(char *fromhost, char *tohost, + char *fromnetaddr, char *tonetaddr) +{ + struct hostent *host; + int j; + int errnum; + +#ifdef AF_INET6 + host = getipnodebyname(fromhost, AF_INET6, AI_DEFAULT, &errnum); + if (host == NULL) { +#ifdef DEBUG + (void) fprintf(stderr, dgettext("sndr", + "Could not find host %s"), fromhost); +#endif + return (-1); + } + for (j = 0; j < host->h_length; j++) + fromnetaddr[j] = host->h_addr[j]; + freehostent(host); +#else /* !AF_INET6 */ + host = gethostbyname(fromhost); + if (host == NULL) { +#ifdef DEBUG + (void) fprintf(stderr, dgettext("sndr", + "Could not find host %s"), fromhost); +#endif + return (-1); + } + + if (host->h_length < 4) { +#ifdef DEBUG + fprintf(stderr, "host->h_length(%d) < 4!\n", host->h_length); +#endif + return (-1); + } + + for (j = 0; j < host->h_length; j++) + fromnetaddr[j] = host->h_addr[j]; +#endif /* AF_INET6 */ + +#ifdef AF_INET6 + host = getipnodebyname(tohost, AF_INET6, AI_DEFAULT, &errnum); + if (host == NULL) { +#ifdef DEBUG + (void) fprintf(stderr, dgettext("sndr", + "Could not find host %s"), tohost); +#endif + return (-1); + } + for (j = 0; j < host->h_length; j++) + tonetaddr[j] = host->h_addr[j]; + freehostent(host); +#else /* !AF_INET6 */ + host = gethostbyname(tohost); + if (host == NULL) { +#ifdef DEBUG + (void) fprintf(stderr, dgettext("sndr", + "Could not find host %s"), tohost); +#endif + return (-1); + } + + if (host->h_length < 4) { +#ifdef DEBUG + fprintf(stderr, "host->h_length(%d) < 4!\n", host->h_length); +#endif + return (-1); + } + + for (j = 0; j < host->h_length; j++) + tonetaddr[j] = host->h_addr[j]; +#endif /* AF_INET6 */ + return (0); +} + +/* + * Get the network address on "hostname" for program "prog" + * with version "vers" by using the nconf configuration data + * passed in. + * + * If the address of a netconfig pointer is null then + * information is not sufficient and no netbuf will be returned. + * + * Finally, ping the null procedure of that service. + * + */ +static struct netbuf * +get_the_addr(char *hostname, ulong_t prog, ulong_t vers, + struct netconfig *nconf, ushort_t port, struct t_info *tinfo, + int portmap) +{ + struct netbuf *nb = NULL; + struct t_bind *tbind = NULL; + CLIENT *cl = NULL; + struct timeval tv; + int fd = -1; + AUTH *ah = NULL; + + if (nconf == NULL) + return (NULL); + + if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) == -1) + goto done; + + /* LINTED pointer alignment */ + if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL) + goto done; + + if (portmap) { /* contact rpcbind */ + if (rpcb_getaddr(prog, vers, nconf, &tbind->addr, + hostname) == FALSE) { + goto done; + } + + if (port) { + if (strcmp(nconf->nc_protofmly, NC_INET) == 0) + /* LINTED pointer alignment */ + ((struct sockaddr_in *)tbind->addr.buf)->sin_port + = port; +#ifdef NC_INET6 + else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) + /* LINTED pointer alignment */ + ((struct sockaddr_in6 *)tbind->addr.buf)->sin6_port + = port; +#endif + } + + /* Simon -- we never use the client we create?! */ + cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0); + if (cl == NULL) + goto done; + + ah = authsys_create_default(); + if (ah != NULL) + cl->cl_auth = ah; + + tv.tv_sec = 5; + tv.tv_usec = 0; + + (void) clnt_control(cl, CLSET_TIMEOUT, (char *)&tv); + } else { /* create our own address and skip rpcbind */ + struct netbuf *nb; + struct hostent *hp; + int j; + int errnum; + unsigned short family; + nb = &(tbind->addr); + +#ifdef AF_INET6 + if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) { + hp = getipnodebyname(hostname, AF_INET6, 0, &errnum); + family = AF_INET6; + nb->len = nb->maxlen = sizeof (struct sockaddr_in6); + } else { + hp = getipnodebyname(hostname, AF_INET, 0, &errnum); + family = AF_INET; + nb->len = nb->maxlen = sizeof (struct sockaddr_in); + } + if (hp == NULL) { +#ifdef DEBUG_ADDR + (void) fprintf(stderr, dgettext("sndr", + "Could not find host %s\n"), hostname); +#endif + goto done; + } + nb->buf = (char *)calloc(1, nb->maxlen); + if (nb->buf == NULL) { + (void) printf(dgettext("sndr", "no memory\n")); + goto done; + } + + if (family == AF_INET) { + for (j = 0; j < hp->h_length; j++) + nb->buf[j+4] = hp->h_addr[j]; + /* LINTED pointer alignment */ + ((struct sockaddr_in *)(nb->buf))->sin_port = port; + /* LINTED pointer alignment */ + ((struct sockaddr_in *)(nb->buf))->sin_family = AF_INET; + } else { + for (j = 0; j < hp->h_length; j++) + nb->buf[j+8] = hp->h_addr[j]; + /* LINTED pointer alignment */ + ((struct sockaddr_in6 *)(nb->buf))->sin6_port = port; + /* LINTED pointer alignment */ + ((struct sockaddr_in6 *)(nb->buf))->sin6_family = + AF_INET6; + } + freehostent(hp); +#else + hp = gethostbyname(hostname); + if (hp == NULL) { +#ifdef DEBUG + (void) fprintf(stderr, dgettext("sndr", + "Could not find host %s"), hostname); +#endif + goto done; + } + + nb->len = nb->maxlen = sizeof (struct sockaddr_in); + nb->buf = (char *)calloc(1, nb->maxlen); + if (nb->buf == NULL) { + (void) printf(dgettext("sndr", "no memory\n")); + free(nb); + nb = NULL; + goto done; + } + + for (j = 0; j < hp->h_length; j++) + nb->buf[j+4] = hp->h_addr[j]; + + if (hp->h_addrtype == AF_INET) { + ((struct sockaddr_in *)(nb->buf))->sin_port = port; + ((struct sockaddr_in *)(nb->buf))->sin_family = AF_INET; + } +#endif + } + + /* + * Make a copy of the netbuf to return + */ + nb = (struct netbuf *)calloc(1, sizeof (*nb)); + if (nb == NULL) { + (void) printf(dgettext("sndr", "no memory\n")); + goto done; + } + + *nb = tbind->addr; /* structure copy */ + + nb->buf = (char *)calloc(1, nb->maxlen); + if (nb->buf == NULL) { + (void) printf(dgettext("sndr", "no memory\n")); + free(nb); + nb = NULL; + goto done; + } + + (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len); + +done: + if (cl) { + if (ah != NULL) { + AUTH_DESTROY(cl->cl_auth); + cl->cl_auth = NULL; + } + + clnt_destroy(cl); + cl = NULL; + } + + if (tbind) { + t_free((char *)tbind, T_BIND); + tbind = NULL; + } + + if (fd >= 0) + (void) t_close(fd); + return (nb); +} + +/* + * Get a network address on "hostname" for program "prog" + * with version "vers". If the port number is specified (non zero) + * then try for a TCP/UDP transport and set the port number of the + * resulting IP address. + * + * If the address of a netconfig pointer was passed and + * if it's not null, use it as the netconfig otherwise + * assign the address of the netconfig that was used to + * establish contact with the service. + * If portmap is false, we return a similiar address and we do not + * contact rpcbind + * + */ +struct netbuf * +get_addr(char *hostname, ulong_t prog, ulong_t vers, struct netconfig **nconfp, + char *proto, char *srvport, struct t_info *tinfo, int portmap) +{ + struct netbuf *nb = NULL; + struct netconfig *nconf = NULL; + NCONF_HANDLE *nc = NULL; + int nthtry = FIRST_TRY; + struct servent *svp; + ushort_t port; + + /* + * First lets get the requested port + */ + + if ((svp = getservbyname(srvport, proto)) == NULL) + goto done; + port = svp->s_port; + /* + * No nconf passed in. + * + * Try to get a nconf from /etc/netconfig filtered by + * the NETPATH environment variable. + * First search for COTS, second for CLTS unless proto + * is specified. When we retry, we reset the + * netconfig list so that we would search the whole list + * all over again. + */ + if ((nc = setnetpath()) == NULL) + goto done; + + /* + * If proto is specified, then only search for the match, + * otherwise try COTS first, if failed, try CLTS. + */ + if (proto) { + while (nconf = getnetpath(nc)) { + if (strcmp(nconf->nc_netid, proto) == 0) { + /* + * If the port number is specified then TCP/UDP + * is needed. Otherwise any cots/clts will do. + */ + if (port == 0) + break; + + if ((strcmp(nconf->nc_protofmly, NC_INET) == 0 +#ifdef NC_INET6 + /* CSTYLED */ + || strcmp(nconf->nc_protofmly, NC_INET6) == 0 +#endif + /* CSTYLED */ + ) && + (strcmp(nconf->nc_proto, NC_TCP) == 0 || + strcmp(nconf->nc_proto, NC_UDP) == 0)) + break; + else { + nconf = NULL; + break; + } + } + } + if (nconf == NULL) + goto done; + if ((nb = get_the_addr(hostname, prog, vers, nconf, port, + tinfo, portmap)) == NULL) { + goto done; + } + } else { +retry: + while (nconf = getnetpath(nc)) { + if (nconf->nc_flag & NC_VISIBLE) { + if (nthtry == FIRST_TRY) { + if ((nconf->nc_semantics == NC_TPI_COTS_ORD) || + (nconf->nc_semantics == NC_TPI_COTS)) { + if (port == 0) + break; + if ((strcmp(nconf->nc_protofmly, + NC_INET) == 0 +#ifdef NC_INET6 + /* CSTYLED */ + || strcmp(nconf->nc_protofmly, + NC_INET6) == 0 +#endif + /* CSTYLED */ + ) && + (strcmp(nconf->nc_proto, NC_TCP) == 0)) + break; + } + } + } + } /* while */ + if (nconf == NULL) { + if (++nthtry <= MNT_PREF_LISTLEN) { + endnetpath(nc); + if ((nc = setnetpath()) == NULL) + goto done; + goto retry; + } else + goto done; + } else { + if ((nb = get_the_addr(hostname, prog, vers, nconf, + port, tinfo, portmap)) == NULL) { + /* + * Continue the same search path in the + * netconfig db until no more matched + * nconf (nconf == NULL). + */ + goto retry; + } +#ifdef AF_INET6 + if ((nb->len == 8) && + (strcmp(nconf->nc_protofmly, NC_INET6) == 0)) { + /* + * We have a mismatch in the netconfig retry + */ + free(nb); + goto retry; + } +#endif + } + } + + /* + * Got nconf and nb. Now dup the netconfig structure (nconf) + * and return it thru nconfp. + */ + *nconfp = getnetconfigent(nconf->nc_netid); + if (*nconfp == NULL) { + syslog(LOG_ERR, "no memory\n"); + free(nb); + nb = NULL; + } +done: + if (nc) + endnetpath(nc); + return (nb); +} + + +/* return values as for nsc_check_release() */ +int +rdc_check_release(char **reqd) +{ + /* librdc.so must be built on the runtime OS release */ + return (nsc_check_release(BUILD_REV_STR, NULL, reqd)); +} |