diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 18:37:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 18:37:14 +0000 |
commit | ea648e70a989cca190cd7403fe892fd2dcc290b4 (patch) | |
tree | e2b6b1c647da68b0d4d66082835e256eb30970e8 /lib/lwres/getipnode.c | |
parent | Initial commit. (diff) | |
download | bind9-upstream.tar.xz bind9-upstream.zip |
Adding upstream version 1:9.11.5.P4+dfsg.upstream/1%9.11.5.P4+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | lib/lwres/getipnode.c | 1160 |
1 files changed, 1160 insertions, 0 deletions
diff --git a/lib/lwres/getipnode.c b/lib/lwres/getipnode.c new file mode 100644 index 0000000..f0c0ae6 --- /dev/null +++ b/lib/lwres/getipnode.c @@ -0,0 +1,1160 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id: getipnode.c,v 1.47 2009/09/01 23:47:45 tbox Exp $ */ + +/*! \file */ + +/** + * These functions perform thread safe, protocol independent + * nodename-to-address and address-to-nodename translation as defined in + * RFC2553. This use a struct hostent which is defined in namedb.h: + * + * \code + * struct hostent { + * char *h_name; // official name of host + * char **h_aliases; // alias list + * int h_addrtype; // host address type + * int h_length; // length of address + * char **h_addr_list; // list of addresses from name server + * }; + * #define h_addr h_addr_list[0] // address, for backward compatibility + * \endcode + * + * The members of this structure are: + * + * \li h_name: + * The official (canonical) name of the host. + * + * \li h_aliases: + * A NULL-terminated array of alternate names (nicknames) for the + * host. + * + * \li h_addrtype: + * The type of address being returned - usually PF_INET or + * PF_INET6. + * + * \li h_length: + * The length of the address in bytes. + * + * \li h_addr_list: + * A NULL terminated array of network addresses for the host. Host + * addresses are returned in network byte order. + * + * lwres_getipnodebyname() looks up addresses of protocol family af for + * the hostname name. The flags parameter contains ORed flag bits to + * specify the types of addresses that are searched for, and the types of + * addresses that are returned. The flag bits are: + * + * \li #AI_V4MAPPED: + * This is used with an af of #AF_INET6, and causes IPv4 addresses + * to be returned as IPv4-mapped IPv6 addresses. + * + * \li #AI_ALL: + * This is used with an af of #AF_INET6, and causes all known + * addresses (IPv6 and IPv4) to be returned. If #AI_V4MAPPED is + * also set, the IPv4 addresses are return as mapped IPv6 + * addresses. + * + * \li #AI_ADDRCONFIG: + * Only return an IPv6 or IPv4 address if here is an active + * network interface of that type. This is not currently + * implemented in the BIND 9 lightweight resolver, and the flag is + * ignored. + * + * \li #AI_DEFAULT: + * This default sets the #AI_V4MAPPED and #AI_ADDRCONFIG flag bits. + * + * lwres_getipnodebyaddr() performs a reverse lookup of address src which + * is len bytes long. af denotes the protocol family, typically PF_INET + * or PF_INET6. + * + * lwres_freehostent() releases all the memory associated with the struct + * hostent pointer. Any memory allocated for the h_name, h_addr_list + * and h_aliases is freed, as is the memory for the hostent structure + * itself. + * + * \section getipnode_return Return Values + * + * If an error occurs, lwres_getipnodebyname() and + * lwres_getipnodebyaddr() set *error_num to an appropriate error code + * and the function returns a NULL pointer. The error codes and their + * meanings are defined in \link netdb.h <lwres/netdb.h>\endlink: + * + * \li #HOST_NOT_FOUND: + * No such host is known. + * + * \li #NO_ADDRESS: + * The server recognised the request and the name but no address + * is available. Another type of request to the name server for + * the domain might return an answer. + * + * \li #TRY_AGAIN: + * A temporary and possibly transient error occurred, such as a + * failure of a server to respond. The request may succeed if + * retried. + * + * \li #NO_RECOVERY: + * An unexpected failure occurred, and retrying the request is + * pointless. + * + * lwres_hstrerror() translates these error codes to suitable error + * messages. + * + * \section getipnode_see See Also + * + * getaddrinfo.c, gethost.c, getnameinfo.c, herror.c, RFC2553 + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <lwres/lwres.h> +#include <lwres/net.h> +#include <lwres/netdb.h> /* XXX #include <netdb.h> */ + +#include "assert_p.h" + +#ifndef INADDRSZ +#define INADDRSZ 4 +#endif +#ifndef IN6ADDRSZ +#define IN6ADDRSZ 16 +#endif + +#ifdef LWRES_PLATFORM_NEEDIN6ADDRANY +LIBLWRES_EXTERNAL_DATA const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; +#endif + +#ifndef IN6_IS_ADDR_V4COMPAT +static const unsigned char in6addr_compat[12] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +#define IN6_IS_ADDR_V4COMPAT(x) (!memcmp((x)->s6_addr, in6addr_compat, 12) && \ + ((x)->s6_addr[12] != 0 || \ + (x)->s6_addr[13] != 0 || \ + (x)->s6_addr[14] != 0 || \ + ((x)->s6_addr[15] != 0 && \ + (x)->s6_addr[15] != 1))) +#endif +#ifndef IN6_IS_ADDR_V4MAPPED +#define IN6_IS_ADDR_V4MAPPED(x) (!memcmp((x)->s6_addr, in6addr_mapped, 12)) +#endif + +static const unsigned char in6addr_mapped[12] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff +}; + +/*** + *** Forward declarations. + ***/ + +static int +scan_interfaces(int *, int *); + +static struct hostent * +copyandmerge(struct hostent *, struct hostent *, int, int *); + +static struct hostent * +hostfromaddr(lwres_gnbaresponse_t *addr, int af, const void *src); + +static struct hostent * +hostfromname(lwres_gabnresponse_t *name, int af); + +/*** + *** 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 * +lwres_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 = NULL; + int v4 = 0, v6 = 0; + int tmp_err = 0; + lwres_context_t *lwrctx = NULL; + lwres_gabnresponse_t *by = NULL; + int n; + + /* + * 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 = lwres_net_pton(AF_INET, name, &in4)) != 1) + v6 = lwres_net_pton(AF_INET6, name, &in6); + + /* + * Impossible combination? + */ + if ((af == AF_INET6 && (flags & AI_V4MAPPED) == 0 && v4 == 1) || + (af == AF_INET && v6 == 1) || + (have_v4 == 0 && v4 == 1) || + (have_v6 == 0 && v6 == 1) || + (have_v4 == 0 && af == AF_INET) || + (have_v6 == 0 && af == AF_INET6 && + (((flags & AI_V4MAPPED) != 0 && have_v4) || + (flags & AI_V4MAPPED) == 0))) { + *error_num = HOST_NOT_FOUND; + return (NULL); + } + + /* + * Literal address? + */ + if (v4 == 1 || v6 == 1) { + char *addr_list[2]; + char *aliases[1]; + char mappedname[sizeof("::ffff:123.123.123.123")]; + union { + const char *const_name; + char *deconst_name; + } u; + + u.const_name = name; + if (v4 == 1 && af == AF_INET6) { + strcpy(mappedname, "::ffff:"); + lwres_net_ntop(AF_INET, (char *)&in4, + mappedname + sizeof("::ffff:") - 1, + sizeof(mappedname) - sizeof("::ffff:") + + 1); + he.h_name = mappedname; + } else + he.h_name = u.deconst_name; + he.h_addr_list = addr_list; + he.h_addr_list[0] = (v4 == 1) ? (char *)&in4 : (char *)&in6; + he.h_addr_list[1] = NULL; + he.h_aliases = aliases; + he.h_aliases[0] = NULL; + he.h_length = (v4 == 1) ? INADDRSZ : IN6ADDRSZ; + he.h_addrtype = (v4 == 1) ? AF_INET : AF_INET6; + return (copyandmerge(&he, NULL, af, error_num)); + } + + n = lwres_context_create(&lwrctx, NULL, NULL, NULL, 0); + if (n != 0) { + *error_num = NO_RECOVERY; + goto cleanup; + } + (void) lwres_conf_parse(lwrctx, lwres_resolv_conf); + tmp_err = NO_RECOVERY; + if (have_v6 && af == AF_INET6) { + n = lwres_getaddrsbyname(lwrctx, name, LWRES_ADDRTYPE_V6, &by); + if (n == 0) { + he1 = hostfromname(by, AF_INET6); + lwres_gabnresponse_free(lwrctx, &by); + if (he1 == NULL) { + *error_num = NO_RECOVERY; + goto cleanup; + } + } else { + if (n == LWRES_R_NOTFOUND) + tmp_err = HOST_NOT_FOUND; + else { + *error_num = NO_RECOVERY; + goto cleanup; + } + } + } + + if (have_v4 && + ((af == AF_INET) || + (af == AF_INET6 && (flags & AI_V4MAPPED) != 0 && + (he1 == NULL || (flags & AI_ALL) != 0)))) { + n = lwres_getaddrsbyname(lwrctx, name, LWRES_ADDRTYPE_V4, &by); + if (n == 0) { + he2 = hostfromname(by, AF_INET); + lwres_gabnresponse_free(lwrctx, &by); + if (he2 == NULL) { + *error_num = NO_RECOVERY; + goto cleanup; + } + } else if (he1 == NULL) { + if (n == LWRES_R_NOTFOUND) + *error_num = HOST_NOT_FOUND; + else + *error_num = NO_RECOVERY; + goto cleanup; + } + } else + *error_num = tmp_err; + + he3 = copyandmerge(he1, he2, af, error_num); + + cleanup: + if (he1 != NULL) + lwres_freehostent(he1); + if (he2 != NULL) + lwres_freehostent(he2); + if (lwrctx != NULL) { + lwres_conf_clear(lwrctx); + lwres_context_destroy(&lwrctx); + } + return (he3); +} + +/*% performs a reverse lookup of address src which is len bytes long. af denotes the protocol family, typically #PF_INET or PF_INET6. */ +struct hostent * +lwres_getipnodebyaddr(const void *src, size_t len, int af, int *error_num) { + struct hostent *he1, *he2; + lwres_context_t *lwrctx = NULL; + lwres_gnbaresponse_t *by = NULL; + lwres_result_t n; + union { + const void *konst; + struct in6_addr *in6; + } u; + + /* + * Sanity checks. + */ + if (src == NULL) { + *error_num = NO_RECOVERY; + return (NULL); + } + + switch (af) { + case AF_INET: + if (len != (unsigned int)INADDRSZ) { + *error_num = NO_RECOVERY; + return (NULL); + } + break; + case AF_INET6: + if (len != (unsigned int)IN6ADDRSZ) { + *error_num = NO_RECOVERY; + return (NULL); + } + break; + default: + *error_num = NO_RECOVERY; + return (NULL); + } + + /* + * The de-"const"-ing game is done because at least one + * vendor's system (RedHat 6.0) defines the IN6_IS_ADDR_* + * macros in such a way that they discard the const with + * internal casting, and gcc ends up complaining. Rather + * than replacing their own (possibly optimized) definitions + * with our own, cleanly discarding the const is the easiest + * thing to do. + */ + u.konst = src; + + /* + * Look up IPv4 and IPv4 mapped/compatible addresses. + */ + if ((af == AF_INET6 && IN6_IS_ADDR_V4COMPAT(u.in6)) || + (af == AF_INET6 && IN6_IS_ADDR_V4MAPPED(u.in6)) || + (af == AF_INET)) { + const unsigned char *cp = src; + + if (af == AF_INET6) + cp += 12; + n = lwres_context_create(&lwrctx, NULL, NULL, NULL, 0); + if (n == LWRES_R_SUCCESS) + (void) lwres_conf_parse(lwrctx, lwres_resolv_conf); + if (n == LWRES_R_SUCCESS) + n = lwres_getnamebyaddr(lwrctx, LWRES_ADDRTYPE_V4, + INADDRSZ, cp, &by); + if (n != LWRES_R_SUCCESS) { + lwres_conf_clear(lwrctx); + lwres_context_destroy(&lwrctx); + if (n == LWRES_R_NOTFOUND) + *error_num = HOST_NOT_FOUND; + else + *error_num = NO_RECOVERY; + return (NULL); + } + he1 = hostfromaddr(by, AF_INET, cp); + lwres_gnbaresponse_free(lwrctx, &by); + lwres_conf_clear(lwrctx); + lwres_context_destroy(&lwrctx); + if (af != AF_INET6) + return (he1); + + /* + * Convert from AF_INET to AF_INET6. + */ + he2 = copyandmerge(he1, NULL, af, error_num); + lwres_freehostent(he1); + if (he2 == NULL) + return (NULL); + /* + * Restore original address. + */ + memmove(he2->h_addr, src, len); + return (he2); + } + + /* + * Lookup IPv6 address. + */ + if (memcmp(src, &in6addr_any, IN6ADDRSZ) == 0) { + *error_num = HOST_NOT_FOUND; + return (NULL); + } + + n = lwres_context_create(&lwrctx, NULL, NULL, NULL, 0); + if (n == LWRES_R_SUCCESS) + (void) lwres_conf_parse(lwrctx, lwres_resolv_conf); + if (n == LWRES_R_SUCCESS) + n = lwres_getnamebyaddr(lwrctx, LWRES_ADDRTYPE_V6, IN6ADDRSZ, + src, &by); + if (n != 0) { + lwres_conf_clear(lwrctx); + lwres_context_destroy(&lwrctx); + + if (n == LWRES_R_NOTFOUND) + *error_num = HOST_NOT_FOUND; + else + *error_num = NO_RECOVERY; + + return (NULL); + } + + he1 = hostfromaddr(by, AF_INET6, src); + lwres_gnbaresponse_free(lwrctx, &by); + if (he1 == NULL) + *error_num = NO_RECOVERY; + lwres_conf_clear(lwrctx); + lwres_context_destroy(&lwrctx); + return (he1); +} + +/*% releases all the memory associated with the struct hostent pointer */ +void +lwres_freehostent(struct hostent *he) { + char **cpp; + int names = 1; + int addresses = 1; + + if (he == NULL) + return; + + free(he->h_name); + + cpp = he->h_addr_list; + while (*cpp != NULL) { + free(*cpp); + *cpp = NULL; + cpp++; + addresses++; + } + + cpp = he->h_aliases; + while (*cpp != NULL) { + free(*cpp); + cpp++; + names++; + } + + free(he->h_aliases); + free(he->h_addr_list); + free(he); +} + +/* + * Private + */ + +/* + * 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. + */ + +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \ + !defined(IRIX_EMUL_IOCTL_SIOCGIFCONF) + +#ifdef __hpux +#define lifc_len iflc_len +#define lifc_buf iflc_buf +#define lifc_req iflc_req +#define LIFCONF if_laddrconf +#else +#define ISC_HAVE_LIFC_FAMILY 1 +#define ISC_HAVE_LIFC_FLAGS 1 +#define LIFCONF lifconf +#endif + +#ifdef __hpux +#define lifr_addr iflr_addr +#define lifr_name iflr_name +#define lifr_dstaddr iflr_dstaddr +#define lifr_flags iflr_flags +#define ss_family sa_family +#define LIFREQ if_laddrreq +#else +#define LIFREQ lifreq +#endif + +static int +scan_interfaces6(int *have_v4, int *have_v6) { + struct LIFCONF lifc; + struct LIFREQ lifreq; + struct in_addr in4; + struct in6_addr in6; + char *buf = NULL, *cp, *cplim; + static unsigned int bufsiz = 4095; + int s, cpsize, n; + + /* + * Set to zero. Used as loop terminators below. + */ + *have_v4 = *have_v6 = 0; + + /* + * Get interface list from system. + */ + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) + goto err_ret; + + /* + * Grow buffer until large enough to contain all interface + * descriptions. + */ + for (;;) { + buf = malloc(bufsiz); + if (buf == NULL) + goto err_ret; +#ifdef ISC_HAVE_LIFC_FAMILY + lifc.lifc_family = AF_UNSPEC; /* request all families */ +#endif +#ifdef ISC_HAVE_LIFC_FLAGS + 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 err_ret; + + if (bufsiz > 1000000) + goto err_ret; + + free(buf); + 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) { + memmove(&lifreq, cp, sizeof(lifreq)); +#ifdef LWRES_PLATFORM_HAVESALEN +#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) { + memmove(&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) { + memmove(&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) + free(buf); + close(s); + return (0); + err_ret: + if (buf != NULL) + free(buf); + if (s != -1) + close(s); + return (-1); +} +#endif + +static int +scan_interfaces(int *have_v4, int *have_v6) { +#if !defined(SIOCGIFCONF) || !defined(SIOCGIFADDR) + *have_v4 = *have_v6 = 1; + return (0); +#else + 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; + +#ifdef WIN32 + InitSockets(); +#endif +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \ + !defined(IRIX_EMUL_IOCTL_SIOCGIFCONF) + /* + * Try to scan the interfaces using IPv6 ioctls(). + */ + if (!scan_interfaces6(have_v4, have_v6)) { +#ifdef WIN32 + DestroySockets(); +#endif + return (0); + } +#endif + + /* + * Set to zero. Used as loop terminators below. + */ + *have_v4 = *have_v6 = 0; + + /* + * 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 = malloc(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; + + free(buf); + 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) { + memmove(&u.ifreq, cp, sizeof(u.ifreq)); +#ifdef LWRES_PLATFORM_HAVESALEN +#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)) + memmove(&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) { + memmove(&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) { + memmove(&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) + free(buf); +#ifdef WIN32 + DestroySockets(); +#endif + close(s); + return (0); + + err_ret: + if (buf != NULL) + free(buf); + if (s != -1) + close(s); +#ifdef WIN32 + DestroySockets(); +#endif + return (-1); +#endif +} + +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++; + } + } + } + + if (addresses == 1) { + *error_num = NO_ADDRESS; + return (NULL); + } + + he = malloc(sizeof(*he)); + if (he == NULL) + goto no_recovery; + + he->h_addr_list = malloc(sizeof(char *) * (addresses)); + if (he->h_addr_list == NULL) + goto cleanup0; + memset(he->h_addr_list, 0, sizeof(char *) * (addresses)); + + /* + * Copy addresses. + */ + npp = he->h_addr_list; + if (he1 != NULL) { + cpp = he1->h_addr_list; + while (*cpp != NULL) { + *npp = malloc((af == AF_INET) ? INADDRSZ : IN6ADDRSZ); + if (*npp == NULL) + goto cleanup1; + /* + * Convert to mapped if required. + */ + if (af == AF_INET6 && he1->h_addrtype == AF_INET) { + memmove(*npp, in6addr_mapped, + sizeof(in6addr_mapped)); + memmove(*npp + sizeof(in6addr_mapped), *cpp, + INADDRSZ); + } else { + memmove(*npp, *cpp, + (af == AF_INET) ? INADDRSZ : IN6ADDRSZ); + } + cpp++; + npp++; + } + } + + if (he2 != NULL) { + cpp = he2->h_addr_list; + while (*cpp != NULL) { + *npp = malloc((af == AF_INET) ? INADDRSZ : IN6ADDRSZ); + if (*npp == NULL) + goto cleanup1; + /* + * Convert to mapped if required. + */ + if (af == AF_INET6 && he2->h_addrtype == AF_INET) { + memmove(*npp, in6addr_mapped, + sizeof(in6addr_mapped)); + memmove(*npp + sizeof(in6addr_mapped), *cpp, + INADDRSZ); + } else { + memmove(*npp, *cpp, + (af == AF_INET) ? INADDRSZ : IN6ADDRSZ); + } + cpp++; + npp++; + } + } + + he->h_aliases = malloc(sizeof(char *) * (names)); + if (he->h_aliases == NULL) + goto cleanup1; + memset(he->h_aliases, 0, sizeof(char *) * (names)); + + /* + * Copy aliases. + */ + npp = he->h_aliases; + cpp = (he1 != NULL) ? he1->h_aliases + : ((he2 != NULL) ? he2->h_aliases : NULL); + while (cpp != NULL && *cpp != NULL) { + len = strlen (*cpp) + 1; + *npp = malloc(len); + if (*npp == NULL) + goto cleanup2; + strcpy(*npp, *cpp); + npp++; + cpp++; + } + + /* + * Copy hostname. + */ + he->h_name = malloc(strlen((he1 != NULL) ? + he1->h_name : he2->h_name) + 1); + if (he->h_name == NULL) + goto cleanup2; + strcpy(he->h_name, (he1 != NULL) ? he1->h_name : he2->h_name); + + /* + * Set address type and length. + */ + he->h_addrtype = af; + he->h_length = (af == AF_INET) ? INADDRSZ : IN6ADDRSZ; + return (he); + + cleanup2: + cpp = he->h_aliases; + while (*cpp != NULL) { + free(*cpp); + cpp++; + } + free(he->h_aliases); + + cleanup1: + cpp = he->h_addr_list; + while (*cpp != NULL) { + free(*cpp); + *cpp = NULL; + cpp++; + } + free(he->h_addr_list); + + cleanup0: + free(he); + + no_recovery: + *error_num = NO_RECOVERY; + return (NULL); +} + +static struct hostent * +hostfromaddr(lwres_gnbaresponse_t *addr, int af, const void *src) { + struct hostent *he; + int i; + + he = malloc(sizeof(*he)); + if (he == NULL) + goto cleanup; + memset(he, 0, sizeof(*he)); + + /* + * Set family and length. + */ + he->h_addrtype = af; + switch (af) { + case AF_INET: + he->h_length = INADDRSZ; + break; + case AF_INET6: + he->h_length = IN6ADDRSZ; + break; + default: + INSIST(0); + } + + /* + * Copy name. + */ + he->h_name = strdup(addr->realname); + if (he->h_name == NULL) + goto cleanup; + + /* + * Copy aliases. + */ + he->h_aliases = malloc(sizeof(char *) * (addr->naliases + 1)); + if (he->h_aliases == NULL) + goto cleanup; + for (i = 0; i < addr->naliases; i++) { + he->h_aliases[i] = strdup(addr->aliases[i]); + if (he->h_aliases[i] == NULL) + goto cleanup; + } + he->h_aliases[i] = NULL; + + /* + * Copy address. + */ + he->h_addr_list = malloc(sizeof(char *) * 2); + if (he->h_addr_list == NULL) + goto cleanup; + he->h_addr_list[0] = malloc(he->h_length); + if (he->h_addr_list[0] == NULL) + goto cleanup; + memmove(he->h_addr_list[0], src, he->h_length); + he->h_addr_list[1] = NULL; + return (he); + + cleanup: + if (he != NULL && he->h_addr_list != NULL) { + for (i = 0; he->h_addr_list[i] != NULL; i++) + free(he->h_addr_list[i]); + free(he->h_addr_list); + } + if (he != NULL && he->h_aliases != NULL) { + for (i = 0; he->h_aliases[i] != NULL; i++) + free(he->h_aliases[i]); + free(he->h_aliases); + } + if (he != NULL && he->h_name != NULL) + free(he->h_name); + if (he != NULL) + free(he); + return (NULL); +} + +static struct hostent * +hostfromname(lwres_gabnresponse_t *name, int af) { + struct hostent *he; + int i; + lwres_addr_t *addr; + + he = malloc(sizeof(*he)); + if (he == NULL) + goto cleanup; + memset(he, 0, sizeof(*he)); + + /* + * Set family and length. + */ + he->h_addrtype = af; + switch (af) { + case AF_INET: + he->h_length = INADDRSZ; + break; + case AF_INET6: + he->h_length = IN6ADDRSZ; + break; + default: + INSIST(0); + } + + /* + * Copy name. + */ + he->h_name = strdup(name->realname); + if (he->h_name == NULL) + goto cleanup; + + /* + * Copy aliases. + */ + he->h_aliases = malloc(sizeof(char *) * (name->naliases + 1)); + if (he->h_aliases == NULL) + goto cleanup; + for (i = 0; i < name->naliases; i++) { + he->h_aliases[i] = strdup(name->aliases[i]); + if (he->h_aliases[i] == NULL) + goto cleanup; + } + he->h_aliases[i] = NULL; + + /* + * Copy addresses. + */ + he->h_addr_list = malloc(sizeof(char *) * (name->naddrs + 1)); + if (he->h_addr_list == NULL) + goto cleanup; + addr = LWRES_LIST_HEAD(name->addrs); + i = 0; + while (addr != NULL) { + he->h_addr_list[i] = malloc(he->h_length); + if (he->h_addr_list[i] == NULL) + goto cleanup; + memmove(he->h_addr_list[i], addr->address, he->h_length); + addr = LWRES_LIST_NEXT(addr, link); + i++; + } + he->h_addr_list[i] = NULL; + return (he); + + cleanup: + if (he != NULL && he->h_addr_list != NULL) { + for (i = 0; he->h_addr_list[i] != NULL; i++) + free(he->h_addr_list[i]); + free(he->h_addr_list); + } + if (he != NULL && he->h_aliases != NULL) { + for (i = 0; he->h_aliases[i] != NULL; i++) + free(he->h_aliases[i]); + free(he->h_aliases); + } + if (he != NULL && he->h_name != NULL) + free(he->h_name); + if (he != NULL) + free(he); + return (NULL); +} |