From 0db787bf5836f8f2a78dd92ec83bd8d358f91153 Mon Sep 17 00:00:00 2001 From: janwas Date: Mon, 4 Jun 2007 23:55:29 +0000 Subject: [PATCH] wsock: fix init problem (if getnameinfo et al were the first function called from winsock - as happens when using quickstart - ws2_32 wasn't loaded first) This was SVN commit r5142. --- source/lib/sysdep/win/wdbg.cpp | 6 +- source/lib/sysdep/win/wposix/wsock.cpp | 69 ++++++++++++------- source/lib/sysdep/win/wposix/wsock.h | 8 ++- source/network/SocketBase.cpp | 95 ++++++++++++-------------- 4 files changed, 96 insertions(+), 82 deletions(-) diff --git a/source/lib/sysdep/win/wdbg.cpp b/source/lib/sysdep/win/wdbg.cpp index 98a88e8e70..3c36efe4eb 100644 --- a/source/lib/sysdep/win/wdbg.cpp +++ b/source/lib/sysdep/win/wdbg.cpp @@ -671,7 +671,8 @@ LONG WINAPI wdbg_exception_filter(EXCEPTION_POINTERS* ep) //----------------------------------------------------------------------------- // install SEH exception handler -typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE)(_EXCEPTION_RECORD* ExceptionRecord, PVOID EstablisherFrame, _CONTEXT* ContextRecord, PVOID DispatcherContext); +typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE)(_EXCEPTION_RECORD* ExceptionRecord, + PVOID EstablisherFrame, _CONTEXT* ContextRecord, PVOID DispatcherContext); struct _EXCEPTION_REGISTRATION_RECORD { @@ -684,7 +685,8 @@ static bool IsUnwinding(DWORD exceptionFlags) return (exceptionFlags & 2) != 0; } -static EXCEPTION_DISPOSITION ExceptionHandler(_EXCEPTION_RECORD* ExceptionRecord, PVOID EstablisherFrame, _CONTEXT* ContextRecord, PVOID DispatcherContext) +static EXCEPTION_DISPOSITION ExceptionHandler(_EXCEPTION_RECORD* ExceptionRecord, + PVOID UNUSED(EstablisherFrame), _CONTEXT* ContextRecord, PVOID UNUSED(DispatcherContext)) { if(!IsUnwinding(ExceptionRecord->ExceptionFlags)) { diff --git a/source/lib/sysdep/win/wposix/wsock.cpp b/source/lib/sysdep/win/wposix/wsock.cpp index 1050dc8531..4315206c25 100644 --- a/source/lib/sysdep/win/wposix/wsock.cpp +++ b/source/lib/sysdep/win/wposix/wsock.cpp @@ -23,10 +23,6 @@ WINIT_REGISTER_MAIN_INIT(wsock_Init); WINIT_REGISTER_MAIN_SHUTDOWN(wsock_Shutdown); -uint16_t htons(uint16_t s) -{ - return (s >> 8) | ((s & 0xff) << 8); -} // IPv6 globals // These are included in the linux C libraries and in newer platform SDKs, @@ -36,56 +32,79 @@ const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; // ::_1 //----------------------------------------------------------------------------- -// manual import of certain functions +// 'optional' IPv6 routines -// don't delay-load because we don't want to require these functions to be -// present. the user must be able to check if they are available (currently, -// on Win2k with IPv6 update or WinXP). can't use compile-time HAVE_* to -// make that decision because we don't want to distribute a separate EXE. +// we hide the function pointers behind stub functions - this avoids +// surprising users. speed is irrelevant here. manually writing these stubs +// is ugly, but delay-load error handling is hairy, so don't use that. +// +// the first call of these stubs must trigger wsock_ActualInit in case no +// other winsock function was called yet. we can't simply rely on +// ModuleShouldInitialize because taking references prevents shutdown. +// adding an extra haveInitialized flag would be redundant. instead, +// enter a clever but safe hack: we call a harmless winsock function that +// triggers the delay load or does nothing if init has already happened. -// function pointers, automatically initialized before any use of ws2_32.dll static int (WINAPI *pgetnameinfo)(const struct sockaddr*, socklen_t, char*, socklen_t, char*, socklen_t, unsigned int); static int (WINAPI *pgetaddrinfo)(const char*, const char*, const struct addrinfo*, struct addrinfo**); static void (WINAPI *pfreeaddrinfo)(struct addrinfo*); -static HMODULE hWs2_32Dll; - -static void ImportOptionalFunctions() -{ - *(void**)&pgetnameinfo = GetProcAddress(hWs2_32Dll, "getnameinfo"); - *(void**)&pgetaddrinfo = GetProcAddress(hWs2_32Dll, "getaddrinfo"); - *(void**)&pfreeaddrinfo = GetProcAddress(hWs2_32Dll, "freeaddrinfo"); -} - int getnameinfo(const struct sockaddr* sa, socklen_t salen, char* host, socklen_t hostlen, char* serv, socklen_t servlen, unsigned int flags) { + (void)htonl(0); // trigger init if not done already + if(!pgetnameinfo) + { + errno = ENOSYS; + return -1; + } + return pgetnameinfo(sa, salen, host, hostlen, serv, servlen, flags); } int getaddrinfo(const char* nodename, const char* servname, const struct addrinfo* hints, struct addrinfo** res) { + (void)htonl(0); // trigger init if not done already + if(!pgetaddrinfo) + { + errno = ENOSYS; + return -1; + } + return pgetaddrinfo(nodename, servname, hints, res); } void freeaddrinfo(struct addrinfo* ai) { + // (no dummy htonl call or checking of the function pointer is needed + // since getaddrinfo must succeed to get a valid addrinfo*.) + pfreeaddrinfo(ai); } +static void ImportOptionalFunctions() +{ + // (by the time we get here, ws2_32.dll will have been loaded, so + // this isn't the only reference and can be freed immediately) + HMODULE hWs2_32Dll = LoadLibrary("ws2_32.dll"); + *(void**)&pgetnameinfo = GetProcAddress(hWs2_32Dll, "getnameinfo"); + *(void**)&pgetaddrinfo = GetProcAddress(hWs2_32Dll, "getaddrinfo"); + *(void**)&pfreeaddrinfo = GetProcAddress(hWs2_32Dll, "freeaddrinfo"); + FreeLibrary(hWs2_32Dll); +} + + //----------------------------------------------------------------------------- static ModuleInitState initState; // called from delay loader the first time a wsock function is called // (shortly before the actual wsock function is called). -static LibError wsock_actual_init() +static LibError wsock_ActualInit() { if(!ModuleShouldInitialize(&initState)) return INFO::OK; - hWs2_32Dll = LoadLibrary("ws2_32.dll"); - char d[1024]; int ret = WSAStartup(0x0002, d); // want 2.0 debug_assert(ret == 0); @@ -97,8 +116,8 @@ static LibError wsock_actual_init() static LibError wsock_Init() { - // trigger wsock_actual_init when someone first calls a wsock function. - static WdllLoadNotify loadNotify = { "ws2_32", wsock_actual_init }; + // trigger wsock_ActualInit when someone first calls a winsock function. + static WdllLoadNotify loadNotify = { "ws2_32", wsock_ActualInit }; wdll_add_notify(&loadNotify); return INFO::OK; } @@ -112,7 +131,5 @@ static LibError wsock_Shutdown() int ret = WSACleanup(); debug_assert(ret >= 0); - FreeLibrary(hWs2_32Dll); - return INFO::OK; } diff --git a/source/lib/sysdep/win/wposix/wsock.h b/source/lib/sysdep/win/wposix/wsock.h index 9dbacfad9b..2d21fade5f 100644 --- a/source/lib/sysdep/win/wposix/wsock.h +++ b/source/lib/sysdep/win/wposix/wsock.h @@ -169,6 +169,8 @@ struct addrinfo #define NI_MAXHOST 1025 #define NI_MAXSERV 32 +// these functions are only supported on WinXP+, and Win2k with IPv6 update. +// otherwise, they return -1 with errno = ENOSYS. extern int getnameinfo(const struct sockaddr*, socklen_t, char*, socklen_t, char*, socklen_t, unsigned int); extern int getaddrinfo(const char*, const char*, const struct addrinfo*, struct addrinfo**); extern void freeaddrinfo(struct addrinfo*); @@ -183,10 +185,10 @@ extern void freeaddrinfo(struct addrinfo*); // // -extern uint16_t htons(uint16_t hostshort); -#define ntohs htons +IMP(unsigned short, htons, (unsigned short hostlong)) +IMP(unsigned short, ntohs, (unsigned short hostlong)) IMP(unsigned long, htonl, (unsigned long hostlong)) - +IMP(unsigned long, ntohl, (unsigned long hostlong)) IMP(in_addr_t, inet_addr, (const char*)) IMP(char*, inet_ntoa, (in_addr)) diff --git a/source/network/SocketBase.cpp b/source/network/SocketBase.cpp index a4123d4269..d3539bf6a6 100644 --- a/source/network/SocketBase.cpp +++ b/source/network/SocketBase.cpp @@ -106,67 +106,60 @@ CSocketAddress CSocketAddress::Loopback(int port, ESocketProtocol proto) PS_RESULT CSocketAddress::Resolve(const char *name, int port, CSocketAddress &addr) { - if ((getaddrinfo) != NULL) + addrinfo *ai; + int res=getaddrinfo(name, NULL, NULL, &ai); + if (res == 0) { - addrinfo *ai; - int res=getaddrinfo(name, NULL, NULL, &ai); - if (res == 0) + if (ai->ai_addrlen < sizeof(addr.m_Union)) + cpu_memcpy(&addr.m_Union, ai->ai_addr, ai->ai_addrlen); + switch (addr.m_Union.m_Family) { - if (ai->ai_addrlen < sizeof(addr.m_Union)) - cpu_memcpy(&addr.m_Union, ai->ai_addr, ai->ai_addrlen); - switch (addr.m_Union.m_Family) - { - case IPv4: - addr.m_Union.m_IPv4.sin_port=htons(port); - break; - case IPv6: - addr.m_Union.m_IPv6.sin6_port=htons(port); - break; - } - freeaddrinfo(ai); - return PS_OK; - } - else - return NO_SUCH_HOST; - } - else - { - hostent *he; - - addr.m_Union.m_IPv4.sin_family=AF_INET; - addr.m_Union.m_IPv4.sin_port=htons(port); - // Try to parse dot-notation IP - addr.m_Union.m_IPv4.sin_addr.s_addr=inet_addr(name); - if (addr.m_Union.m_IPv4.sin_addr.s_addr==INADDR_NONE) // Not a dotted IP, try name resolution - { - he=gethostbyname(name); - if (!he) - { - return NO_SUCH_HOST; - } - addr.m_Union.m_IPv4.sin_addr=*(struct in_addr *)(he->h_addr_list[0]); + case IPv4: + addr.m_Union.m_IPv4.sin_port=htons(port); + break; + case IPv6: + addr.m_Union.m_IPv6.sin6_port=htons(port); + break; } + freeaddrinfo(ai); return PS_OK; } + // supported, but failed + if (errno != ENOSYS) + return NO_SUCH_HOST; + // else: IPv6 not supported, fall back to IPv4 + + hostent *he; + + addr.m_Union.m_IPv4.sin_family=AF_INET; + addr.m_Union.m_IPv4.sin_port=htons(port); + // Try to parse dot-notation IP + addr.m_Union.m_IPv4.sin_addr.s_addr=inet_addr(name); + if (addr.m_Union.m_IPv4.sin_addr.s_addr==INADDR_NONE) // Not a dotted IP, try name resolution + { + he=gethostbyname(name); + if (!he) + return NO_SUCH_HOST; + addr.m_Union.m_IPv4.sin_addr=*(struct in_addr *)(he->h_addr_list[0]); + } + return PS_OK; } CStr CSocketAddress::GetString() const { char convBuf[NI_MAXHOST]; - if ((getnameinfo) != NULL) - { - int res=getnameinfo((sockaddr *)&m_Union, sizeof(m_Union), convBuf, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - if (res == 0) - { - return CStr(convBuf); - } - // getnameinfo won't return a string for the IPv6 unspecified address - else if (m_Union.m_Family == IPv6 && res==EAI_NONAME) - return "::"; - else - return ""; - } - else if (m_Union.m_Family == IPv4) + int res=getnameinfo((sockaddr *)&m_Union, sizeof(m_Union), convBuf, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (res == 0) + return CStr(convBuf); + // getnameinfo won't return a string for the IPv6 unspecified address + else if (m_Union.m_Family == IPv6 && res==EAI_NONAME) + return "::"; + // supported, but failed + else if (errno != ENOSYS) + return ""; + // else: IPv6 not supported, fall back to IPv4 + + if (m_Union.m_Family == IPv4) { sprintf(convBuf, "%d.%d.%d.%d", m_Union.m_IPv4.sin_addr.s_addr&0xff,