/* * 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 \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 #include #include #include #include #include #include #include /* XXX #include */ #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); }