X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fnetwork%2Fgetaddrinfo.c;h=236088e2dde1c8de8b8cd25ab975cccf90d91e65;hb=6d9fde80110542d213ed6236efc134931da58066;hp=b815947b8528886e6bc52bc1c0ddc6cc3d50f853;hpb=7a89a604893398070f36e7eefcdce55cd35d3fca;p=vlc diff --git a/src/network/getaddrinfo.c b/src/network/getaddrinfo.c index b815947b85..236088e2dd 100644 --- a/src/network/getaddrinfo.c +++ b/src/network/getaddrinfo.c @@ -2,7 +2,7 @@ * getaddrinfo.c: getaddrinfo/getnameinfo replacement functions ***************************************************************************** * Copyright (C) 2005 the VideoLAN team - * Copyright (C) 2002-2004 Rémi Denis-Courmont + * Copyright (C) 2002-2007 Rémi Denis-Courmont * $Id$ * * Author: Rémi Denis-Courmont @@ -22,6 +22,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include #include /* size_t */ @@ -53,21 +57,14 @@ #ifndef AF_UNSPEC # define AF_UNSPEC 0 #endif -#ifndef EAI_OVERFLOW -# define EAI_OVERFLOW -12 /* Argument buffer overflow. */ -#endif - -#define _NI_MASK (NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|\ - NI_DGRAM) -#define _AI_MASK (AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST) #ifndef HAVE_GAI_STRERROR -static struct +static const struct { - int code; - const char *msg; -} const __gai_errlist[] = + int code; + const char msg[41]; +} gai_errlist[] = { { 0, "Error 0" }, { EAI_BADFLAGS, "Invalid flag used" }, @@ -82,65 +79,35 @@ static struct { EAI_MEMORY, "Memory allocation failure" }, { EAI_OVERFLOW, "Buffer overflow" }, { EAI_SYSTEM, "System error" }, - { 0, NULL } + { 0, "" }, }; -static const char *__gai_unknownerr = "Unrecognized error number"; +static const char gai_unknownerr[] = "Unrecognized error number"; /**************************************************************************** * Converts an EAI_* error code into human readable english text. ****************************************************************************/ -const char *vlc_gai_strerror( int errnum ) +const char *vlc_gai_strerror (int errnum) { - int i; + for (unsigned i = 0; *gai_errlist[i].msg; i++) + if (errnum == gai_errlist[i].code) + return gai_errlist[i].msg; - for (i = 0; __gai_errlist[i].msg != NULL; i++) - if (errnum == __gai_errlist[i].code) - return __gai_errlist[i].msg; - - return __gai_unknownerr; + return gai_unknownerr; } -# undef _EAI_POSITIVE_MAX #else /* ifndef HAVE_GAI_STRERROR */ -const char *vlc_gai_strerror( int errnum ) +const char *vlc_gai_strerror (int errnum) { - return gai_strerror( errnum ); + return gai_strerror (errnum); } #endif -#if !(defined (HAVE_GETNAMEINFO) && defined (HAVE_GETADDRINFO)) -/* - * Converts the current herrno error value into an EAI_* error code. - * That error code is normally returned by getnameinfo() or getaddrinfo(). - */ -static int -gai_error_from_herrno( void ) -{ - switch(h_errno) - { - case HOST_NOT_FOUND: - return EAI_NONAME; - - case NO_ADDRESS: -# if (NO_ADDRESS != NO_DATA) - case NO_DATA: -# endif - return EAI_NODATA; - - case NO_RECOVERY: - return EAI_FAIL; - - case TRY_AGAIN: - return EAI_AGAIN; - } - return EAI_SYSTEM; -} -#endif /* if !(HAVE_GETNAMEINFO && HAVE_GETADDRINFO) */ - #ifndef HAVE_GETNAMEINFO +#define _NI_MASK (NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|\ + NI_DGRAM) /* * getnameinfo() non-thread-safe IPv4-only implementation, - * Address-family-independant address to hostname translation + * Address-family-independent address to hostname translation * (reverse DNS lookup in case of IPv4). * * This is meant for use on old IP-enabled systems that are not IPv6-aware, @@ -150,11 +117,17 @@ gai_error_from_herrno( void ) * GNU C library 2.0.x is known to lack this function, even though it defines * getaddrinfo(). */ +#ifdef WIN32 +static int WSAAPI +getnameinfo (const struct sockaddr *sa, socklen_t salen, + char *host, DWORD hostlen, char *serv, DWORD servlen, int flags) +#else static int -__getnameinfo( const struct sockaddr *sa, socklen_t salen, - char *host, int hostlen, char *serv, int servlen, int flags ) +getnameinfo (const struct sockaddr *sa, socklen_t salen, + char *host, int hostlen, char *serv, int servlen, int flags) +#endif { - if (((unsigned)salen < sizeof (struct sockaddr_in)) + if (((size_t)salen < sizeof (struct sockaddr_in)) || (sa->sa_family != AF_INET)) return EAI_FAMILY; else if (flags & (~_NI_MASK)) @@ -179,14 +152,14 @@ __getnameinfo( const struct sockaddr *sa, socklen_t salen, if (snprintf (host, hostlen, "%u.%u.%u.%u", ipv4 >> 24, (ipv4 >> 16) & 0xff, (ipv4 >> 8) & 0xff, - ipv4 & 0xff) >= hostlen) + ipv4 & 0xff) >= (int)hostlen) return EAI_OVERFLOW; } if (serv != NULL) { if (snprintf (serv, servlen, "%u", - (unsigned int)ntohs (addr->sin_port)) >= servlen) + (unsigned int)ntohs (addr->sin_port)) >= (int)servlen) return EAI_OVERFLOW; } } @@ -195,107 +168,43 @@ __getnameinfo( const struct sockaddr *sa, socklen_t salen, #endif /* if !HAVE_GETNAMEINFO */ -#if defined( WIN32 ) && !defined( UNDER_CE ) - /* - * Here is the kind of kludge you need to keep binary compatibility among - * varying OS versions... - */ -typedef int (CALLBACK * GETNAMEINFO) ( const struct sockaddr*, socklen_t, - char*, DWORD, char*, DWORD, int ); -typedef int (CALLBACK * GETADDRINFO) (const char *, const char *, - const struct addrinfo *, - struct addrinfo **); - -typedef void (CALLBACK * FREEADDRINFO) ( struct addrinfo * ); - - -static WINAPI int _ws2_getnameinfo_bind( const struct sockaddr *sa, socklen_t salen, - char *host, DWORD hostlen, char *serv, DWORD servlen, int flags ); - -static WINAPI int _ws2_getaddrinfo_bind(const char *node, const char *service, - const struct addrinfo *hints, struct addrinfo **res); - -static WINAPI void _ws2_freeaddrinfo_bind( struct addrinfo *infos ); - -static GETNAMEINFO ws2_getnameinfo = _ws2_getnameinfo_bind; -static GETADDRINFO ws2_getaddrinfo = _ws2_getaddrinfo_bind; -static FREEADDRINFO ws2_freeaddrinfo = _ws2_freeaddrinfo_bind; - -static int _ws2_find_ipv6_api(void) +#ifndef HAVE_GETADDRINFO +#define _AI_MASK (AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST) +/* + * Converts the current herrno error value into an EAI_* error code. + * That error code is normally returned by getnameinfo() or getaddrinfo(). + */ +static int +gai_error_from_herrno (void) { - /* For Windows XP and above, IPv6 stack is in WS2_32.DLL */ - HINSTANCE module = LoadLibrary( "ws2_32.dll" ); - if( module != NULL ) + switch (h_errno) { - ws2_getnameinfo = (GETNAMEINFO)GetProcAddress( module, "getnameinfo" ); - ws2_getaddrinfo = (GETADDRINFO)GetProcAddress( module, "getaddrinfo" ); - ws2_freeaddrinfo = (FREEADDRINFO)GetProcAddress( module, "freeaddrinfo" ); - if( ws2_getnameinfo && ws2_getaddrinfo && ws2_freeaddrinfo ) - { - /* got them */ - return 1; - } - FreeLibrary( module ); - - /* For Windows 2000 and below, try IPv6 stack in in WSHIP6.DLL */ - module = LoadLibrary( "wship6.dll" ); - if( module != NULL ) - { - ws2_getnameinfo = (GETNAMEINFO)GetProcAddress( module, "getnameinfo" ); - ws2_getaddrinfo = (GETADDRINFO)GetProcAddress( module, "getaddrinfo" ); - ws2_freeaddrinfo = (FREEADDRINFO)GetProcAddress( module, "freeaddrinfo" ); - if( ws2_getnameinfo && ws2_getaddrinfo && ws2_freeaddrinfo ) - { - /* got them */ - return 1; - } - FreeLibrary( module ); - } - } - /* no API */ - return 0; -} + case HOST_NOT_FOUND: + return EAI_NONAME; -static WINAPI int _ws2_getnameinfo_bind( const struct sockaddr *sa, socklen_t salen, - char *host, DWORD hostlen, char *serv, DWORD servlen, int flags ) -{ - if( _ws2_find_ipv6_api() ) - { - return ws2_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); - } - /* return a possible error if API is not found */ - WSASetLastError(WSAHOST_NOT_FOUND); - return WSAHOST_NOT_FOUND; -} + case NO_ADDRESS: +# if (NO_ADDRESS != NO_DATA) + case NO_DATA: +# endif + return EAI_NODATA; -static WINAPI int _ws2_getaddrinfo_bind(const char *node, const char *service, - const struct addrinfo *hints, struct addrinfo **res) -{ - if( _ws2_find_ipv6_api() ) - { - return ws2_getaddrinfo(node, service, hints, res); - } - /* return a possible error if API is not found */ - WSASetLastError(WSAHOST_NOT_FOUND); - return WSAHOST_NOT_FOUND; -} + case NO_RECOVERY: + return EAI_FAIL; -static WINAPI void _ws2_freeaddrinfo_bind( struct addrinfo *infos ) -{ - if( _ws2_find_ipv6_api() ) - { - ws2_freeaddrinfo(infos); + case TRY_AGAIN: + return EAI_AGAIN; } + return EAI_SYSTEM; } -#endif - -#ifndef HAVE_GETADDRINFO /* * This functions must be used to free the memory allocated by getaddrinfo(). */ -static void -__freeaddrinfo (struct addrinfo *res) +#ifdef WIN32 +static void WSAAPI freeaddrinfo (struct addrinfo *res) +#else +static void freeaddrinfo (struct addrinfo *res) +#endif { if (res != NULL) { @@ -372,16 +281,22 @@ makeipv4info (int type, int proto, u_long ip, u_short port, const char *name) /* * getaddrinfo() non-thread-safe IPv4-only implementation - * Address-family-independant hostname to address resolution. + * Address-family-independent hostname to address resolution. * * This is meant for IPv6-unaware systems that do probably not provide * getaddrinfo(), but still have old function gethostbyname(). * * Only UDP and TCP over IPv4 are supported here. */ +#ifdef WIN32 +static int WSAAPI +getaddrinfo (const char *node, const char *service, + const struct addrinfo *hints, struct addrinfo **res) +#else static int -__getaddrinfo (const char *node, const char *service, - const struct addrinfo *hints, struct addrinfo **res) +getaddrinfo (const char *node, const char *service, + const struct addrinfo *hints, struct addrinfo **res) +#endif { struct addrinfo *info; u_long ip; @@ -469,7 +384,7 @@ __getaddrinfo (const char *node, const char *service, entry = gethostbyname (node); if (entry == NULL) - return EAI_NONAME; + return gai_error_from_herrno (); if ((entry->h_length != 4) || (entry->h_addrtype != AF_INET)) return EAI_FAMILY; @@ -487,38 +402,14 @@ __getaddrinfo (const char *node, const char *service, port = 0; else { - long d; + unsigned long d; char *end; d = strtoul (service, &end, 0); - if (end[0] /* service is not a number */ - || (d > 65535)) - { - struct servent *entry; - const char *protoname; - - switch (protocol) - { - case IPPROTO_TCP: - protoname = "tcp"; - break; - - case IPPROTO_UDP: - protoname = "udp"; - break; - - default: - protoname = NULL; - } - - entry = getservbyname (service, protoname); - if (entry == NULL) - return EAI_SERVICE; + if (end[0] || (d > 65535u)) + return EAI_SERVICE; - port = entry->s_port; - } - else - port = htons ((u_short)d); + port = htons ((u_short)d); } /* building results... */ @@ -552,6 +443,98 @@ __getaddrinfo (const char *node, const char *service, } #endif /* if !HAVE_GETADDRINFO */ +#if defined( WIN32 ) && !defined( UNDER_CE ) + /* + * Here is the kind of kludge you need to keep binary compatibility among + * varying OS versions... + */ +typedef int (WSAAPI * GETNAMEINFO) ( const struct sockaddr FAR *, socklen_t, + char FAR *, DWORD, char FAR *, DWORD, int ); +typedef int (WSAAPI * GETADDRINFO) (const char FAR *, const char FAR *, + const struct addrinfo FAR *, + struct addrinfo FAR * FAR *); + +typedef void (WSAAPI * FREEADDRINFO) ( struct addrinfo FAR * ); + +static int WSAAPI _ws2_getnameinfo_bind ( const struct sockaddr FAR *, socklen_t, + char FAR *, DWORD, char FAR *, DWORD, int ); +static int WSAAPI _ws2_getaddrinfo_bind (const char FAR *, const char FAR *, + const struct addrinfo FAR *, + struct addrinfo FAR * FAR *); + +static GETNAMEINFO ws2_getnameinfo = _ws2_getnameinfo_bind; +static GETADDRINFO ws2_getaddrinfo = _ws2_getaddrinfo_bind; +static FREEADDRINFO ws2_freeaddrinfo; + +static FARPROC ws2_find_api (LPCTSTR name) +{ + FARPROC f = NULL; + + HMODULE m = GetModuleHandle (TEXT("WS2_32")); + if (m != NULL) + f = GetProcAddress (m, name); + + if (f == NULL) + { + /* Windows 2K IPv6 preview */ + m = LoadLibrary (TEXT("WSHIP6")); + if (m != NULL) + f = GetProcAddress (m, name); + } + + return f; +} + +static WSAAPI int _ws2_getnameinfo_bind( const struct sockaddr FAR * sa, socklen_t salen, + char FAR *host, DWORD hostlen, char FAR *serv, DWORD servlen, int flags ) +{ + GETNAMEINFO entry = (GETNAMEINFO)ws2_find_api (TEXT("getnameinfo")); + int result; + + if (entry == NULL) + { + /* not found, use replacement API instead */ + entry = getnameinfo; + + } + /* call API before replacing function pointer to avoid crash */ + result = entry (sa, salen, host, hostlen, serv, servlen, flags); + ws2_getnameinfo = entry; + return result; +} +#undef getnameinfo +#define getnameinfo ws2_getnameinfo + +static WSAAPI int _ws2_getaddrinfo_bind(const char FAR *node, const char FAR *service, + const struct addrinfo FAR *hints, struct addrinfo FAR * FAR *res) +{ + GETADDRINFO entry; + FREEADDRINFO freentry; + int result; + + entry = (GETADDRINFO)ws2_find_api (TEXT("getaddrinfo")); + freentry = (FREEADDRINFO)ws2_find_api (TEXT("freeaddrinfo")); + + if ((entry == NULL) || (freentry == NULL)) + { + /* not found, use replacement API instead */ + entry = getaddrinfo; + freentry = freeaddrinfo; + } + /* call API before replacing function pointer to avoid crash */ + result = entry (node, service, hints, res); + ws2_freeaddrinfo = freentry; + ws2_getaddrinfo = entry; + return result; +} +#undef getaddrinfo +#undef freeaddrinfo +#define getaddrinfo ws2_getaddrinfo +#define freeaddrinfo ws2_freeaddrinfo +#define HAVE_GETADDRINFO +#endif + + int vlc_getnameinfo( const struct sockaddr *sa, int salen, char *host, int hostlen, int *portnum, int flags ) @@ -570,30 +553,8 @@ int vlc_getnameinfo( const struct sockaddr *sa, int salen, psz_serv = NULL; i_servlen = 0; } -#if defined( WIN32 ) && !defined( UNDER_CE ) - i_val = ws2_getnameinfo( sa, salen, host, hostlen, psz_serv, - i_servlen, flags ); -#endif -#if defined( HAVE_GETNAMEINFO ) || defined( UNDER_CE ) - i_val = getnameinfo(sa, salen, host, hostlen, psz_serv, i_servlen, flags); -#else - { -# ifdef HAVE_USABLE_MUTEX_THAT_DONT_NEED_LIBVLC_POINTER - static vlc_value_t lock; - /* my getnameinfo implementation is not thread-safe as it uses - * gethostbyaddr and the likes */ - vlc_mutex_lock( lock.p_address ); -#else -# warning FIXME : This is not thread-safe! -#endif - i_val = __getnameinfo( sa, salen, host, hostlen, psz_serv, i_servlen, - flags ); -# ifdef HAVE_USABLE_MUTEX_THAT_DONT_NEED_LIBVLC_POINTER - vlc_mutex_unlock( lock.p_address ); -# endif - } -#endif + i_val = getnameinfo(sa, salen, host, hostlen, psz_serv, i_servlen, flags); if( portnum != NULL ) *portnum = atoi( psz_serv ); @@ -602,7 +563,6 @@ int vlc_getnameinfo( const struct sockaddr *sa, int salen, } -/* TODO: support for setting sin6_scope_id */ int vlc_getaddrinfo( vlc_object_t *p_this, const char *node, int i_port, const struct addrinfo *p_hints, struct addrinfo **res ) @@ -624,26 +584,51 @@ int vlc_getaddrinfo( vlc_object_t *p_this, const char *node, snprintf( psz_service, 6, "%d", i_port ); /* Check if we have to force ipv4 or ipv6 */ - if( p_hints == NULL ) - memset( &hints, 0, sizeof( hints ) ); - else - memcpy( &hints, p_hints, sizeof( hints ) ); - - if( hints.ai_family == AF_UNSPEC ) + memset (&hints, 0, sizeof (hints)); + if (p_hints != NULL) { - vlc_value_t val; + const int safe_flags = + AI_PASSIVE | + AI_CANONNAME | + AI_NUMERICHOST | +#ifdef AI_NUMERICSERV + AI_NUMERICSERV | +#endif +#ifdef AI_ALL + AI_ALL | +#endif +#ifdef AI_ADDRCONFIG + AI_ADDRCONFIG | +#endif +#ifdef AI_V4MAPPED + AI_V4MAPPED | +#endif + 0; + + hints.ai_family = p_hints->ai_family; + hints.ai_socktype = p_hints->ai_socktype; + hints.ai_protocol = p_hints->ai_protocol; + /* Unfortunately, some flags chang the layout of struct addrinfo, so + * they cannot be copied blindly from p_hints to &hints. Therefore, we + * only copy flags that we know for sure are "safe". + */ + hints.ai_flags = p_hints->ai_flags & safe_flags; + } - var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Get( p_this, "ipv4", &val ); - if( val.b_bool ) - hints.ai_family = AF_INET; +#ifdef AI_NUMERICSERV + /* We only ever use port *numbers* */ + hints.ai_flags |= AI_NUMERICSERV; +#endif + if( hints.ai_family == AF_UNSPEC ) + { #ifdef AF_INET6 - var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Get( p_this, "ipv6", &val ); - if( val.b_bool ) + if (var_CreateGetBool (p_this, "ipv6")) hints.ai_family = AF_INET6; + else #endif + if (var_CreateGetBool (p_this, "ipv4")) + hints.ai_family = AF_INET; } /* @@ -674,76 +659,36 @@ int vlc_getaddrinfo( vlc_object_t *p_this, const char *node, } } -#if defined( WIN32 ) && !defined( UNDER_CE ) +#ifdef WIN32 /* * Winsock tries to resolve numerical IPv4 addresses as AAAA - * and IPv6 addresses as A... There comes the work around. + * and IPv6 addresses as A... There comes the bug-to-bug fix. */ if ((hints.ai_flags & AI_NUMERICHOST) == 0) { hints.ai_flags |= AI_NUMERICHOST; - if (ws2_getaddrinfo (psz_node, psz_service, &hints, res) == 0) + if (getaddrinfo (psz_node, psz_service, &hints, res) == 0) return 0; hints.ai_flags &= ~AI_NUMERICHOST; } - - return ws2_getaddrinfo (psz_node, psz_service, &hints, res); #endif -#if defined( HAVE_GETADDRINFO ) || defined( UNDER_CE ) -# ifdef AI_IDN +#ifdef AI_IDN /* Run-time I18n Domain Names support */ - { - static vlc_bool_t i_idn = VLC_TRUE; /* beware of thread-safety */ - - if( i_idn ) - { - int i_ret; - - hints.ai_flags |= AI_IDN; - i_ret = getaddrinfo( psz_node, psz_service, &hints, res ); - - if( i_ret != EAI_BADFLAGS ) - return i_ret; - - /* libidn not available: disable and retry without it */ - - /* NOTE: Using i_idn here would not be thread-safe */ - hints.ai_flags &= ~AI_IDN; - i_idn = VLC_FALSE; - msg_Dbg( p_this, "International Domain Names not supported - " \ - "disabled" ); - } - } -# endif - return getaddrinfo( psz_node, psz_service, &hints, res ); -#else -{ - int i_ret; - - vlc_value_t lock; + hints.ai_flags |= AI_IDN; + int ret = getaddrinfo (psz_node, psz_service, &hints, res); + if (ret != EAI_BADFLAGS) + return ret; - var_Create( p_this->p_libvlc, "getaddrinfo_mutex", VLC_VAR_MUTEX ); - var_Get( p_this->p_libvlc, "getaddrinfo_mutex", &lock ); - vlc_mutex_lock( lock.p_address ); - - i_ret = __getaddrinfo( psz_node, psz_service, &hints, res ); - vlc_mutex_unlock( lock.p_address ); - return i_ret; -} + /* IDN not available: disable and retry without it */ + hints.ai_flags &= ~AI_IDN; #endif + return getaddrinfo (psz_node, psz_service, &hints, res); } void vlc_freeaddrinfo( struct addrinfo *infos ) { -#if defined( WIN32 ) && !defined( UNDER_CE ) - ws2_freeaddrinfo( infos ); -#endif -#if defined( HAVE_GETADDRINFO ) || defined( UNDER_CE ) - freeaddrinfo( infos ); -#else - __freeaddrinfo( infos ); -#endif + freeaddrinfo (infos); }