diff options
| -rw-r--r-- | src/include/ChangeLog | 25 | ||||
| -rw-r--r-- | src/include/fake-addrinfo.c | 299 | ||||
| -rw-r--r-- | src/include/fake-addrinfo.h | 29 |
3 files changed, 273 insertions, 80 deletions
diff --git a/src/include/ChangeLog b/src/include/ChangeLog index 7cfea4524..7e3412655 100644 --- a/src/include/ChangeLog +++ b/src/include/ChangeLog @@ -1,3 +1,28 @@ +2002-03-11 Ken Raeburn <raeburn@mit.edu> + + * fake-addrinfo.c (fixup_addrinfo): Deleted. + (fake_getaddrinfo, fake_getnameinfo, fake_freeaddrinfo): Renamed + from non-"fake_" versions, and made static. Compile if + NEED_FAKE_GETADDRINFO is defined. + (fake_getnameinfo): Truncate results if provided buffers are too + small. + (getaddrinfo, getnameinfo, freeaddrinfo) [HAVE_FAKE_GETADDRINFO]: + New functions, simple wrappers around the "fake_" versions. + (getaddrinfo, freeaddrinfo) [WRAP_GETADDRINFO]: New functions + which call the system versions via function pointers and then fix + up some known problems in the returned data. + (getnameinfo) [WRAP_GETNAMEINFO]: Likewise. + (gaiptr, faiptr, gniptr) [WRAP_GETADDRINFO || WRAP_GETNAMEINFO]: + New static variables, initialized with addresses of system + versions of getaddrinfo, etc. + * fake-addrinfo.h (fixup_addrinfo): Declaration deleted. + (WRAP_GETADDRINFO): New macro, defined on Linux and AIX. + (getaddrinfo, getnameinfo, freeaddrinfo): Define as macros, and + declare functions, if WRAP_GETADDRINFO is defined or + HAVE_GETADDRINFO is not defined; drop BROKEN_GETADDRINFO check. + (gai_strerror, addrinfo, EAI_*): Define macros and declare + functions only if HAVE_GETADDRINFO isn't defined. + 2002-03-06 Ken Raeburn <raeburn@mit.edu> * krb5.hin (krb5_const): Restore macro definition, but include a diff --git a/src/include/fake-addrinfo.c b/src/include/fake-addrinfo.c index a78e9daf4..a2981166d 100644 --- a/src/include/fake-addrinfo.c +++ b/src/include/fake-addrinfo.c @@ -41,10 +41,32 @@ /* To do, maybe: - IPv6 support for systems with working inet6 socket code but broken - getaddrinfo implementations? (RH Linux 6.1 libc getaddrinfo - ignores AI_NUMERICHOST. Solaris 8 doesn't appear to support - IPv6.) Could use gethostbyname2 if available. */ + + 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. + + + Windows support. + + Apparently XP and .Net provide getaddrinfo and friends, but + earlier versions do not. Since we want one binary to work on + multiple platforms, the best option appears to be to use the OS + version if it's available, and the fake one here otherwise. This + means both the wrapper and the fake versions need to be compiled, + and they need to use the OS version of structures and macros. + + Try defining NEED_FAKE_GETADDRINFO and WRAP_GETADDRINFO but not + HAVE_FAKE_GETADDRINFO, and put in the right magic to look up the + function addresses at run time. + + + 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. + + + Upgrade host requirements to include working implementations of + these functions, and throw all this away. :-) */ #include "fake-addrinfo.h" @@ -55,54 +77,11 @@ # undef _XOPEN_SOURCE_EXTENDED #endif -void fixup_addrinfo (struct addrinfo *ai) -{ - struct addrinfo *ai2; - - if (ai == 0) - return; - - /* 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 all the - returned structures, sometimes it's the canonical name and - sometimes it's the numeric form of an address. So if we - actually want the canonical name, we may have to look through - the list and discard numeric addresses. - - Since it's dependent on the target hostname, it's hard to check - for at configure time. */ - if (ai->ai_canonname && strchr(ai->ai_canonname, ':')) { - for (ai2 = ai->ai_next; ai2; ai2 = ai2->ai_next) { - if (ai2->ai_canonname == 0) - continue; - if (!strchr(ai2->ai_canonname, ':')) { - char *p = ai->ai_canonname; - ai->ai_canonname = ai2->ai_canonname; - ai2->ai_canonname = p; - break; - } - } - if (ai2 == 0) - /* Ran off end, with no non-numeric name. What to do? */ - ; - } - - for (; 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. */ - if (ai->ai_addr->sa_family == 0) - ai->ai_addr->sa_family = ai->ai_family; -#ifdef HAVE_SA_LEN - if (ai->ai_addr->sa_len == 0) - ai->ai_addr->sa_len = ai->ai_addrlen; +#ifdef HAVE_FAKE_GETADDRINFO +#define NEED_FAKE_GETADDRINFO #endif - } -} -#ifdef HAVE_FAKE_GETADDRINFO +#ifdef NEED_FAKE_GETADDRINFO static int translate_h_errno (int h); @@ -155,8 +134,9 @@ static int fai_add_hosts_by_name (const char *name, int af, return 0; } -int getaddrinfo (const char *name, const char *serv, - const struct addrinfo *hint, struct addrinfo **result) +static int +fake_getaddrinfo (const char *name, const char *serv, + const struct addrinfo *hint, struct addrinfo **result) { struct addrinfo *res = 0; int ret; @@ -246,10 +226,11 @@ int getaddrinfo (const char *name, const char *serv, return 0; } -int getnameinfo (const struct sockaddr *sa, socklen_t len, - char *host, size_t hostlen, - char *service, size_t servicelen, - int flags) +static int +fake_getnameinfo (const struct sockaddr *sa, socklen_t len, + char *host, size_t hostlen, + char *service, size_t servicelen, + int flags) { struct hostent *hp; const struct sockaddr_in *sinp; @@ -265,10 +246,7 @@ int getnameinfo (const struct sockaddr *sa, socklen_t len, char *p; numeric_host: p = inet_ntoa (sinp->sin_addr); - if (strlen (p) < hostlen) - strcpy (host, p); - else - return EAI_FAIL; /* ?? */ + strncpy (host, p, hostlen); } else { hp = gethostbyaddr ((const char *) &sinp->sin_addr, sizeof (struct in_addr), @@ -278,11 +256,12 @@ int getnameinfo (const struct sockaddr *sa, socklen_t len, goto numeric_host; return translate_h_errno (h_errno); } - if (strlen (hp->h_name) < hostlen) - strcpy (host, hp->h_name); - else - return EAI_FAIL; /* ?? */ + /* According to the Open Group spec, getnameinfo can + silently truncate, but must still return a + null-terminated string. */ + strncpy (host, hp->h_name, hostlen); } + host[hostlen-1] = 0; } if (service) { @@ -294,26 +273,22 @@ int getnameinfo (const struct sockaddr *sa, socklen_t len, if (port < 0 || port > 65535) return EAI_FAIL; sprintf (numbuf, "%d", port); - if (strlen (numbuf) < servicelen) - strcpy (service, numbuf); - else - return EAI_FAIL; + strncpy (service, numbuf, servicelen); } else { sp = getservbyport (sinp->sin_port, (flags & NI_DGRAM) ? "udp" : "tcp"); if (sp == 0) goto numeric_service; - if (strlen (sp->s_name) < servicelen) - strcpy (service, sp->s_name); - else - return EAI_FAIL; + strncpy (service, sp->s_name, servicelen); } + service[servicelen-1] = 0; } return 0; } -void freeaddrinfo (struct addrinfo *ai) +static void +fake_freeaddrinfo (struct addrinfo *ai) { struct addrinfo *next; while (ai) { @@ -372,4 +347,182 @@ static int translate_h_errno (int h) } } +#ifdef HAVE_FAKE_GETADDRINFO +int getaddrinfo (const char *name, const char *serv, + const struct addrinfo *hint, struct addrinfo **result) +{ + return fake_getaddrinfo(name, serv, hint, result); +} + +void freeaddrinfo (struct addrinfo *ai) +{ + fake_freeaddrinfo(ai); +} + +int getnameinfo (const struct sockaddr *sa, socklen_t len, + char *host, size_t hostlen, + char *service, size_t servicelen, + int flags) +{ + return fake_getnameinfo(sa, len, host, hostlen, service, servicelen, + flags); +} +#endif /* HAVE_FAKE_GETADDRINFO */ +#endif /* NEED_FAKE_GETADDRINFO */ + + +#if defined (WRAP_GETADDRINFO) || defined (WRAP_GETNAMEINFO) +/* These variables will contain pointers to the system versions. They + have to be initialized at the end, because the way we initialize + them (for UNIX) is #undef and a reference to the C library symbol + name. */ +static int (*gaiptr) (const char *, const char *, const struct addrinfo *, + struct addrinfo **); +static void (*faiptr) (struct addrinfo *); +#ifdef WRAP_GETNAMEINFO +static int (*gniptr) (const struct sockaddr *, socklen_t, + char *, size_t, char *, size_t, int); +#endif + +#ifdef WRAP_GETADDRINFO + +int +getaddrinfo (const char *name, const char *serv, const struct addrinfo *hint, + struct addrinfo **result) +{ + int aierr; + + aierr = (*gaiptr) (name, serv, hint, result); + if (aierr || *result == 0) + return aierr; + +#ifdef __linux__ + /* 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. + + 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. */ +#define COPY_FIRST_CANONNAME + if (name && (hint->ai_flags & AI_CANONNAME)) { + struct hostent *hp; + const char *name2 = 0; + int i; + + hp = gethostbyname(name); + if (hp == 0) { + if ((*result)->ai_canonname != 0) + /* XXX Indicate success with the existing name? */ + return 0; + /* No canonname listed, and gethostbyname failed. */ + name2 = 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; + } + + (*result)->ai_canonname = strdup(name2); + if ((*result)->ai_canonname == 0) { + (*faiptr)(*result); + *result = 0; + return EAI_MEMORY; + } + } +#endif + +#ifdef _AIX + for (; 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. */ + if (ai->ai_addr->sa_family == 0) + ai->ai_addr->sa_family = ai->ai_family; +#ifdef HAVE_SA_LEN /* always true on aix, actually */ + if (ai->ai_addr->sa_len == 0) + ai->ai_addr->sa_len = ai->ai_addrlen; +#endif + } +#endif + + /* Not dealt with yet: + + - Some versions of GNU libc can lose some IPv4 addresses in + certain cases when multiple IPv4 and IPv6 addresses are + available. + + - Wrapping a possibly-missing system version, as we'll need to + do for Windows. */ + + return 0; +} + +void freeaddrinfo (struct addrinfo *ai) +{ +#ifdef COPY_FIRST_CANONNAME + free(ai->ai_canonname); + ai->ai_canonname = 0; + (*faiptr)(ai); +#else + (*faiptr)(ai); +#endif +} +#endif /* WRAP_GETADDRINFO */ + +#ifdef WRAP_GETNAMEINFO +int getnameinfo (const struct sockaddr *sa, socklen_t len, + char *host, size_t hostlen, + char *service, size_t servicelen, + int flags) +{ + return (*gniptr)(sa, len, host, hostlen, service, servicelen, flags); +} +#endif /* WRAP_GETNAMEINFO */ + +#undef getaddrinfo +#undef getnameinfo +#undef freeaddrinfo +static int (*gaiptr) (const char *, const char *, const struct addrinfo *, + struct addrinfo **) = &getaddrinfo; +static void (*faiptr) (struct addrinfo *) = &freeaddrinfo; +#ifdef WRAP_GETNAMEINFO +static int (*gniptr) (const struct sockaddr *, socklen_t, + char *, size_t, char *, size_t, int) = &getnameinfo; #endif + +#endif /* WRAP_GETADDRINFO || WRAP_GETNAMEINFO */ diff --git a/src/include/fake-addrinfo.h b/src/include/fake-addrinfo.h index d7c651180..661c0ffd0 100644 --- a/src/include/fake-addrinfo.h +++ b/src/include/fake-addrinfo.h @@ -51,13 +51,15 @@ #define FAI_CONCAT(A,B) FAI_CONCAT2(A,B) #define FAI_CONCAT2(A,B) A ## B -/* Various C libraries have broken implementations of getaddrinfo. */ -#undef fixup_addrinfo -#define fixup_addrinfo FAI_CONCAT(FAI_PREFIX, _fixup_addrinfo) +#define fixup_addrinfo do not call me! -extern void fixup_addrinfo (struct addrinfo *ai); +#if defined (__linux__) || defined (_AIX) +/* See comments in fake-addrinfo.c. */ +# define WRAP_GETADDRINFO +/* # define WRAP_GETNAMEINFO */ +#endif -#if !defined (HAVE_GETADDRINFO) || defined (BROKEN_GETADDRINFO) +#if !defined (HAVE_GETADDRINFO) || defined(WRAP_GETADDRINFO) #undef getaddrinfo #define getaddrinfo FAI_CONCAT(FAI_PREFIX, _fake_getaddrinfo) @@ -65,6 +67,11 @@ extern void fixup_addrinfo (struct addrinfo *ai); #define getnameinfo FAI_CONCAT(FAI_PREFIX, _fake_getnameinfo) #undef freeaddrinfo #define freeaddrinfo FAI_CONCAT(FAI_PREFIX, _fake_freeaddrinfo) + +#endif + +#if !defined (HAVE_GETADDRINFO) + #undef gai_strerror #define gai_strerror FAI_CONCAT(FAI_PREFIX, _fake_gai_strerror) #undef addrinfo @@ -141,6 +148,10 @@ struct addrinfo { #undef EAI_SYSTEM #define EAI_SYSTEM 11 +#endif /* ! HAVE_GETADDRINFO */ + +#if !defined (HAVE_GETADDRINFO) || defined (WRAP_GETADDRINFO) + int getaddrinfo (const char *name, const char *serv, const struct addrinfo *hint, struct addrinfo **result); @@ -151,14 +162,18 @@ int getnameinfo (const struct sockaddr *addr, socklen_t len, void freeaddrinfo (struct addrinfo *ai); -char *gai_strerror (int code); +#endif + +#if !defined (HAVE_GETADDRINFO) #define HAVE_FAKE_GETADDRINFO #define HAVE_GETADDRINFO #undef HAVE_GETNAMEINFO #define HAVE_GETNAMEINFO -#endif /* HAVE_GETADDRINFO */ +char *gai_strerror (int code); + +#endif /* Fudge things on older gai implementations. */ /* AIX 4.3.3 is based on RFC 2133; no AI_NUMERICHOST. */ |
