/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996-1999 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
static int scan_interfaces(int *, int *);
static struct hostent *copyandmerge(struct hostent *, struct hostent *, int, int *);
/*%
* Public functions
*/
/*%
* AI_V4MAPPED + AF_INET6
* If no IPv6 address then a query for IPv4 and map returned values.
*
* AI_ALL + AI_V4MAPPED + AF_INET6
* Return IPv6 and IPv4 mapped.
*
* AI_ADDRCONFIG
* Only return IPv6 / IPv4 address if there is an interface of that
* type active.
*/
struct hostent *
getipnodebyname(const char *name, int af, int flags, int *error_num) {
int have_v4 = 1, have_v6 = 1;
struct in_addr in4;
struct in6_addr in6;
struct hostent he, *he1 = NULL, *he2 = NULL, *he3;
int v4 = 0, v6 = 0;
struct net_data *net_data = init();
u_long options;
int tmp_err;
/* If we care about active interfaces then check. */
if ((flags & AI_ADDRCONFIG) != 0)
if (scan_interfaces(&have_v4, &have_v6) == -1) {
*error_num = NO_RECOVERY;
return (NULL);
}
/* Check for literal address. */
if ((v4 = inet_pton(AF_INET, name, &in4)) != 1)
v6 = inet_pton(AF_INET6, name, &in6);
/*%
* Scan the interface table and set have_v4 and have_v6 depending
* upon whether there are IPv4 and IPv6 interface addresses.
*
* Returns:
* 0 on success
* -1 on failure.
*/
/* Get interface list from system. */
if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
goto cleanup;
/*
* Grow buffer until large enough to contain all interface
* descriptions.
*/
for (;;) {
buf = memget(bufsiz);
if (buf == NULL)
goto cleanup;
#ifdef SETFAMILYFLAGS
lifc.lifc_family = AF_UNSPEC; /*%< request all families */
lifc.lifc_flags = 0;
#endif
lifc.lifc_len = bufsiz;
lifc.lifc_buf = buf;
if ((n = ioctl(s, SIOCGLIFCONF, (char *)&lifc)) != -1) {
/*
* Some OS's just return what will fit rather
* than set EINVAL if the buffer is too small
* to fit all the interfaces in. If
* lifc.lifc_len is too near to the end of the
* buffer we will grow it just in case and
* retry.
*/
if (lifc.lifc_len + 2 * sizeof(lifreq) < bufsiz)
break;
}
if ((n == -1) && errno != EINVAL)
goto cleanup;
if (bufsiz > 1000000)
goto cleanup;
memput(buf, bufsiz);
bufsiz += 4096;
}
/* Parse system's interface list. */
cplim = buf + lifc.lifc_len; /*%< skip over if's with big ifr_addr's */
for (cp = buf;
(*have_v4 == 0 || *have_v6 == 0) && cp < cplim;
cp += cpsize) {
memcpy(&lifreq, cp, sizeof lifreq);
#ifdef HAVE_SA_LEN
#ifdef FIX_ZERO_SA_LEN
if (lifreq.lifr_addr.sa_len == 0)
lifreq.lifr_addr.sa_len = 16;
#endif
#ifdef HAVE_MINIMUM_IFREQ
cpsize = sizeof lifreq;
if (lifreq.lifr_addr.sa_len > sizeof (struct sockaddr))
cpsize += (int)lifreq.lifr_addr.sa_len -
(int)(sizeof (struct sockaddr));
#else
cpsize = sizeof lifreq.lifr_name + lifreq.lifr_addr.sa_len;
#endif /* HAVE_MINIMUM_IFREQ */
#elif defined SIOCGIFCONF_ADDR
cpsize = sizeof lifreq;
#else
cpsize = sizeof lifreq.lifr_name;
/* XXX maybe this should be a hard error? */
if (ioctl(s, SIOCGLIFADDR, (char *)&lifreq) < 0)
continue;
#endif
switch (lifreq.lifr_addr.ss_family) {
case AF_INET:
if (*have_v4 == 0) {
memcpy(&in4,
&((struct sockaddr_in *)
&lifreq.lifr_addr)->sin_addr,
sizeof in4);
if (in4.s_addr == INADDR_ANY)
break;
n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq);
if (n < 0)
break;
if ((lifreq.lifr_flags & IFF_UP) == 0)
break;
*have_v4 = 1;
}
break;
case AF_INET6:
if (*have_v6 == 0) {
memcpy(&in6,
&((struct sockaddr_in6 *)
&lifreq.lifr_addr)->sin6_addr, sizeof in6);
if (memcmp(&in6, &in6addr_any, sizeof in6) == 0)
break;
n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq);
if (n < 0)
break;
if ((lifreq.lifr_flags & IFF_UP) == 0)
break;
*have_v6 = 1;
}
break;
}
}
if (buf != NULL)
memput(buf, bufsiz);
close(s);
/* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */
return;
cleanup:
if (buf != NULL)
memput(buf, bufsiz);
if (s != -1)
close(s);
/* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */
return;
}
#endif
static int
scan_interfaces(int *have_v4, int *have_v6) {
struct ifconf ifc;
union {
char _pad[256]; /*%< leave space for IPv6 addresses */
struct ifreq ifreq;
} u;
struct in_addr in4;
struct in6_addr in6;
char *buf = NULL, *cp, *cplim;
static unsigned int bufsiz = 4095;
int s, n;
size_t cpsize;
/* Set to zero. Used as loop terminators below. */
*have_v4 = *have_v6 = 0;
#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \
!defined(IRIX_EMUL_IOCTL_SIOCGIFCONF)
/*
* Try to scan the interfaces using IPv6 ioctls().
*/
scan_interfaces6(have_v4, have_v6);
if (*have_v4 != 0 && *have_v6 != 0)
return (0);
#endif
#ifdef __linux
scan_linux6(have_v6);
#endif
/* Get interface list from system. */
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
goto err_ret;
/*
* Grow buffer until large enough to contain all interface
* descriptions.
*/
for (;;) {
buf = memget(bufsiz);
if (buf == NULL)
goto err_ret;
ifc.ifc_len = bufsiz;
ifc.ifc_buf = buf;
#ifdef IRIX_EMUL_IOCTL_SIOCGIFCONF
/*
* This is a fix for IRIX OS in which the call to ioctl with
* the flag SIOCGIFCONF may not return an entry for all the
* interfaces like most flavors of Unix.
*/
if (emul_ioctl(&ifc) >= 0)
break;
#else
if ((n = ioctl(s, SIOCGIFCONF, (char *)&ifc)) != -1) {
/*
* Some OS's just return what will fit rather
* than set EINVAL if the buffer is too small
* to fit all the interfaces in. If
* ifc.ifc_len is too near to the end of the
* buffer we will grow it just in case and
* retry.
*/
if (ifc.ifc_len + 2 * sizeof(u.ifreq) < bufsiz)
break;
}
#endif
if ((n == -1) && errno != EINVAL)
goto err_ret;
if (bufsiz > 1000000)
goto err_ret;
memput(buf, bufsiz);
bufsiz += 4096;
}
/* Parse system's interface list. */
cplim = buf + ifc.ifc_len; /*%< skip over if's with big ifr_addr's */
for (cp = buf;
(*have_v4 == 0 || *have_v6 == 0) && cp < cplim;
cp += cpsize) {
memcpy(&u.ifreq, cp, sizeof u.ifreq);
#ifdef HAVE_SA_LEN
#ifdef FIX_ZERO_SA_LEN
if (u.ifreq.ifr_addr.sa_len == 0)
u.ifreq.ifr_addr.sa_len = 16;
#endif
#ifdef HAVE_MINIMUM_IFREQ
cpsize = sizeof u.ifreq;
if (u.ifreq.ifr_addr.sa_len > sizeof (struct sockaddr))
cpsize += (int)u.ifreq.ifr_addr.sa_len -
(int)(sizeof (struct sockaddr));
#else
cpsize = sizeof u.ifreq.ifr_name + u.ifreq.ifr_addr.sa_len;
#endif /* HAVE_MINIMUM_IFREQ */
if (cpsize > sizeof u.ifreq && cpsize <= sizeof u)
memcpy(&u.ifreq, cp, cpsize);
#elif defined SIOCGIFCONF_ADDR
cpsize = sizeof u.ifreq;
#else
cpsize = sizeof u.ifreq.ifr_name;
/* XXX maybe this should be a hard error? */
if (ioctl(s, SIOCGIFADDR, (char *)&u.ifreq) < 0)
continue;
#endif
switch (u.ifreq.ifr_addr.sa_family) {
case AF_INET:
if (*have_v4 == 0) {
memcpy(&in4,
&((struct sockaddr_in *)
&u.ifreq.ifr_addr)->sin_addr,
sizeof in4);
if (in4.s_addr == INADDR_ANY)
break;
n = ioctl(s, SIOCGIFFLAGS, (char *)&u.ifreq);
if (n < 0)
break;
if ((u.ifreq.ifr_flags & IFF_UP) == 0)
break;
*have_v4 = 1;
}
break;
case AF_INET6:
if (*have_v6 == 0) {
memcpy(&in6,
&((struct sockaddr_in6 *)
&u.ifreq.ifr_addr)->sin6_addr,
sizeof in6);
if (memcmp(&in6, &in6addr_any, sizeof in6) == 0)
break;
n = ioctl(s, SIOCGIFFLAGS, (char *)&u.ifreq);
if (n < 0)
break;
if ((u.ifreq.ifr_flags & IFF_UP) == 0)
break;
*have_v6 = 1;
}
break;
}
}
if (buf != NULL)
memput(buf, bufsiz);
close(s);
/* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */
return (0);
err_ret:
if (buf != NULL)
memput(buf, bufsiz);
if (s != -1)
close(s);
/* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */
return (-1);
}
static struct hostent *
copyandmerge(struct hostent *he1, struct hostent *he2, int af, int *error_num) {
struct hostent *he = NULL;
int addresses = 1; /*%< NULL terminator */
int names = 1; /*%< NULL terminator */
int len = 0;
char **cpp, **npp;
/*
* Work out array sizes;
*/
if (he1 != NULL) {
cpp = he1->h_addr_list;
while (*cpp != NULL) {
addresses++;
cpp++;
}
cpp = he1->h_aliases;
while (*cpp != NULL) {
names++;
cpp++;
}
}
if (he2 != NULL) {
cpp = he2->h_addr_list;
while (*cpp != NULL) {
addresses++;
cpp++;
}
if (he1 == NULL) {
cpp = he2->h_aliases;
while (*cpp != NULL) {
names++;
cpp++;
}
}
}
static struct hostent *
fakeaddr(const char *name, int af, struct net_data *net_data) {
struct pvt *pvt;
freepvt(net_data);
net_data->ho_data = malloc(sizeof (struct pvt));
if (!net_data->ho_data) {
errno = ENOMEM;
RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL);
return (NULL);
}
pvt = net_data->ho_data;
#ifndef __bsdi__
/*
* Unlike its forebear(inet_aton), our friendly inet_pton() is strict
* in its interpretation of its input, and it will only return "1" if
* the input string is a formally valid(and thus unambiguous with
* respect to host names) internet address specification for this AF.
*
* This means "telnet 0xdeadbeef" and "telnet 127.1" are dead now.
*/
if (inet_pton(af, name, pvt->addr) != 1) {
#else
/* BSDI XXX
* We put this back to inet_aton -- we really want the old behavior
* Long live 127.1...
*/
if ((af != AF_INET ||
inet_aton(name, (struct in_addr *)pvt->addr) != 1) &&
inet_pton(af, name, pvt->addr) != 1) {
#endif
RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND);
return (NULL);
}
strncpy(pvt->name, name, NS_MAXDNAME);
pvt->name[NS_MAXDNAME] = '\0';
if (af == AF_INET && (net_data->res->options & RES_USE_INET6) != 0U) {
map_v4v6_address(pvt->addr, pvt->addr);
af = AF_INET6;
}
pvt->host.h_addrtype = af;
switch(af) {
case AF_INET:
pvt->host.h_length = NS_INADDRSZ;
break;
case AF_INET6:
pvt->host.h_length = NS_IN6ADDRSZ;
break;
default:
errno = EAFNOSUPPORT;
RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL);
return (NULL);
}
pvt->host.h_name = pvt->name;
pvt->host.h_aliases = pvt->aliases;
pvt->aliases[0] = NULL;
pvt->addrs[0] = (char *)pvt->addr;
pvt->addrs[1] = NULL;
pvt->host.h_addr_list = pvt->addrs;
RES_SET_H_ERRNO(net_data->res, NETDB_SUCCESS);
return (&pvt->host);
}
#ifdef grot /*%< for future use in gethostbyaddr(), for "SUNSECURITY" */
struct hostent *rhp;
char **haddr;
u_long old_options;
char hname2[MAXDNAME+1];
if (af == AF_INET) {
/*
* turn off search as the name should be absolute,
* 'localhost' should be matched by defnames
*/
strncpy(hname2, hp->h_name, MAXDNAME);
hname2[MAXDNAME] = '\0';
old_options = net_data->res->options;
net_data->res->options &= ~RES_DNSRCH;
net_data->res->options |= RES_DEFNAMES;
if (!(rhp = gethostbyname(hname2))) {
net_data->res->options = old_options;
RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND);
return (NULL);
}
net_data->res->options = old_options;
for (haddr = rhp->h_addr_list; *haddr; haddr++)
if (!memcmp(*haddr, addr, INADDRSZ))
break;
if (!*haddr) {
RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND);
return (NULL);
}
}
#endif /* grot */
#endif /*__BIND_NOSTATIC*/