diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/include/ChangeLog | 9 | ||||
-rw-r--r-- | src/include/fake-addrinfo.h | 1183 | ||||
-rw-r--r-- | src/util/support/ChangeLog | 10 | ||||
-rw-r--r-- | src/util/support/fake-addrinfo.c | 1352 | ||||
-rw-r--r-- | src/util/support/libkrb5support.exports | 7 |
5 files changed, 1394 insertions, 1167 deletions
diff --git a/src/include/ChangeLog b/src/include/ChangeLog index c9837b3d4..f16444c4e 100644 --- a/src/include/ChangeLog +++ b/src/include/ChangeLog @@ -1,3 +1,12 @@ +2004-11-03 Ken Raeburn <raeburn@mit.edu> + + * fake-addrinfo.h: Move most of code content into + util/support/fake-addrinfo.c. + (krb5int_getaddrinfo, krb5int_freeaddrinfo, krb5int_getnameinfo, + krb5int_gai_strerror): Declare. + (getaddrinfo, freeaddrinfo, getnameinfo, gai_strerror): Define as + macros mapping to the krb5int_ function names. + 2004-10-29 Ken Raeburn <raeburn@mit.edu> * fake-addrinfo.h: Include errno.h earlier. 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 */ diff --git a/src/util/support/ChangeLog b/src/util/support/ChangeLog index 2ee4dd930..9698d0b3f 100644 --- a/src/util/support/ChangeLog +++ b/src/util/support/ChangeLog @@ -1,3 +1,13 @@ +2004-11-03 Ken Raeburn <raeburn@mit.edu> + + * fake-addrinfo.c: Import most of the contents of + include/fake-addrinfo.h, so we only compile it once. + (krb5int_getaddrinfo, krb5int_freeaddrinfo, krb5int_getnameinfo, + krb5int_gai_strerror): New functions, always defined and + exported. + * libkrb5support.exports: Export the new functions, not the old + _fac symbols. + 2004-10-25 Ken Raeburn <raeburn@mit.edu> * libkrb5support.exports: Export krb5int_fac, _lock_fac, diff --git a/src/util/support/fake-addrinfo.c b/src/util/support/fake-addrinfo.c index 233a29336..b0ac00104 100644 --- a/src/util/support/fake-addrinfo.c +++ b/src/util/support/fake-addrinfo.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004 by the Massachusetts Institute of Technology, + * Copyright (C) 2001,2002,2003,2004 by the Massachusetts Institute of Technology, * Cambridge, MA, USA. All Rights Reserved. * * This software is being provided to you, the LICENSEE, by the @@ -39,10 +39,1320 @@ * fashion that it might be confused with the original M.I.T. software. */ +/* Approach overview: + + If a system version is available but buggy, save handles to it, + 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. + + If no system version exists, use gethostby* and fake it. Define + the data structures and flag values locally. + + + On Mac OS X, getaddrinfo results aren't cached (though + gethostbyname results are), so we need to build a cache here. Now + things are getting really messy. Because the cache is in use, we + use getservbyname, and throw away thread safety. (Not that the + cache is thread safe, but when we get locking support, that'll be + dealt with.) This code needs tearing down and rebuilding, soon. + + + Note that recent Windows developers' code has an interesting hack: + When you include the right header files, with the right set of + macros indicating system versions, you'll get an inline function + that looks for getaddrinfo (or whatever) in the system library, and + calls it if it's there. If it's not there, it fakes it with + gethostby* calls. + + We're taking a simpler approach: A system provides these routines or + it does not. + + Someday, we may want to take into account different versions (say, + different revs of GNU libc) where some are broken in one way, and + some work or are broken in another way. Cross that bridge when we + come to it. */ + +/* To do, maybe: + + + For AIX 4.3.3, using the RFC 2133 definition: Implement + AI_NUMERICHOST. It's not defined in the header file. + + For certain (old?) versions of GNU libc, AI_NUMERICHOST is + defined but not implemented. + + + Use gethostbyname2, inet_aton and other IPv6 or thread-safe + functions if available. But, see + http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=135182 for one + gethostbyname2 problem on Linux. And besides, if a platform is + supporting IPv6 at all, they really should be doing getaddrinfo + by now. + + + inet_ntop, inet_pton + + + Conditionally export/import the function definitions, so a + library can have a single copy instead of multiple. + + + Upgrade host requirements to include working implementations of + these functions, and throw all this away. Pleeease? :-) */ + +#include "port-sockets.h" +#include "socket-utils.h" +#include "k5-platform.h" +#include "k5-thread.h" + +#include <stdio.h> /* for sprintf */ +#include <errno.h> + +#define IMPLEMENT_FAKE_GETADDRINFO #include "fake-addrinfo.h" -/* Allocate the storage here. */ -struct fac krb5int_fac = { K5_MUTEX_PARTIAL_INITIALIZER, 0 }; +#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 + +struct addrinfo { + int ai_family; /* PF_foo */ + int ai_socktype; /* SOCK_foo */ + int ai_protocol; /* 0, IPPROTO_foo */ + int ai_flags; /* AI_PASSIVE etc */ + size_t ai_addrlen; /* real length of socket address */ + char *ai_canonname; /* canonical name of host */ + struct sockaddr *ai_addr; /* pointer to variable-size address */ + struct addrinfo *ai_next; /* next in linked list */ +}; + +#undef AI_PASSIVE +#define AI_PASSIVE 0x01 +#undef AI_CANONNAME +#define AI_CANONNAME 0x02 +#undef AI_NUMERICHOST +#define AI_NUMERICHOST 0x04 +/* RFC 2553 says these are part of the interface for getipnodebyname, + not for getaddrinfo. RFC 3493 says they're part of the interface + for getaddrinfo, and getipnodeby* are deprecated. Our fake + getaddrinfo implementation here does IPv4 only anyways. */ +#undef AI_V4MAPPED +#define AI_V4MAPPED 0 +#undef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#undef AI_ALL +#define AI_ALL 0 +#undef AI_DEFAULT +#define AI_DEFAULT (AI_V4MAPPED|AI_ADDRCONFIG) + +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif +#ifndef NI_MAXSERV +#define NI_MAXSERV 32 +#endif + +#undef NI_NUMERICHOST +#define NI_NUMERICHOST 0x01 +#undef NI_NUMERICSERV +#define NI_NUMERICSERV 0x02 +#undef NI_NAMEREQD +#define NI_NAMEREQD 0x04 +#undef NI_DGRAM +#define NI_DGRAM 0x08 +#undef NI_NOFQDN +#define NI_NOFQDN 0x10 + + +#undef EAI_ADDRFAMILY +#define EAI_ADDRFAMILY 1 +#undef EAI_AGAIN +#define EAI_AGAIN 2 +#undef EAI_BADFLAGS +#define EAI_BADFLAGS 3 +#undef EAI_FAIL +#define EAI_FAIL 4 +#undef EAI_FAMILY +#define EAI_FAMILY 5 +#undef EAI_MEMORY +#define EAI_MEMORY 6 +#undef EAI_NODATA +#define EAI_NODATA 7 +#undef EAI_NONAME +#define EAI_NONAME 8 +#undef EAI_SERVICE +#define EAI_SERVICE 9 +#undef EAI_SOCKTYPE +#define EAI_SOCKTYPE 10 +#undef EAI_SYSTEM +#define EAI_SYSTEM 11 + +#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 +# define AI_NUMERICHOST 0 +#endif +/* Partial RFC 2553 implementations may not have AI_ADDRCONFIG and + friends, which RFC 3493 says are now part of the getaddrinfo + interface, and we'll want to use. */ +#ifndef AI_ADDRCONFIG +# define AI_ADDRCONFIG 0 +#endif +#ifndef AI_V4MAPPED +# define AI_V4MAPPED 0 +#endif +#ifndef AI_ALL +# define AI_ALL 0 +#endif +#ifndef AI_DEFAULT +# 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; +}; + +static struct fac krb5int_fac = { K5_MUTEX_PARTIAL_INITIALIZER, 0 }; + +#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 + +static 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 */ int krb5int_init_fac (void) { @@ -55,7 +1365,7 @@ void krb5int_fini_fac (void) } extern int krb5int_call_thread_support_init(void); -int krb5int_lock_fac (void) +static int krb5int_lock_fac (void) { int err; err = krb5int_call_thread_support_init(); @@ -64,7 +1374,39 @@ int krb5int_lock_fac (void) return k5_mutex_lock(&krb5int_fac.lock); } -int krb5int_unlock_fac (void) +static int krb5int_unlock_fac (void) { return k5_mutex_unlock(&krb5int_fac.lock); } + +#if defined(KRB5_USE_INET6) && defined(NEED_INSIXADDR_ANY) +/* If compiling with IPv6 support and C library does not define in6addr_any */ +const struct in6_addr krb5int_in6addr_any = IN6ADDR_ANY_INIT; +#endif + + + +int krb5int_getaddrinfo (const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **aip) +{ + return getaddrinfo(node, service, hints, aip); +} + +void krb5int_freeaddrinfo (struct addrinfo *ai) +{ + freeaddrinfo(ai); +} + +const char *krb5int_gai_strerror(int err) +{ + return gai_strerror(err); +} + +int krb5int_getnameinfo (const struct sockaddr *sa, socklen_t salen, + char *hbuf, size_t hbuflen, + char *sbuf, size_t sbuflen, + int flags) +{ + return getnameinfo(sa, salen, hbuf, hbuflen, sbuf, sbuflen, flags); +} diff --git a/src/util/support/libkrb5support.exports b/src/util/support/libkrb5support.exports index 7954cc9e8..5e8208306 100644 --- a/src/util/support/libkrb5support.exports +++ b/src/util/support/libkrb5support.exports @@ -2,6 +2,7 @@ krb5int_key_register krb5int_key_delete krb5int_getspecific krb5int_setspecific -krb5int_fac -krb5int_lock_fac -krb5int_unlock_fac +krb5int_getaddrinfo +krb5int_freeaddrinfo +krb5int_gai_strerror +krb5int_getnameinfo |