summaryrefslogtreecommitdiff
path: root/usr/src/lib/librdc/common/netaddrs.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/librdc/common/netaddrs.c')
-rw-r--r--usr/src/lib/librdc/common/netaddrs.c670
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));
+}