netaddrs.c revision fcf3ce441efd61da9bb2884968af01cb7c1452cc
/*
* 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));
}