diff options
Diffstat (limited to 'src/include/fake-addrinfo.h')
-rw-r--r-- | src/include/fake-addrinfo.h | 1183 |
1 files changed, 24 insertions, 1159 deletions
diff --git a/src/include/fake-addrinfo.h b/src/include/fake-addrinfo.h index 9ed8d406e..8e063c4f4 100644 --- a/src/include/fake-addrinfo.h +++ b/src/include/fake-addrinfo.h @@ -42,10 +42,10 @@ /* Approach overview: If a system version is available but buggy, save handles to it (via - inline functions), redefine the names to refer to static functions - defined here, and in those functions, call the system versions and - fix up the returned data. Use the native data structures and flag - values. + inline functions in a support library), redefine the names to refer + to library functions, and in those functions, call the system + versions and fix up the returned data. Use the native data + structures and flag values. If no system version exists, use gethostby* and fake it. Define the data structures and flag values locally. @@ -101,248 +101,9 @@ #define FAI_DEFINED #include "port-sockets.h" #include "socket-utils.h" -#include "k5-platform.h" -#include "k5-thread.h" - -#include <stdio.h> /* for sprintf */ -#include <errno.h> - -#ifdef S_SPLINT_S -/*@-incondefs@*/ -extern int -getaddrinfo (/*@in@*/ /*@null@*/ const char *, - /*@in@*/ /*@null@*/ const char *, - /*@in@*/ /*@null@*/ const struct addrinfo *, - /*@out@*/ struct addrinfo **) - ; -extern void -freeaddrinfo (/*@only@*/ /*@out@*/ struct addrinfo *) - ; -extern int -getnameinfo (const struct sockaddr *addr, socklen_t addrsz, - /*@out@*/ /*@null@*/ char *h, socklen_t hsz, - /*@out@*/ /*@null@*/ char *s, socklen_t ssz, - int flags) - /*@requires (maxSet(h)+1) >= hsz /\ (maxSet(s)+1) >= ssz @*/ - /* too hard: maxRead(addr) >= (addrsz-1) */ - /*@modifies *h, *s@*/; -extern /*@dependent@*/ char *gai_strerror (int code) /*@*/; -/*@=incondefs@*/ -#endif - - -#if defined (__APPLE__) && defined (__MACH__) -#define FAI_CACHE -#endif - -#if (defined (__linux__) && defined(HAVE_GETADDRINFO)) || defined (_AIX) -/* See comments below. */ -# define WRAP_GETADDRINFO -#endif - -#if defined (__linux__) && defined(HAVE_GETADDRINFO) -# define COPY_FIRST_CANONNAME -#endif - -#ifdef _AIX -# define NUMERIC_SERVICE_BROKEN -# define COPY_FIRST_CANONNAME -#endif - - -#ifdef COPY_FIRST_CANONNAME -# include <string.h> -#endif - -#ifdef NUMERIC_SERVICE_BROKEN -# include <ctype.h> /* isdigit */ -# include <stdlib.h> /* strtoul */ -#endif - -#ifdef _WIN32 -#define HAVE_GETADDRINFO 1 -#define HAVE_GETNAMEINFO 1 -#endif - - -/* Do we actually have *any* systems we care about that don't provide - either getaddrinfo or one of these two flavors of - gethostbyname_r? */ -#if !defined(HAVE_GETHOSTBYNAME_R) || defined(THREADSAFE_GETHOSTBYNAME) -#define GET_HOST_BY_NAME(NAME, HP, ERR) \ - { (HP) = gethostbyname (NAME); (ERR) = h_errno; } -#define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR) \ - { (HP) = gethostbyaddr ((ADDR), (ADDRLEN), (FAMILY)); (ERR) = h_errno; } -#else -#ifdef _AIX /* XXX should have a feature test! */ -#define GET_HOST_BY_NAME(NAME, HP, ERR) \ - { \ - struct hostent my_h_ent; \ - struct hostent_data my_h_ent_data; \ - (HP) = (gethostbyname_r((NAME), &my_h_ent, &my_h_ent_data) \ - ? 0 \ - : &my_h_ent); \ - (ERR) = h_errno; \ - } -/* -#define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR) \ - { \ - struct hostent my_h_ent; \ - struct hostent_data my_h_ent_data; \ - (HP) = (gethostbyaddr_r((ADDR), (ADDRLEN), (FAMILY), &my_h_ent, \ - &my_h_ent_data) \ - ? 0 \ - : &my_h_ent); \ - (ERR) = my_h_err; \ - } -*/ -#else -#ifdef GETHOSTBYNAME_R_RETURNS_INT -#define GET_HOST_BY_NAME(NAME, HP, ERR) \ - { \ - struct hostent my_h_ent, *my_hp; \ - int my_h_err; \ - char my_h_buf[8192]; \ - (HP) = (gethostbyname_r((NAME), &my_h_ent, \ - my_h_buf, sizeof (my_h_buf), &my_hp, \ - &my_h_err) \ - ? 0 \ - : &my_h_ent); \ - (ERR) = my_h_err; \ - } -#define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR) \ - { \ - struct hostent my_h_ent, *my_hp; \ - int my_h_err; \ - char my_h_buf[8192]; \ - (HP) = (gethostbyaddr_r((ADDR), (ADDRLEN), (FAMILY), &my_h_ent, \ - my_h_buf, sizeof (my_h_buf), &my_hp, \ - &my_h_err) \ - ? 0 \ - : &my_h_ent); \ - (ERR) = my_h_err; \ - } -#else -#define GET_HOST_BY_NAME(NAME, HP, ERR) \ - { \ - struct hostent my_h_ent; \ - int my_h_err; \ - char my_h_buf[8192]; \ - (HP) = gethostbyname_r((NAME), &my_h_ent, \ - my_h_buf, sizeof (my_h_buf), &my_h_err); \ - (ERR) = my_h_err; \ - } -#define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR) \ - { \ - struct hostent my_h_ent; \ - int my_h_err; \ - char my_h_buf[8192]; \ - (HP) = gethostbyaddr_r((ADDR), (ADDRLEN), (FAMILY), &my_h_ent, \ - my_h_buf, sizeof (my_h_buf), &my_h_err); \ - (ERR) = my_h_err; \ - } -#endif /* returns int? */ -#endif /* _AIX */ -#endif - -/* Now do the same for getservby* functions. */ -#ifndef HAVE_GETSERVBYNAME_R -#define GET_SERV_BY_NAME(NAME, PROTO, SP, ERR) \ - ((SP) = getservbyname (NAME, PROTO), (ERR) = (SP) ? 0 : -1) -#define GET_SERV_BY_PORT(PORT, PROTO, SP, ERR) \ - ((SP) = getservbyport (PORT, PROTO), (ERR) = (SP) ? 0 : -1) -#else -#ifdef GETSERVBYNAME_R_RETURNS_INT -#define GET_SERV_BY_NAME(NAME, PROTO, SP, ERR) \ - { \ - struct servent my_s_ent, *my_sp; \ - int my_s_err; \ - char my_s_buf[8192]; \ - (SP) = (getservbyname_r((NAME), (PROTO), &my_s_ent, \ - my_s_buf, sizeof (my_s_buf), &my_sp, \ - &my_s_err) \ - ? 0 \ - : &my_s_ent); \ - (ERR) = my_s_err; \ - } -#define GET_SERV_BY_PORT(PORT, PROTO, SP, ERR) \ - { \ - struct servent my_s_ent, *my_sp; \ - int my_s_err; \ - char my_s_buf[8192]; \ - (SP) = (getservbyport_r((PORT), (PROTO), &my_s_ent, \ - my_s_buf, sizeof (my_s_buf), &my_sp, \ - &my_s_err) \ - ? 0 \ - : &my_s_ent); \ - (ERR) = my_s_err; \ - } -#else -/* returns ptr -- IRIX? */ -#define GET_SERV_BY_NAME(NAME, PROTO, SP, ERR) \ - { \ - struct servent my_s_ent; \ - char my_s_buf[8192]; \ - (SP) = getservbyname_r((NAME), (PROTO), &my_s_ent, \ - my_s_buf, sizeof (my_s_buf)); \ - (ERR) = (SP) == NULL; \ - } - -#define GET_SERV_BY_PORT(PORT, PROTO, SP, ERR) \ - { \ - struct servent my_s_ent, *my_sp; \ - char my_s_buf[8192]; \ - my_sp = getservbyport_r((PORT), (PROTO), &my_s_ent, \ - my_s_buf, sizeof (my_s_buf)); \ - (SP) = my_sp; \ - (ERR) = my_sp == 0; \ - (ERR) = (ERR); /* avoid "unused" warning */ \ - } -#endif -#endif - -#if defined(WRAP_GETADDRINFO) || defined(FAI_CACHE) -static inline int -system_getaddrinfo (const char *name, const char *serv, - const struct addrinfo *hint, - struct addrinfo **res) -{ - return getaddrinfo(name, serv, hint, res); -} - -static inline void -system_freeaddrinfo (struct addrinfo *ai) -{ - freeaddrinfo(ai); -} - -/* Note: Implementations written to RFC 2133 use size_t, while RFC - 2553 implementations use socklen_t, for the second parameter. - - Mac OS X (10.2) and AIX 4.3.3 appear to be in the RFC 2133 camp, - but we don't have an autoconf test for that right now. */ -static inline int -system_getnameinfo (const struct sockaddr *sa, socklen_t salen, - char *host, size_t hostlen, char *serv, size_t servlen, - int flags) -{ - return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); -} -#endif - -#if !defined (HAVE_GETADDRINFO) || defined(WRAP_GETADDRINFO) || defined(FAI_CACHE) - -#undef getaddrinfo -#define getaddrinfo my_fake_getaddrinfo -#undef freeaddrinfo -#define freeaddrinfo my_fake_freeaddrinfo - -#endif #if !defined (HAVE_GETADDRINFO) -#undef gai_strerror -#define gai_strerror my_fake_gai_strerror #undef addrinfo #define addrinfo my_fake_addrinfo @@ -420,144 +181,6 @@ struct addrinfo { #endif /* ! HAVE_GETADDRINFO */ -#if (!defined (HAVE_GETADDRINFO) || defined (WRAP_GETADDRINFO)) && defined(DEBUG_ADDRINFO) -/* Some debug routines. */ - -static const char *protoname (int p, char *buf) { -#define X(N) if (p == IPPROTO_ ## N) return #N - - X(TCP); - X(UDP); - X(ICMP); - X(IPV6); -#ifdef IPPROTO_GRE - X(GRE); -#endif - X(NONE); - X(RAW); -#ifdef IPPROTO_COMP - X(COMP); -#endif - - sprintf(buf, " %-2d", p); - return buf; -} - -static const char *socktypename (int t, char *buf) { - switch (t) { - case SOCK_DGRAM: return "DGRAM"; - case SOCK_STREAM: return "STREAM"; - case SOCK_RAW: return "RAW"; - case SOCK_RDM: return "RDM"; - case SOCK_SEQPACKET: return "SEQPACKET"; - } - sprintf(buf, " %-2d", t); - return buf; -} - -static const char *familyname (int f, char *buf) { - switch (f) { - default: - sprintf(buf, "AF %d", f); - return buf; - case AF_INET: return "AF_INET"; - case AF_INET6: return "AF_INET6"; -#ifdef AF_UNIX - case AF_UNIX: return "AF_UNIX"; -#endif - } -} - -static void debug_dump_getaddrinfo_args (const char *name, const char *serv, - const struct addrinfo *hint) -{ - const char *sep; - fprintf(stderr, - "getaddrinfo(hostname %s, service %s,\n" - " hints { ", - name ? name : "(null)", serv ? serv : "(null)"); - if (hint) { - char buf[30]; - sep = ""; -#define Z(FLAG) if (hint->ai_flags & AI_##FLAG) fprintf(stderr, "%s%s", sep, #FLAG), sep = "|" - Z(CANONNAME); - Z(PASSIVE); -#ifdef AI_NUMERICHOST - Z(NUMERICHOST); -#endif - if (sep[0] == 0) - fprintf(stderr, "no-flags"); - if (hint->ai_family) - fprintf(stderr, " %s", familyname(hint->ai_family, buf)); - if (hint->ai_socktype) - fprintf(stderr, " SOCK_%s", socktypename(hint->ai_socktype, buf)); - if (hint->ai_protocol) - fprintf(stderr, " IPPROTO_%s", protoname(hint->ai_protocol, buf)); - } else - fprintf(stderr, "(null)"); - fprintf(stderr, " }):\n"); -} - -static void debug_dump_error (int err) -{ - fprintf(stderr, "error %d: %s\n", err, gai_strerror(err)); -} - -static void debug_dump_addrinfos (const struct addrinfo *ai) -{ - int count = 0; - fprintf(stderr, "addrinfos returned:\n"); - while (ai) { - fprintf(stderr, "%p...", ai); - fprintf(stderr, " socktype=%s", socktypename(ai->ai_socktype)); - fprintf(stderr, " ai_family=%s", familyname(ai->ai_family)); - if (ai->ai_family != ai->ai_addr->sa_family) - fprintf(stderr, " sa_family=%s", - familyname(ai->ai_addr->sa_family)); - fprintf(stderr, "\n"); - ai = ai->ai_next; - count++; - } - fprintf(stderr, "end addrinfos returned (%d)\n"); -} - -#endif - -#if !defined (HAVE_GETADDRINFO) || defined (WRAP_GETADDRINFO) - -static -int getaddrinfo (const char *name, const char *serv, - const struct addrinfo *hint, struct addrinfo **result); - -static -void freeaddrinfo (struct addrinfo *ai); - -#endif - -#if !defined (HAVE_GETADDRINFO) - -#define HAVE_FAKE_GETADDRINFO /* was not originally HAVE_GETADDRINFO */ -#define HAVE_GETADDRINFO -#define NEED_FAKE_GETNAMEINFO -#undef HAVE_GETNAMEINFO -#define HAVE_GETNAMEINFO 1 - -#undef getnameinfo -#define getnameinfo my_fake_getnameinfo - -static -char *gai_strerror (int code); - -#endif - -#if !defined (HAVE_GETADDRINFO) -static -int getnameinfo (const struct sockaddr *addr, socklen_t len, - char *host, socklen_t hostlen, - char *service, socklen_t servicelen, - int flags); -#endif - /* Fudge things on older gai implementations. */ /* AIX 4.3.3 is based on RFC 2133; no AI_NUMERICHOST. */ #ifndef AI_NUMERICHOST @@ -579,790 +202,32 @@ int getnameinfo (const struct sockaddr *addr, socklen_t len, # define AI_DEFAULT (AI_ADDRCONFIG|AI_V4MAPPED) #endif -#if defined(HAVE_FAKE_GETADDRINFO) || defined(FAI_CACHE) -#define NEED_FAKE_GETADDRINFO -#endif - -#if defined(NEED_FAKE_GETADDRINFO) || defined(WRAP_GETADDRINFO) -#include <stdlib.h> -#endif - -struct face { - struct in_addr *addrs4; - struct in6_addr *addrs6; - unsigned int naddrs4, naddrs6; - time_t expiration; - char *canonname, *name; - struct face *next; -}; - -/* fake addrinfo cache */ -struct fac { - k5_mutex_t lock; - struct face *data; -}; -extern struct fac krb5int_fac; - -#ifdef NEED_FAKE_GETADDRINFO -#include <string.h> /* for strspn */ - -static inline int translate_h_errno (int h); - -static inline int fai_add_entry (struct addrinfo **result, void *addr, - int port, const struct addrinfo *template) -{ - struct addrinfo *n = malloc (sizeof (struct addrinfo)); - if (n == 0) - return EAI_MEMORY; - if (template->ai_family != AF_INET -#ifdef KRB5_USE_INET6 - && template->ai_family != AF_INET6 -#endif - ) - return EAI_FAMILY; - *n = *template; - if (template->ai_family == AF_INET) { - struct sockaddr_in *sin4; - sin4 = malloc (sizeof (struct sockaddr_in)); - if (sin4 == 0) - return EAI_MEMORY; - n->ai_addr = (struct sockaddr *) sin4; - sin4->sin_family = AF_INET; - sin4->sin_addr = *(struct in_addr *)addr; - sin4->sin_port = port; -#ifdef HAVE_SA_LEN - sin4->sin_len = sizeof (struct sockaddr_in); -#endif - } -#ifdef KRB5_USE_INET6 - if (template->ai_family == AF_INET6) { - struct sockaddr_in6 *sin6; - sin6 = malloc (sizeof (struct sockaddr_in6)); - if (sin6 == 0) - return EAI_MEMORY; - n->ai_addr = (struct sockaddr *) sin6; - sin6->sin6_family = AF_INET6; - sin6->sin6_addr = *(struct in6_addr *)addr; - sin6->sin6_port = port; -#ifdef HAVE_SA_LEN - sin6->sin6_len = sizeof (struct sockaddr_in6); -#endif - } -#endif - n->ai_next = *result; - *result = n; - return 0; -} - -#ifdef FAI_CACHE -/* fake addrinfo cache entries */ -#define CACHE_ENTRY_LIFETIME 15 /* seconds */ - -static void plant_face (const char *name, struct face *entry) -{ - entry->name = strdup(name); - if (entry->name == NULL) - /* @@ Wastes memory. */ - return; - k5_mutex_assert_locked(&krb5int_fac.lock); - entry->next = krb5int_fac.data; - entry->expiration = time(0) + CACHE_ENTRY_LIFETIME; - krb5int_fac.data = entry; -#ifdef DEBUG_ADDRINFO - printf("added cache entry '%s' at %p: %d ipv4, %d ipv6; expire %d\n", - name, entry, entry->naddrs4, entry->naddrs6, entry->expiration); -#endif -} - -static int find_face (const char *name, struct face **entry) -{ - struct face *fp, **fpp; - time_t now = time(0); - - /* First, scan for expired entries and free them. - (Future improvement: Integrate these two loops.) */ -#ifdef DEBUG_ADDRINFO - printf("scanning cache at %d for '%s'...\n", now, name); -#endif - k5_mutex_assert_locked(&krb5int_fac.lock); - for (fpp = &krb5int_fac.data; *fpp; ) { - fp = *fpp; -#ifdef DEBUG_ADDRINFO - printf(" checking expiration time of @%p: %d\n", - fp, fp->expiration); -#endif - if (fp->expiration < now) { -#ifdef DEBUG_ADDRINFO - printf("\texpiring cache entry\n"); -#endif - free(fp->name); - free(fp->canonname); - free(fp->addrs4); - free(fp->addrs6); - *fpp = fp->next; - free(fp); - /* Stay at this point in the list, and check again. */ - } else - /* Move forward. */ - fpp = &(*fpp)->next; - } - - for (fp = krb5int_fac.data; fp; fp = fp->next) { -#ifdef DEBUG_ADDRINFO - printf(" comparing entry @%p\n", fp); -#endif - if (!strcasecmp(fp->name, name)) { -#ifdef DEBUG_ADDRINFO - printf("\tMATCH!\n"); -#endif - *entry = fp; - return 1; - } - } - return 0; -} - -#endif - -extern int krb5int_lock_fac(void), krb5int_unlock_fac(void); - -static inline int fai_add_hosts_by_name (const char *name, - struct addrinfo *template, - int portnum, int flags, - struct addrinfo **result) -{ -#ifdef FAI_CACHE - - struct face *ce; - int i, r, err; - - err = krb5int_lock_fac(); - if (err) { - errno = err; - return EAI_SYSTEM; - } - if (!find_face(name, &ce)) { - struct addrinfo myhints = { 0 }, *ai, *ai2; - int i4, i6, aierr; - -#ifdef DEBUG_ADDRINFO - printf("looking up new data for '%s'...\n", name); -#endif - myhints.ai_socktype = SOCK_STREAM; - myhints.ai_flags = AI_CANONNAME; - /* Don't set ai_family -- we want to cache all address types, - because the next lookup may not use the same constraints as - the current one. We *could* cache them separately, so that - we never have to look up an IPv6 address if we are always - asked for IPv4 only, but let's deal with that later, if we - have to. */ - aierr = system_getaddrinfo(name, "telnet", &myhints, &ai); - if (aierr) { - krb5int_unlock_fac(); - return aierr; - } - ce = malloc(sizeof(struct face)); - memset(ce, 0, sizeof(*ce)); - ce->expiration = time(0) + 30; - for (ai2 = ai; ai2; ai2 = ai2->ai_next) { -#ifdef DEBUG_ADDRINFO - printf(" found an address in family %d...\n", ai2->ai_family); -#endif - switch (ai2->ai_family) { - case AF_INET: - ce->naddrs4++; - break; - case AF_INET6: - ce->naddrs6++; - break; - default: - break; - } - } - ce->addrs4 = calloc(ce->naddrs4, sizeof(*ce->addrs4)); - if (ce->addrs4 == NULL && ce->naddrs4 != 0) { - krb5int_unlock_fac(); - system_freeaddrinfo(ai); - return EAI_MEMORY; - } - ce->addrs6 = calloc(ce->naddrs6, sizeof(*ce->addrs6)); - if (ce->addrs6 == NULL && ce->naddrs6 != 0) { - krb5int_unlock_fac(); - free(ce->addrs4); - system_freeaddrinfo(ai); - return EAI_MEMORY; - } - for (ai2 = ai, i4 = i6 = 0; ai2; ai2 = ai2->ai_next) { - switch (ai2->ai_family) { - case AF_INET: - ce->addrs4[i4++] = ((struct sockaddr_in *)ai2->ai_addr)->sin_addr; - break; - case AF_INET6: - ce->addrs6[i6++] = ((struct sockaddr_in6 *)ai2->ai_addr)->sin6_addr; - break; - default: - break; - } - } - ce->canonname = ai->ai_canonname ? strdup(ai->ai_canonname) : 0; - system_freeaddrinfo(ai); - plant_face(name, ce); - } - template->ai_family = AF_INET6; - template->ai_addrlen = sizeof(struct sockaddr_in6); - for (i = 0; i < ce->naddrs6; i++) { - r = fai_add_entry (result, &ce->addrs6[i], portnum, template); - if (r) { - krb5int_unlock_fac(); - return r; - } - } - template->ai_family = AF_INET; - template->ai_addrlen = sizeof(struct sockaddr_in); - for (i = 0; i < ce->naddrs4; i++) { - r = fai_add_entry (result, &ce->addrs4[i], portnum, template); - if (r) { - krb5int_unlock_fac(); - return r; - } - } - if (*result && (flags & AI_CANONNAME)) - (*result)->ai_canonname = (ce->canonname - ? strdup(ce->canonname) - : NULL); - krb5int_unlock_fac(); - return 0; - -#else - - struct hostent *hp; - int i, r; - int herr; - - GET_HOST_BY_NAME (name, hp, herr); - if (hp == 0) - return translate_h_errno (herr); - for (i = 0; hp->h_addr_list[i]; i++) { - r = fai_add_entry (result, hp->h_addr_list[i], portnum, template); - if (r) - return r; - } - if (*result && (flags & AI_CANONNAME)) - (*result)->ai_canonname = strdup (hp->h_name); - return 0; - -#endif -} - -static inline void -fake_freeaddrinfo (struct addrinfo *ai) -{ - struct addrinfo *next; - while (ai) { - next = ai->ai_next; - if (ai->ai_canonname) - free (ai->ai_canonname); - if (ai->ai_addr) - free (ai->ai_addr); - free (ai); - ai = next; - } -} - -static inline int -fake_getaddrinfo (const char *name, const char *serv, - const struct addrinfo *hint, struct addrinfo **result) -{ - struct addrinfo *res = 0; - int ret; - int port = 0, socktype; - int flags; - struct addrinfo template; - -#ifdef DEBUG_ADDRINFO - debug_dump_getaddrinfo_args(name, serv, hint); -#endif - - if (hint != 0) { - if (hint->ai_family != 0 && hint->ai_family != AF_INET) - return EAI_NODATA; - socktype = hint->ai_socktype; - flags = hint->ai_flags; - } else { - socktype = 0; - flags = 0; - } - - if (serv) { - size_t numlen = strspn (serv, "0123456789"); - if (serv[numlen] == '\0') { - /* pure numeric */ - unsigned long p = strtoul (serv, 0, 10); - if (p == 0 || p > 65535) - return EAI_NONAME; - port = htons (p); - } else { - struct servent *sp; - int try_dgram_too = 0, s_err; - - if (socktype == 0) { - try_dgram_too = 1; - socktype = SOCK_STREAM; - } - try_service_lookup: - GET_SERV_BY_NAME(serv, socktype == SOCK_STREAM ? "tcp" : "udp", - sp, s_err); - if (sp == 0) { - if (try_dgram_too) { - socktype = SOCK_DGRAM; - goto try_service_lookup; - } - return EAI_SERVICE; - } - port = sp->s_port; - } - } - - if (name == 0) { - name = (flags & AI_PASSIVE) ? "0.0.0.0" : "127.0.0.1"; - flags |= AI_NUMERICHOST; - } - - template.ai_family = AF_INET; - template.ai_addrlen = sizeof (struct sockaddr_in); - template.ai_socktype = socktype; - template.ai_protocol = 0; - template.ai_flags = 0; - template.ai_canonname = 0; - template.ai_next = 0; - template.ai_addr = 0; - - /* If NUMERICHOST is set, parse a numeric address. - If it's not set, don't accept such names. */ - if (flags & AI_NUMERICHOST) { - struct in_addr addr4; -#if 0 - ret = inet_aton (name, &addr4); - if (ret) - return EAI_NONAME; -#else - addr4.s_addr = inet_addr (name); - if (addr4.s_addr == 0xffffffff || addr4.s_addr == -1) - /* 255.255.255.255 or parse error, both bad */ - return EAI_NONAME; -#endif - ret = fai_add_entry (&res, &addr4, port, &template); - } else { - ret = fai_add_hosts_by_name (name, &template, port, flags, - &res); - } - - if (ret && ret != NO_ADDRESS) { - fake_freeaddrinfo (res); - return ret; - } - if (res == 0) - return NO_ADDRESS; - *result = res; - return 0; -} - -#ifdef NEED_FAKE_GETNAMEINFO -static inline int -fake_getnameinfo (const struct sockaddr *sa, socklen_t len, - char *host, socklen_t hostlen, - char *service, socklen_t servicelen, - int flags) -{ - struct hostent *hp; - const struct sockaddr_in *sinp; - struct servent *sp; - size_t hlen, slen; - - if (sa->sa_family != AF_INET) { - return EAI_FAMILY; - } - sinp = (const struct sockaddr_in *) sa; - - hlen = hostlen; - if (hostlen < 0 || hlen != hostlen) { - errno = EINVAL; - return EAI_SYSTEM; - } - slen = servicelen; - if (servicelen < 0 || slen != servicelen) { - errno = EINVAL; - return EAI_SYSTEM; - } - - if (host) { - if (flags & NI_NUMERICHOST) { -#if (defined(__GNUC__) && defined(__mips__)) || 1 /* thread safety always */ - /* The inet_ntoa call, passing a struct, fails on IRIX 6.5 - using gcc 2.95; we get back "0.0.0.0". Since this in a - configuration still important at Athena, here's the - workaround, which also happens to be thread-safe.... */ - const unsigned char *uc; - char tmpbuf[20]; - numeric_host: - uc = (const unsigned char *) &sinp->sin_addr; - sprintf(tmpbuf, "%d.%d.%d.%d", uc[0], uc[1], uc[2], uc[3]); - strncpy(host, tmpbuf, hlen); -#else - char *p; - numeric_host: - p = inet_ntoa (sinp->sin_addr); - strncpy (host, p, hlen); -#endif - } else { - int herr; - GET_HOST_BY_ADDR((const char *) &sinp->sin_addr, - sizeof (struct in_addr), - sa->sa_family, hp, herr); - if (hp == 0) { - if (herr == NO_ADDRESS && !(flags & NI_NAMEREQD)) /* ??? */ - goto numeric_host; - return translate_h_errno (herr); - } - /* According to the Open Group spec, getnameinfo can - silently truncate, but must still return a - null-terminated string. */ - strncpy (host, hp->h_name, hlen); - } - host[hostlen-1] = 0; - } - - if (service) { - if (flags & NI_NUMERICSERV) { - char numbuf[10]; - int port; - numeric_service: - port = ntohs (sinp->sin_port); - if (port < 0 || port > 65535) - return EAI_FAIL; - sprintf (numbuf, "%d", port); - strncpy (service, numbuf, slen); - } else { - int serr; - GET_SERV_BY_PORT(sinp->sin_port, - (flags & NI_DGRAM) ? "udp" : "tcp", - sp, serr); - if (sp == 0) - goto numeric_service; - strncpy (service, sp->s_name, slen); - } - service[servicelen-1] = 0; - } - - return 0; -} -#endif - -#if defined(HAVE_FAKE_GETADDRINFO) || defined(NEED_FAKE_GETNAMEINFO) - -static inline -char *gai_strerror (int code) -{ - switch (code) { - case EAI_ADDRFAMILY: return "address family for nodename not supported"; - case EAI_AGAIN: return "temporary failure in name resolution"; - case EAI_BADFLAGS: return "bad flags to getaddrinfo/getnameinfo"; - case EAI_FAIL: return "non-recoverable failure in name resolution"; - case EAI_FAMILY: return "ai_family not supported"; - case EAI_MEMORY: return "out of memory"; - case EAI_NODATA: return "no address associated with hostname"; - case EAI_NONAME: return "name does not exist"; - case EAI_SERVICE: return "service name not supported for specified socket type"; - case EAI_SOCKTYPE: return "ai_socktype not supported"; - case EAI_SYSTEM: return strerror (errno); - default: return "bogus getaddrinfo error?"; - } -} -#endif - -static inline int translate_h_errno (int h) -{ - switch (h) { - case 0: - return 0; -#ifdef NETDB_INTERNAL - case NETDB_INTERNAL: - if (errno == ENOMEM) - return EAI_MEMORY; - return EAI_SYSTEM; -#endif - case HOST_NOT_FOUND: - return EAI_NONAME; - case TRY_AGAIN: - return EAI_AGAIN; - case NO_RECOVERY: - return EAI_FAIL; - case NO_DATA: -#if NO_DATA != NO_ADDRESS - case NO_ADDRESS: -#endif - return EAI_NODATA; - default: - return EAI_SYSTEM; - } -} - -#if defined(HAVE_FAKE_GETADDRINFO) || defined(FAI_CACHE) -static inline -int getaddrinfo (const char *name, const char *serv, - const struct addrinfo *hint, struct addrinfo **result) -{ - return fake_getaddrinfo(name, serv, hint, result); -} - -static inline -void freeaddrinfo (struct addrinfo *ai) -{ - fake_freeaddrinfo(ai); -} - -#ifdef NEED_FAKE_GETNAMEINFO -static inline -int getnameinfo (const struct sockaddr *sa, socklen_t len, - char *host, socklen_t hostlen, - char *service, socklen_t servicelen, - int flags) -{ - return fake_getnameinfo(sa, len, host, hostlen, service, servicelen, - flags); -} -#endif /* NEED_FAKE_GETNAMEINFO */ -#endif /* HAVE_FAKE_GETADDRINFO */ -#endif /* NEED_FAKE_GETADDRINFO */ - - -#ifdef WRAP_GETADDRINFO - -static inline -int -getaddrinfo (const char *name, const char *serv, const struct addrinfo *hint, - struct addrinfo **result) -{ - int aierr; -#if defined(_AIX) || defined(COPY_FIRST_CANONNAME) - struct addrinfo *ai; -#endif -#ifdef NUMERIC_SERVICE_BROKEN - int service_is_numeric = 0; - int service_port = 0; - int socket_type = 0; -#endif - -#ifdef DEBUG_ADDRINFO - debug_dump_getaddrinfo_args(name, serv, hint); -#endif - -#ifdef NUMERIC_SERVICE_BROKEN - /* AIX 4.3.3 is broken. (Or perhaps out of date?) - - If a numeric service is provided, and it doesn't correspond to - a known service name for tcp or udp (as appropriate), an error - code (for "host not found") is returned. If the port maps to a - known service for both udp and tcp, all is well. */ - if (serv && serv[0] && isdigit(serv[0])) { - unsigned long lport; - char *end; - lport = strtoul(serv, &end, 10); - if (!*end) { - if (lport > 65535) - return EAI_SOCKTYPE; - service_is_numeric = 1; - service_port = htons(lport); - serv = "discard"; /* defined for both udp and tcp */ - if (hint) - socket_type = hint->ai_socktype; - } - } -#endif - - aierr = system_getaddrinfo (name, serv, hint, result); - if (aierr || *result == 0) { -#ifdef DEBUG_ADDRINFO - debug_dump_error(aierr); -#endif - return aierr; - } - - /* Linux libc version 6 (libc-2.2.4.so on Debian) is broken. - - RFC 2553 says that when AI_CANONNAME is set, the ai_canonname - flag of the first returned structure has the canonical name of - the host. Instead, GNU libc sets ai_canonname in each returned - structure to the name that the corresponding address maps to, - if any, or a printable numeric form. - - RFC 2553 bis and the new Open Group spec say that field will be - the canonical name if it can be determined, otherwise, the - provided hostname or a copy of it. - - IMNSHO, "canonical name" means CNAME processing and not PTR - processing, but I can see arguing it. Using the numeric form - when that's not the form provided is just wrong. So, let's fix - it. - - The glibc 2.2.5 sources indicate that the canonical name is - *not* allocated separately, it's just some extra storage tacked - on the end of the addrinfo structure. So, let's try this - approach: If getaddrinfo sets ai_canonname, we'll replace the - *first* one with allocated storage, and free up that pointer in - freeaddrinfo if it's set; the other ai_canonname fields will be - left untouched. And we'll just pray that the application code - won't mess around with the list structure; if we start doing - that, we'll have to start replacing and freeing all of the - ai_canonname fields. - - Ref: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=133668 . - - Since it's dependent on the target hostname, it's hard to check - for at configure time. Always do it on Linux for now. When - they get around to fixing it, add a compile-time or run-time - check for the glibc version in use. - - Some Windows documentation says that even when AI_CANONNAME is - set, the returned ai_canonname field can be null. The NetBSD - 1.5 implementation also does this, if the input hostname is a - numeric host address string. That case isn't handled well at - the moment. - - Libc version 5 didn't have getaddrinfo at all. */ - -#ifdef COPY_FIRST_CANONNAME - /* - * This code must *always* return an error, return a null - * ai_canonname, or return an ai_canonname allocated here using - * malloc, so that freeaddrinfo can always free a non-null - * ai_canonname. Note that it really doesn't matter if the - * AI_CANONNAME flag was set. - */ - ai = *result; - if (ai->ai_canonname) { - struct hostent *hp; - const char *name2 = 0; - int i, herr; - - /* - * Current versions of GET_HOST_BY_NAME will fail if the - * target hostname has IPv6 addresses only. Make sure it - * fails fairly cleanly. - */ - GET_HOST_BY_NAME (name, hp, herr); - if (hp == 0) { - /* - * This case probably means it's an IPv6-only name. If - * ai_canonname is a numeric address, get rid of it. - */ - if (ai->ai_canonname && strchr(ai->ai_canonname, ':')) - ai->ai_canonname = 0; - name2 = ai->ai_canonname ? ai->ai_canonname : name; - } else { - /* Sometimes gethostbyname will be directed to /etc/hosts - first, and sometimes that file will have entries with - the unqualified name first. So take the first entry - that looks like it could be a FQDN. */ - for (i = 0; hp->h_aliases[i]; i++) { - if (strchr(hp->h_aliases[i], '.') != 0) { - name2 = hp->h_aliases[i]; - break; - } - } - /* Give up, just use the first name (h_name == - h_aliases[0] on all systems I've seen). */ - if (hp->h_aliases[i] == 0) - name2 = hp->h_name; - } - - ai->ai_canonname = strdup(name2); - if (name2 != 0 && ai->ai_canonname == 0) { - system_freeaddrinfo(ai); - *result = 0; -#ifdef DEBUG_ADDRINFO - debug_dump_error(EAI_MEMORY); -#endif - return EAI_MEMORY; - } - /* Zap the remaining ai_canonname fields glibc fills in, in - case the application messes around with the list - structure. */ - while ((ai = ai->ai_next) != NULL) - ai->ai_canonname = 0; - } -#endif - -#ifdef NUMERIC_SERVICE_BROKEN - if (service_port != 0) { - for (ai = *result; ai; ai = ai->ai_next) { - if (socket_type != 0 && ai->ai_socktype == 0) - /* Is this check actually needed? */ - ai->ai_socktype = socket_type; - switch (ai->ai_family) { - case AF_INET: - ((struct sockaddr_in *)ai->ai_addr)->sin_port = service_port; - break; - case AF_INET6: - ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = service_port; - break; - } - } - } -#endif - -#ifdef _AIX - for (ai = *result; ai; ai = ai->ai_next) { - /* AIX 4.3.3 libc is broken. It doesn't set the family or len - fields of the sockaddr structures. Usually, sa_family is - zero, but I've seen it set to 1 in some cases also (maybe - just leftover from previous contents of the memory - block?). So, always override what libc returned. */ - ai->ai_addr->sa_family = ai->ai_family; -#ifdef HAVE_SA_LEN /* always true on AIX, actually */ - ai->ai_addr->sa_len = ai->ai_addrlen; -#endif - } -#endif - - /* Not dealt with currently: - - - Some versions of GNU libc can lose some IPv4 addresses in - certain cases when multiple IPv4 and IPv6 addresses are - available. */ - -#ifdef DEBUG_ADDRINFO - debug_dump_addrinfos(*result); -#endif - - return 0; -} - -static inline -void freeaddrinfo (struct addrinfo *ai) -{ -#ifdef COPY_FIRST_CANONNAME - if (ai) { - free(ai->ai_canonname); - ai->ai_canonname = 0; - system_freeaddrinfo(ai); - } -#else - system_freeaddrinfo(ai); -#endif -} -#endif /* WRAP_GETADDRINFO */ - #if defined(KRB5_USE_INET6) && defined(NEED_INSIXADDR_ANY) /* If compiling with IPv6 support and C library does not define in6addr_any */ +extern const struct in6_addr krb5int_in6addr_any; #undef in6addr_any #define in6addr_any krb5int_in6addr_any -static const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; #endif -#ifdef ADDRINFO_UNDEF_INLINE -# undef inline -# undef ADDRINFO_UNDEF_INLINE +/* Call out to stuff defined in libkrb5support. */ +extern int krb5int_getaddrinfo (const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **aip); +extern void krb5int_freeaddrinfo (struct addrinfo *ai); +extern const char *krb5int_gai_strerror(int err); +extern int krb5int_getnameinfo (const struct sockaddr *sa, socklen_t salen, + char *hbuf, size_t hbuflen, + char *sbuf, size_t sbuflen, + int flags); +#ifdef IMPLEMENT_FAKE_GETADDRINFO +#undef getaddrinfo +#define getaddrinfo krb5int_getaddrinfo +#undef freeaddrinfo +#define freeaddrinfo krb5int_freeaddrinfo +#undef gai_strerror +#define gai_strerror krb5int_gai_strerror +#undef getnameinfo +#define getnameinfo krb5int_getnameinfo #endif #endif /* FAI_DEFINED */ |