From 126757160d1cf1f66e7109c50c53058005a08520 Mon Sep 17 00:00:00 2001 From: Ken Raeburn Date: Tue, 26 Mar 2002 22:37:04 +0000 Subject: [PATCH] Change approach for getaddrinfo support. Now, only fake-addrinfo.h is included, no magic macros need to be defined, and no special care needs to be taken to identify a unique object file used in all builds of a library/program. All defined functions (if any) are static in each object file, and declared inline under gcc so they can be more easily eliminated. Simplifies maintenance, and worst case should add no more than a few KB to libraries and programs. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@14291 dc483132-0cff-0310-8789-dd5450dbe970 --- src/appl/bsd/ChangeLog | 5 + src/appl/bsd/defines.h | 1 - src/appl/bsd/kcmd.c | 2 - src/appl/telnet/telnet/ChangeLog | 5 + src/appl/telnet/telnet/commands.c | 3 +- src/appl/telnet/telnetd/ChangeLog | 5 + src/appl/telnet/telnetd/telnetd.c | 3 +- src/clients/klist/ChangeLog | 5 + src/clients/klist/klist.c | 3 +- src/include/ChangeLog | 8 + src/include/fake-addrinfo.c | 528 ---------------------------- src/include/fake-addrinfo.h | 560 +++++++++++++++++++++++++++++- src/lib/krb5/os/ChangeLog | 10 + src/lib/krb5/os/hostaddr.c | 1 - src/lib/krb5/os/hst_realm.c | 1 - src/lib/krb5/os/localaddr.c | 3 +- src/lib/krb5/os/locate_kdc.c | 3 +- src/util/pty/ChangeLog | 5 + src/util/pty/sane_hostname.c | 3 +- 19 files changed, 592 insertions(+), 562 deletions(-) delete mode 100644 src/include/fake-addrinfo.c diff --git a/src/appl/bsd/ChangeLog b/src/appl/bsd/ChangeLog index f541a00cd..bbdf547dc 100644 --- a/src/appl/bsd/ChangeLog +++ b/src/appl/bsd/ChangeLog @@ -1,3 +1,8 @@ +2002-03-26 Ken Raeburn + + * defines.h (FAI_PREFIX): Don't define. + * kcmd.c (kcmd_connect): Don't include fake-addrinfo.c. + 2002-03-20 Ezra Peisach * Makefile.in: Remove dependencies on krb524_err.h for login.o. If diff --git a/src/appl/bsd/defines.h b/src/appl/bsd/defines.h index d8bf86765..93ef6ec62 100644 --- a/src/appl/bsd/defines.h +++ b/src/appl/bsd/defines.h @@ -69,5 +69,4 @@ krb5_error_code rd_and_store_for_creds(krb5_context context, extern int setenv(char *, char *, int); #endif -#define FAI_PREFIX bsd #include "fake-addrinfo.h" diff --git a/src/appl/bsd/kcmd.c b/src/appl/bsd/kcmd.c index 402e6ccb9..e252e03f6 100644 --- a/src/appl/bsd/kcmd.c +++ b/src/appl/bsd/kcmd.c @@ -188,8 +188,6 @@ restore_sigs (masktype *oldmask) #endif /* POSIX_SIGNALS */ } -#include "fake-addrinfo.c" - static int kcmd_connect (int *sp, int *addrfamilyp, struct sockaddr_in *sockinp, char *hname, char **host_save, unsigned int rport, int *lportp, diff --git a/src/appl/telnet/telnet/ChangeLog b/src/appl/telnet/telnet/ChangeLog index ce9ed967b..8973d8775 100644 --- a/src/appl/telnet/telnet/ChangeLog +++ b/src/appl/telnet/telnet/ChangeLog @@ -1,3 +1,8 @@ +2002-03-26 Ken Raeburn + + * commands.c: Include fake-addrinfo.h, not fake-addrinfo.c. + (FAI_PREFIX): Delete. + 2001-10-09 Ken Raeburn * commands.c, externs.h, ring.h, telnet.c: Make prototypes diff --git a/src/appl/telnet/telnet/commands.c b/src/appl/telnet/telnet/commands.c index 4f31586a1..5e693bd1a 100644 --- a/src/appl/telnet/telnet/commands.c +++ b/src/appl/telnet/telnet/commands.c @@ -119,8 +119,7 @@ int tos = -1; static unsigned long sourceroute(char *, char **, int *); #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ -#define FAI_PREFIX telnet -#include "fake-addrinfo.c" +#include "fake-addrinfo.h" char *hostname; static char _hostname[MAXDNAME]; diff --git a/src/appl/telnet/telnetd/ChangeLog b/src/appl/telnet/telnetd/ChangeLog index 73fd83d04..5c27eca2b 100644 --- a/src/appl/telnet/telnetd/ChangeLog +++ b/src/appl/telnet/telnetd/ChangeLog @@ -1,3 +1,8 @@ +2002-03-26 Ken Raeburn + + * telnetd.c: Include fake-addrinfo.h, not fake-addrinfo.c. + (FAI_PREFIX): Delete. + 2001-10-09 Ken Raeburn * defs.h, state.c, sys_term.c, telnetd-ktd.c, telnetd.c: Make diff --git a/src/appl/telnet/telnetd/telnetd.c b/src/appl/telnet/telnetd/telnetd.c index aefcacf9e..bed4a3294 100644 --- a/src/appl/telnet/telnetd/telnetd.c +++ b/src/appl/telnet/telnetd/telnetd.c @@ -77,8 +77,7 @@ struct socket_security ss; # endif /* SO_SEC_MULTI */ #endif /* _SC_CRAY_SECURE_SYS */ -#define FAI_PREFIX telnetd -#include "fake-addrinfo.c" +#include "fake-addrinfo.h" #ifdef KRB5 #include "krb5.h" diff --git a/src/clients/klist/ChangeLog b/src/clients/klist/ChangeLog index 9d2fa91ad..2e5b11077 100644 --- a/src/clients/klist/ChangeLog +++ b/src/clients/klist/ChangeLog @@ -1,3 +1,8 @@ +2002-03-26 Ken Raeburn + + * klist.c: Include fake-addrinfo.h, not fake-addrinfo.c. + (FAI_PREFIX): Delete. + 2002-03-07 Ken Raeburn * klist.c: Include port-sockets.h and socket-utils.h instead of diff --git a/src/clients/klist/klist.c b/src/clients/klist/klist.c index b3d64935d..763009ddf 100644 --- a/src/clients/klist/klist.c +++ b/src/clients/klist/klist.c @@ -657,8 +657,7 @@ show_credential(cred) #include "port-sockets.h" #include "socket-utils.h" /* for ss2sin etc */ -#define FAI_PREFIX klist -#include "fake-addrinfo.c" +#include "fake-addrinfo.h" void one_addr(a) krb5_address *a; diff --git a/src/include/ChangeLog b/src/include/ChangeLog index 7e3412655..18770f23a 100644 --- a/src/include/ChangeLog +++ b/src/include/ChangeLog @@ -1,3 +1,11 @@ +2002-03-26 Ken Raeburn + + * fake-addrinfo.h: Incorporate all of fake-addrinfo.c. Make all + defined functions static, and inline if gcc is used. Drop + FAI_PREFIX renaming hacks. Fix some bugs in the separation of + getnameinfo from getaddrinfo/freeaddrinfo for wrapping purposes. + * fake-addrinfo.c: Delete. + 2002-03-11 Ken Raeburn * fake-addrinfo.c (fixup_addrinfo): Deleted. diff --git a/src/include/fake-addrinfo.c b/src/include/fake-addrinfo.c deleted file mode 100644 index a2981166d..000000000 --- a/src/include/fake-addrinfo.c +++ /dev/null @@ -1,528 +0,0 @@ -/* - * Copyright (C) 2001,2002 by the Massachusetts Institute of Technology, - * Cambridge, MA, USA. All Rights Reserved. - * - * This software is being provided to you, the LICENSEE, by the - * Massachusetts Institute of Technology (M.I.T.) under the following - * license. By obtaining, using and/or copying this software, you agree - * that you have read, understood, and will comply with these terms and - * conditions: - * - * Export of this software from the United States of America may - * require a specific license from the United States Government. - * It is the responsibility of any person or organization contemplating - * export to obtain such a license before exporting. - * - * WITHIN THAT CONSTRAINT, permission to use, copy, modify and distribute - * this software and its documentation for any purpose and without fee or - * royalty is hereby granted, provided that you agree to comply with the - * following copyright notice and statements, including the disclaimer, and - * that the same appear on ALL copies of the software and documentation, - * including modifications that you make for internal use or for - * distribution: - * - * THIS SOFTWARE IS PROVIDED "AS IS", AND M.I.T. MAKES NO REPRESENTATIONS - * OR WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not - * limitation, M.I.T. MAKES NO REPRESENTATIONS OR WARRANTIES OF - * MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF - * THE LICENSED SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY - * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. - * - * The name of the Massachusetts Institute of Technology or M.I.T. may NOT - * be used in advertising or publicity pertaining to distribution of the - * software. Title to copyright in this software and any associated - * documentation shall at all times remain with M.I.T., and USER agrees to - * preserve same. - * - * Furthermore if you modify this software you must label - * your software as modified software and not distribute it in such a - * fashion that it might be confused with the original M.I.T. software. - */ - -/* 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. - - + 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" - -#if !defined(_XOPEN_SOURCE_EXTENDED) && !defined(HAVE_MACSOCK_H) && !defined(_WIN32) -/* Hack for HPUX, to get h_errno. */ -# define _XOPEN_SOURCE_EXTENDED 1 -# include -# undef _XOPEN_SOURCE_EXTENDED -#endif - -#ifdef HAVE_FAKE_GETADDRINFO -#define NEED_FAKE_GETADDRINFO -#endif - -#ifdef NEED_FAKE_GETADDRINFO - -static int translate_h_errno (int h); - -static int fai_add_entry (struct addrinfo **result, void *addr, int port, - const struct addrinfo *template) -{ - struct addrinfo *n = malloc (sizeof (struct addrinfo)); - struct sockaddr_in *sin4; - if (n == 0) - return EAI_MEMORY; - if (template->ai_family != AF_INET) - return EAI_FAMILY; - *n = *template; - 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 - n->ai_next = *result; - *result = n; - return 0; -} - -static int fai_add_hosts_by_name (const char *name, int af, - struct addrinfo *template, - int portnum, int flags, - struct addrinfo **result) -{ - struct hostent *hp; - int i, r; - - if (af != AF_INET) - /* For now, real ipv6 support needs real getaddrinfo. */ - return EAI_FAMILY; - hp = gethostbyname (name); - if (hp == 0) - return translate_h_errno (h_errno); - 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; -} - -static 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; - - 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; - if (socktype == 0) { - try_dgram_too = 1; - socktype = SOCK_STREAM; - } - try_service_lookup: - sp = getservbyname (serv, socktype == SOCK_STREAM ? "tcp" : "udp"); - 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, AF_INET, &template, port, flags, - &res); - } - - if (ret && ret != NO_ADDRESS) { - freeaddrinfo (res); - return ret; - } - if (res == 0) - return NO_ADDRESS; - *result = res; - return 0; -} - -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; - struct servent *sp; - - if (sa->sa_family != AF_INET) { - return EAI_FAMILY; - } - sinp = (const struct sockaddr_in *) sa; - - if (host) { - if (flags & NI_NUMERICHOST) { - char *p; - numeric_host: - p = inet_ntoa (sinp->sin_addr); - strncpy (host, p, hostlen); - } else { - hp = gethostbyaddr ((const char *) &sinp->sin_addr, - sizeof (struct in_addr), - sa->sa_family); - if (hp == 0) { - if (h_errno == NO_ADDRESS && !(flags & NI_NAMEREQD)) /* ??? */ - goto numeric_host; - return translate_h_errno (h_errno); - } - /* 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) { - 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, servicelen); - } else { - sp = getservbyport (sinp->sin_port, - (flags & NI_DGRAM) ? "udp" : "tcp"); - if (sp == 0) - goto numeric_service; - strncpy (service, sp->s_name, servicelen); - } - service[servicelen-1] = 0; - } - - return 0; -} - -static 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; - } -} - -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?"; - } -} - -static 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; - } -} - -#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 3378eccf2..b0660501e 100644 --- a/src/include/fake-addrinfo.h +++ b/src/include/fake-addrinfo.h @@ -39,41 +39,94 @@ * fashion that it might be confused with the original M.I.T. software. */ +/* Approach overview: + + If a system version is available but buggy, save pointers 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. + + + 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. + + + Upgrade host requirements to include working implementations of + these functions, and throw all this away. Pleeease? :-) */ + #ifndef FAI_DEFINED #define FAI_DEFINED #include "port-sockets.h" #include "socket-utils.h" -#ifndef FAI_PREFIX -# error "FAI_PREFIX must be defined when fake-addrinfo.h is included" -#endif - -#define FAI_CONCAT(A,B) FAI_CONCAT2(A,B) -#define FAI_CONCAT2(A,B) A ## B - #if defined (__linux__) || defined (_AIX) -/* See comments in fake-addrinfo.c. */ +/* See comments below. */ # define WRAP_GETADDRINFO /* # define WRAP_GETNAMEINFO */ #endif +#ifdef WRAP_GETADDRINFO +static int (*gaiptr) (const char *, const char *, const struct addrinfo *, + struct addrinfo **) = &getaddrinfo; +static void (*faiptr) (struct addrinfo *) = &freeaddrinfo; +#endif + +#ifdef WRAP_GETNAMEINFO +static int (*gniptr) (const struct sockaddr *, socklen_t, + char *, size_t, char *, size_t, int) = &getnameinfo; +#endif + #if !defined (HAVE_GETADDRINFO) || defined(WRAP_GETADDRINFO) #undef getaddrinfo -#define getaddrinfo FAI_CONCAT(FAI_PREFIX, _fake_getaddrinfo) -#undef getnameinfo -#define getnameinfo FAI_CONCAT(FAI_PREFIX, _fake_getnameinfo) +#define getaddrinfo my_fake_getaddrinfo #undef freeaddrinfo -#define freeaddrinfo FAI_CONCAT(FAI_PREFIX, _fake_freeaddrinfo) +#define freeaddrinfo my_fake_freeaddrinfo + +#endif + +#if !defined (HAVE_GETADDRINFO) || defined(WRAP_GETNAMEINFO) + +#undef getnameinfo +#define getnameinfo my_fake_getnameinfo #endif #if !defined (HAVE_GETADDRINFO) #undef gai_strerror -#define gai_strerror FAI_CONCAT(FAI_PREFIX, _fake_gai_strerror) +#define gai_strerror my_fake_gai_strerror #undef addrinfo -#define addrinfo FAI_CONCAT(FAI_PREFIX, _fake_addrinfo) +#define addrinfo my_fake_addrinfo struct addrinfo { int ai_family; /* PF_foo */ @@ -150,16 +203,21 @@ struct addrinfo { #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) || defined (WRAP_GETNAMEINFO) +static int getnameinfo (const struct sockaddr *addr, socklen_t len, char *host, size_t hostlen, char *service, size_t servicelen, int flags); - -void freeaddrinfo (struct addrinfo *ai); - #endif #if !defined (HAVE_GETADDRINFO) @@ -169,6 +227,7 @@ void freeaddrinfo (struct addrinfo *ai); #undef HAVE_GETNAMEINFO #define HAVE_GETNAMEINFO +static char *gai_strerror (int code); #endif @@ -179,4 +238,471 @@ char *gai_strerror (int code); # define AI_NUMERICHOST 0 #endif +#if !defined(inline) +# if !defined(__GNUC__) +# define inline /* nothing, just static */ +# else +# define inline __inline__ +# endif +# define ADDRINFO_UNDEF_INLINE +#endif + +#if !defined(_XOPEN_SOURCE_EXTENDED) && !defined(HAVE_MACSOCK_H) && !defined(_WIN32) +/* Hack for HPUX, to get h_errno. */ +# define _XOPEN_SOURCE_EXTENDED 1 +# include +# undef _XOPEN_SOURCE_EXTENDED +#endif + +#ifdef HAVE_FAKE_GETADDRINFO +#define NEED_FAKE_GETADDRINFO +#endif + +#ifdef NEED_FAKE_GETADDRINFO + +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)); + struct sockaddr_in *sin4; + if (n == 0) + return EAI_MEMORY; + if (template->ai_family != AF_INET) + return EAI_FAMILY; + *n = *template; + 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 + n->ai_next = *result; + *result = n; + return 0; +} + +static inline int fai_add_hosts_by_name (const char *name, int af, + struct addrinfo *template, + int portnum, int flags, + struct addrinfo **result) +{ + struct hostent *hp; + int i, r; + + if (af != AF_INET) + /* For now, real ipv6 support needs real getaddrinfo. */ + return EAI_FAMILY; + hp = gethostbyname (name); + if (hp == 0) + return translate_h_errno (h_errno); + 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; +} + +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; + + 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; + if (socktype == 0) { + try_dgram_too = 1; + socktype = SOCK_STREAM; + } + try_service_lookup: + sp = getservbyname (serv, socktype == SOCK_STREAM ? "tcp" : "udp"); + 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, AF_INET, &template, port, flags, + &res); + } + + if (ret && ret != NO_ADDRESS) { + fake_freeaddrinfo (res); + return ret; + } + if (res == 0) + return NO_ADDRESS; + *result = res; + return 0; +} + +static inline 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; + struct servent *sp; + + if (sa->sa_family != AF_INET) { + return EAI_FAMILY; + } + sinp = (const struct sockaddr_in *) sa; + + if (host) { + if (flags & NI_NUMERICHOST) { + char *p; + numeric_host: + p = inet_ntoa (sinp->sin_addr); + strncpy (host, p, hostlen); + } else { + hp = gethostbyaddr ((const char *) &sinp->sin_addr, + sizeof (struct in_addr), + sa->sa_family); + if (hp == 0) { + if (h_errno == NO_ADDRESS && !(flags & NI_NAMEREQD)) /* ??? */ + goto numeric_host; + return translate_h_errno (h_errno); + } + /* 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) { + 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, servicelen); + } else { + sp = getservbyport (sinp->sin_port, + (flags & NI_DGRAM) ? "udp" : "tcp"); + if (sp == 0) + goto numeric_service; + strncpy (service, sp->s_name, servicelen); + } + service[servicelen-1] = 0; + } + + return 0; +} + +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?"; + } +} + +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; + } +} + +#ifdef HAVE_FAKE_GETADDRINFO +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); +} + +static inline +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 + +static inline +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; +} + +static inline +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 +static inline +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 */ + +#endif /* WRAP_GETADDRINFO || WRAP_GETNAMEINFO */ + +#ifdef ADDRINFO_UNDEF_INLINE +# undef inline +# undef ADDRINFO_UNDEF_INLINE +#endif + #endif /* FAI_DEFINED */ diff --git a/src/lib/krb5/os/ChangeLog b/src/lib/krb5/os/ChangeLog index a9cfd5c5e..771357e42 100644 --- a/src/lib/krb5/os/ChangeLog +++ b/src/lib/krb5/os/ChangeLog @@ -1,3 +1,13 @@ +2002-03-26 Ken Raeburn + + * hostaddr.c (FAI_PREFIX): Delete. + * hst_realm.c (FAI_PREFIX): Delete. + * localaddr.c [TEST || DEBUG]: Include fake-addrinfo.h, not + fake-addrinfo.c. + (FAI_PREFIX) [TEST || DEBUG]: Delete. + * locate_kdc.c: Include fake-addrinfo.h, not fake-addrinfo.c. + (FAI_PREFIX): Delete. + 2002-02-20 Ken Raeburn * localaddr.c: Include foreachaddr.c. diff --git a/src/lib/krb5/os/hostaddr.c b/src/lib/krb5/os/hostaddr.c index 3225ac571..55f46bdfa 100644 --- a/src/lib/krb5/os/hostaddr.c +++ b/src/lib/krb5/os/hostaddr.c @@ -30,7 +30,6 @@ #define NEED_SOCKETS #include "k5-int.h" -#define FAI_PREFIX krb5int #include "fake-addrinfo.h" krb5_error_code diff --git a/src/lib/krb5/os/hst_realm.c b/src/lib/krb5/os/hst_realm.c index e4ea6eda6..fb81af2d3 100644 --- a/src/lib/krb5/os/hst_realm.c +++ b/src/lib/krb5/os/hst_realm.c @@ -90,7 +90,6 @@ #endif /* WSHELPER */ #endif /* KRB5_DNS_LOOKUP */ -#define FAI_PREFIX krb5int #include "fake-addrinfo.h" /* for old Unixes and friends ... */ diff --git a/src/lib/krb5/os/localaddr.c b/src/lib/krb5/os/localaddr.c index 129a12f91..43e4c35e9 100644 --- a/src/lib/krb5/os/localaddr.c +++ b/src/lib/krb5/os/localaddr.c @@ -43,8 +43,7 @@ #include #if defined(TEST) || defined(DEBUG) -# define FAI_PREFIX krb5int -# include "fake-addrinfo.c" +# include "fake-addrinfo.h" #endif #include "foreachaddr.c" diff --git a/src/lib/krb5/os/locate_kdc.c b/src/lib/krb5/os/locate_kdc.c index e708459d3..4dc47cfb8 100644 --- a/src/lib/krb5/os/locate_kdc.c +++ b/src/lib/krb5/os/locate_kdc.c @@ -44,8 +44,7 @@ #define T_SRV 33 #endif /* T_SRV */ -#define FAI_PREFIX krb5int -#include "fake-addrinfo.c" +#include "fake-addrinfo.h" /* for old Unixes and friends ... */ #ifndef MAXHOSTNAMELEN diff --git a/src/util/pty/ChangeLog b/src/util/pty/ChangeLog index 4de84cbfc..11bf298f0 100644 --- a/src/util/pty/ChangeLog +++ b/src/util/pty/ChangeLog @@ -1,3 +1,8 @@ +2002-03-26 Ken Raeburn + + * sane_hostname.c: Include fake-addrinfo.h, not fake-addrinfo.c. + (FAI_PREFIX): Delete. + 2002-02-19 Ken Raeburn * Makefile.in (LIBMINOR): Bump due to change in internals. (Tom's diff --git a/src/util/pty/sane_hostname.c b/src/util/pty/sane_hostname.c index 710c318ec..e22d03958 100644 --- a/src/util/pty/sane_hostname.c +++ b/src/util/pty/sane_hostname.c @@ -27,8 +27,7 @@ #include #include "socket-utils.h" -#define FAI_PREFIX krb5int_pty -#include "fake-addrinfo.c" +#include "fake-addrinfo.h" static void downcase (char *s) -- 2.26.2