/*++ Copyright (c) 1994 Microsoft Corporation Module Name: iwinsock.cxx Abstract: Contains functions to load sockets DLL and entry points. Functions and data in this module take care of indirecting sockets calls, hence _I_ in front of the function name Contents: IwinsockInitialize IwinsockTerminate LoadWinsock UnloadWinsock SafeCloseSocket Author: Richard L Firth (rfirth) 12-Apr-1995 Environment: Win32(s) user-mode DLL Revision History: 12-Apr-1995 rfirth Created 08-May-1996 arthurbi Added support for Socks Firewalls. 05-Mar-1998 rfirth Moved SOCKS support into ICSocket class. Removed SOCKS library loading/unloading from this module (revert to pre-SOCKS) --*/ #include #if defined(__cplusplus) extern "C" { #endif #if INET_DEBUG #if defined(RETAIL_LOGGING) #define DPRINTF (void) #else #define DPRINTF dprintf #endif VOID InitDebugSock( VOID ); VOID TerminateDebugSock( VOID ); #else #define DPRINTF (void) #endif // // private types // typedef struct { #if defined(unix) LPSTR FunctionOrdinal; #else DWORD FunctionOrdinal; #endif /* unix */ FARPROC * FunctionAddress; } SOCKETS_FUNCTION; // // global data // GLOBAL SOCKET (PASCAL FAR * _I_accept)( SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen ) = NULL; GLOBAL int (PASCAL FAR * _I_bind)( SOCKET s, const struct sockaddr FAR *addr, int namelen ) = NULL; GLOBAL int (PASCAL FAR * _I_closesocket)( SOCKET s ) = NULL; GLOBAL int (PASCAL FAR * _I_connect)( SOCKET s, const struct sockaddr FAR *name, int namelen ) = NULL; GLOBAL int (PASCAL FAR * _I_gethostname)( char FAR * name, int namelen ) = NULL; GLOBAL LPHOSTENT (PASCAL FAR * _I_gethostbyname)( const char FAR * lpHostName ) = NULL; GLOBAL int (PASCAL FAR * _I_getsockname)( SOCKET s, struct sockaddr FAR *name, int FAR * namelen ) = NULL; GLOBAL int (PASCAL FAR * _I_getpeername)( SOCKET s, struct sockaddr FAR *name, int FAR * namelen ) = NULL; GLOBAL int (PASCAL FAR * _I_getsockopt)( SOCKET s, int level, int optname, char FAR * optval, int FAR *optlen ); GLOBAL u_long (PASCAL FAR * _I_htonl)( u_long hostlong ) = NULL; GLOBAL u_short (PASCAL FAR * _I_htons)( u_short hostshort ) = NULL; GLOBAL unsigned long (PASCAL FAR * _I_inet_addr)( const char FAR * cp ) = NULL; GLOBAL char FAR * (PASCAL FAR * _I_inet_ntoa)( struct in_addr in ) = NULL; GLOBAL int (PASCAL FAR * _I_ioctlsocket)( SOCKET s, long cmd, u_long FAR *argp ) = NULL; GLOBAL int (PASCAL FAR * _I_listen)( SOCKET s, int backlog ) = NULL; GLOBAL u_short (PASCAL FAR * _I_ntohs)( u_short netshort ) = NULL; GLOBAL int (PASCAL FAR * _I_recv)( SOCKET s, char FAR * buf, int len, int flags ) = NULL; GLOBAL int (PASCAL FAR * _I_recvfrom)( SOCKET s, char FAR * buf, int len, int flags, struct sockaddr FAR *from, int FAR * fromlen ) = NULL; GLOBAL int (PASCAL FAR * _I_select)( int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout ) = NULL; GLOBAL int (PASCAL FAR * _I_send)( SOCKET s, const char FAR * buf, int len, int flags ) = NULL; GLOBAL int (PASCAL FAR * _I_sendto)( SOCKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR *to, int tolen ) = NULL; GLOBAL int (PASCAL FAR * _I_setsockopt)( SOCKET s, int level, int optname, const char FAR * optval, int optlen ) = NULL; GLOBAL int (PASCAL FAR * _I_shutdown)( SOCKET s, int how ) = NULL; GLOBAL SOCKET (PASCAL FAR * _I_socket)( int af, int type, int protocol ) = NULL; GLOBAL int (PASCAL FAR * _I_WSAStartup)( WORD wVersionRequired, LPWSADATA lpWSAData ) = NULL; GLOBAL int (PASCAL FAR * _I_WSACleanup)( void ) = NULL; GLOBAL int (PASCAL FAR * _I_WSAGetLastError)( void ) = NULL; GLOBAL void (PASCAL FAR * _I_WSASetLastError)( int iError ) = NULL; GLOBAL int (PASCAL FAR * _I___WSAFDIsSet)( SOCKET, fd_set FAR * ) = NULL; GLOBAL int (PASCAL FAR * _I_getaddrinfo)( IN const char FAR * nodename, IN const char FAR * servname, IN const struct addrinfo FAR * hints, OUT struct addrinfo FAR * FAR * res ) = NULL; GLOBAL void (PASCAL FAR * _I_freeaddrinfo)( IN struct addrinfo *ai ) = NULL; GLOBAL int (PASCAL FAR * _I_getnameinfo)( IN const struct sockaddr FAR * sa, IN socklen_t salen, OUT char FAR * host, IN size_t hostlen, OUT char FAR * serv, IN size_t servlen, IN int flags ) = NULL; GLOBAL int (PASCAL FAR * _I_WSALookupServiceBeginW)( IN LPWSAQUERYSETW lpqsRestrictions, IN DWORD dwControlFlags, OUT LPHANDLE lphLookup ) = NULL; GLOBAL int (PASCAL FAR * _I_WSALookupServiceNextW)( IN HANDLE hLookup, IN DWORD dwControlFlags, IN OUT LPDWORD lpdwBufferLength, OUT LPWSAQUERYSETW lpqsResults ) = NULL; GLOBAL int (PASCAL FAR * _I_WSALookupServiceEnd)( IN HANDLE hLookup ) = NULL; GLOBAL int (PASCAL FAR * _I_WSANSPIoctl)( IN HANDLE hLookup, IN DWORD dwControlCode, IN LPVOID lpvInBuffer, IN DWORD cbInBuffer, OUT LPVOID lpvOutBuffer, IN DWORD cbOutBuffer, OUT LPDWORD lpcbBytesReturned, IN LPWSACOMPLETION lpCompletion ) = NULL; #if INET_DEBUG void SetupSocketsTracing(void); #endif // // private data // // // InitializationLock - protects against multiple threads loading WSOCK32.DLL // and entry points // PRIVATE CRITICAL_SECTION InitializationLock = {0}; // // hWinsock - NULL when WSOCK32.DLL is not loaded // hWs2_32 - NULL when WS2_32.DLL is not loaded. // hAddrResLib - NULL when WS2_32.DLL/WSHIPV6.DLL is not loaded // If WS2_32.DLL is loaded in the process and it has IPv6 entry points, // hAddrResLib==hWs2_32, otherwise it is handle to WSHIPV6.DLL if // the latter is present and usable (IPv6 is present). // PRIVATE HINSTANCE hWinsock = NULL; PRIVATE HINSTANCE hWs2_32 = NULL; PRIVATE HINSTANCE hAddrResLib = NULL; // // WinsockLoadCount - the number of times we have made calls to LoadWinsock() // and UnloadWinsock(). When this reaches 0 (again), we can unload the Winsock // DLL for real // PRIVATE DWORD WinsockLoadCount = 0; // // SocketsFunctions - this is the list of entry points in WSOCK32.DLL that we // need to load for WININET.DLL // #if !defined(unix) PRIVATE SOCKETS_FUNCTION SocketsFunctions[] = { 1, (FARPROC*)&_I_accept, 2, (FARPROC*)&_I_bind, 3, (FARPROC*)&_I_closesocket, 4, (FARPROC*)&_I_connect, 5, (FARPROC*)&_I_getpeername, 6, (FARPROC*)&_I_getsockname, 7, (FARPROC*)&_I_getsockopt, 8, (FARPROC*)&_I_htonl, 9, (FARPROC*)&_I_htons, 10, (FARPROC*)&_I_inet_addr, 11, (FARPROC*)&_I_inet_ntoa, 12, (FARPROC*)&_I_ioctlsocket, 13, (FARPROC*)&_I_listen, 15, (FARPROC*)&_I_ntohs, 16, (FARPROC*)&_I_recv, 17, (FARPROC*)&_I_recvfrom, 18, (FARPROC*)&_I_select, 19, (FARPROC*)&_I_send, 20, (FARPROC*)&_I_sendto, 21, (FARPROC*)&_I_setsockopt, 22, (FARPROC*)&_I_shutdown, 23, (FARPROC*)&_I_socket, 52, (FARPROC*)&_I_gethostbyname, 57, (FARPROC*)&_I_gethostname, 111, (FARPROC*)&_I_WSAGetLastError, 112, (FARPROC*)&_I_WSASetLastError, 115, (FARPROC*)&_I_WSAStartup, 116, (FARPROC*)&_I_WSACleanup, 151, (FARPROC*)&_I___WSAFDIsSet }; #else PRIVATE SOCKETS_FUNCTION SocketsFunctions[] = { "MwAccept", (FARPROC*)&_I_accept, "MwBind", (FARPROC*)&_I_bind, "closesocket", (FARPROC*)&_I_closesocket, "MwConnect", (FARPROC*)&_I_connect, "MwGetpeername", (FARPROC*)&_I_getpeername, "MwGetsockname", (FARPROC*)&_I_getsockname, "MwGetsockopt", (FARPROC*)&_I_getsockopt, "MwHtonl", (FARPROC*)&_I_htonl, "MwHtons", (FARPROC*)&_I_htons, "MwInet_addr", (FARPROC*)&_I_inet_addr, "MwInet_ntoa", (FARPROC*)&_I_inet_ntoa, "ioctlsocket", (FARPROC*)&_I_ioctlsocket, "MwListen", (FARPROC*)&_I_listen, "MwNtohs", (FARPROC*)&_I_ntohs, "MwRecv", (FARPROC*)&_I_recv, "MwRecvfrom", (FARPROC*)&_I_recvfrom, "MwSelect", (FARPROC*)&_I_select, "MwSend", (FARPROC*)&_I_send, "MwSendto", (FARPROC*)&_I_sendto, "MwSetsockopt", (FARPROC*)&_I_setsockopt, "MwShutdown", (FARPROC*)&_I_shutdown, "MwSocket", (FARPROC*)&_I_socket, "MwGethostbyname", (FARPROC*)&_I_gethostbyname, "MwGethostname", (FARPROC*)&_I_gethostname, "WSAGetLastError", (FARPROC*)&_I_WSAGetLastError, "WSASetLastError", (FARPROC*)&_I_WSASetLastError, "WSAStartup", (FARPROC*)&_I_WSAStartup, "WSACleanup", (FARPROC*)&_I_WSACleanup, #if 0 "", (FARPROC*)&_I___WSAFDIsSet #endif }; #endif /* unix */ // // private prototypes // #if INET_DEBUG void SetupSocketsTracing(void); #endif int PASCAL FAR LimitedGetAddrInfo( IN const char FAR * nodename, IN const char FAR * servname, IN const struct addrinfo FAR * hints, OUT struct addrinfo FAR * FAR * res ); void PASCAL FAR LimitedFreeAddrInfo( IN struct addrinfo *ai ); int PASCAL FAR LimitedGetNameInfo( IN const struct sockaddr FAR * sa, IN socklen_t salen, OUT char FAR * host, IN size_t hostlen, OUT char FAR * serv, IN size_t servlen, IN int flags ); // // functions // VOID IwinsockInitialize( VOID ) /*++ Routine Description: Performs initialization/resource allocation for this module Arguments: None. Return Value: None. --*/ { // // initialize the critical section that protects against multiple threads // trying to initialize Winsock // InitializeCriticalSection(&InitializationLock); #if INET_DEBUG InitDebugSock(); #endif } VOID IwinsockTerminate( VOID ) /*++ Routine Description: Performs termination & resource cleanup for this module Arguments: None. Return Value: None. --*/ { DeleteCriticalSection(&InitializationLock); #if INET_DEBUG TerminateDebugSock(); #endif } DWORD LoadWinsock( VOID ) /*++ Routine Description: Dynamically loads Windows sockets library Arguments: None. Return Value: DWORD Success - ERROR_SUCCESS Failure - Win32 error e.g. LoadLibrary() failure WSA error e.g. WSAStartup() failure --*/ { DEBUG_ENTER((DBG_SOCKETS, Dword, "LoadWinsock", NULL )); DWORD error = ERROR_SUCCESS; // // ensure no 2 threads are trying to modify the loaded state of winsock at // the same time // EnterCriticalSection(&InitializationLock); if (hWinsock == NULL) { BOOL failed = FALSE; DPRINTF("Attempting to load winsock\n"); // // Even though we attempt to load WinSock 2 (ws2_32.dll) below, // we still load WinSock 1 (wsock32.dll) here because some of the // supposedly identical ordinal numbers were changed between // versions, thus breaking pure binary compatibility. // hWinsock = LoadLibrary("wsock32"); if (hWinsock != NULL) { // // Load function entry points from the WinSock library. // FARPROC farProc; for (int i = 0; i < ARRAY_ELEMENTS(SocketsFunctions); ++i) { farProc = GetProcAddress( hWinsock, (LPCSTR)(DWORD_PTR)SocketsFunctions[i].FunctionOrdinal ); if (farProc == NULL) { failed = TRUE; DPRINTF("Can't find entry %d in function array\n", i); break; } *SocketsFunctions[i].FunctionAddress = farProc; } if (!failed) { // // although we need a WSADATA for WSAStartup(), it is an // expendible structure (not required for any other sockets // calls) // WSADATA wsaData; error = _I_WSAStartup(0x0101, &wsaData); if (error == ERROR_SUCCESS) { DEBUG_PRINT(SOCKETS, INFO, ("winsock description: %q\n", wsaData.szDescription )); int stringLen; stringLen = lstrlen(wsaData.szDescription); if (strnistr(wsaData.szDescription, "novell", stringLen) && strnistr(wsaData.szDescription, "wsock32", stringLen)) { DEBUG_PRINT(SOCKETS, INFO, ("running on Novell Client32 stack\n" )); GlobalRunningNovellClient32 = TRUE; } #if INET_DEBUG SetupSocketsTracing(); #endif // // We may need to load ws2_32 for several purposes. // If the functionality is actually not available, // we do not want to keep around unnecessarily. // This refcount will help us decide. // INT ws2_32RefCount = 0; // // Attempt load ws2_32.dll. // DPRINTF("Checking for presence of ws2_32\n"); hWs2_32 = LoadLibrary ("ws2_32"); if (hWs2_32!=NULL) { // // Try to locate name resolution API // necessary to access Network Location Awareness // service (available on Whistler). // if (GlobalPlatformWhistler) { farProc = GetProcAddress (hWs2_32, "WSALookupServiceBeginW"); if (farProc!=NULL) { *(FARPROC *)&_I_WSALookupServiceBeginW = farProc; farProc = GetProcAddress (hWs2_32, "WSALookupServiceEnd"); if (farProc!=NULL) { *(FARPROC *)&_I_WSALookupServiceEnd = farProc; farProc = GetProcAddress (hWs2_32, "WSALookupServiceNextW"); if (farProc!=NULL) { *(FARPROC *)&_I_WSALookupServiceNextW = farProc; farProc = GetProcAddress (hWs2_32, "WSANSPIoctl"); if (farProc!=NULL) { *(FARPROC *)&_I_WSANSPIoctl = farProc; } } } } if (farProc!=NULL) { // // Increment ws2_32 ref count for // the purpose of NLA // ws2_32RefCount += 1; } else { _I_WSALookupServiceBeginW = NULL; _I_WSALookupServiceEnd = NULL; _I_WSALookupServiceNextW = NULL; _I_WSANSPIoctl = NULL; } } // // Try to locate the address family independent name // resolution routines (i.e. getaddrinfo, getnameinfo). // In Whistler and beyond, these will be present in // the WinSock 2 library (ws2_32.dll). // // Restricting to Whistler only. if (GlobalPlatformWhistler) { DPRINTF("Looking in ws2_32 for getaddrinfo\n"); farProc = GetProcAddress(hWs2_32, "getaddrinfo"); if (farProc != NULL) { // // Increment ws2_32 ref count for // the purpose of IPv6 address resolution. // ws2_32RefCount += 1; hAddrResLib = hWs2_32; } } } // Restricting to Whistler only. if ((hAddrResLib == NULL) && GlobalPlatformWhistler) { // // In the IPv6 Technology Preview, the address family // independent name resolution calls are snuck in via // the IPv6 WinSock helper library (wship6.dll). // So look there next. // DPRINTF("Looking in wship6 for getaddrinfo\n"); hAddrResLib = LoadLibrary("wship6"); if (hAddrResLib != NULL) { farProc = GetProcAddress(hAddrResLib, "getaddrinfo"); if (farProc == NULL) { FreeLibrary(hAddrResLib); hAddrResLib = NULL; } else { // // The Tech Preview version of the address // family independent APIs doesn't check that // an IPv6 stack is present before returning // IPv6 addresses. So we need to check for it. // SOCKET Test; struct sockaddr_in6 TestSA; DPRINTF("Checking for active IPv6 stack\n"); error = SOCKET_ERROR; Test = _I_socket(PF_INET6, 0, 0); if (Test != INVALID_SOCKET) { memset(&TestSA, 0, sizeof(TestSA)); TestSA.sin6_family = AF_INET6; TestSA.sin6_addr.s6_addr[15] = 1; error = _I_bind(Test, (LPSOCKADDR)&TestSA, sizeof(TestSA)); _I_closesocket(Test); } if (error != 0) { DPRINTF("IPv6 stack is not active\n"); FreeLibrary(hAddrResLib); hAddrResLib = NULL; error = 0; } } } } if (hAddrResLib != NULL) { // // Use routines from this library. Since getaddrinfo // is here, we expect the others to be also, but will // fall back to IPv4-only if any of them is missing. // *(FARPROC *)&_I_getaddrinfo = farProc; farProc = GetProcAddress(hAddrResLib, "freeaddrinfo"); if (farProc != NULL) { *(FARPROC *)&_I_freeaddrinfo = farProc; farProc = GetProcAddress(hAddrResLib, "getnameinfo"); if (farProc != NULL) *(FARPROC *)&_I_getnameinfo = farProc; } if (farProc == NULL) { if (hAddrResLib!=hWs2_32) { FreeLibrary(hAddrResLib); } else { ws2_32RefCount -= 1; } hAddrResLib = NULL; } } if (hAddrResLib == NULL) { // // If we can't find getaddrinfo lying around on the // system somewhere, assume we're still in the // IPv4-only dark ages. // DPRINTF("Using IPv4-only name res functions\n"); _I_getaddrinfo = LimitedGetAddrInfo; _I_freeaddrinfo = LimitedFreeAddrInfo; _I_getnameinfo = LimitedGetNameInfo; } // // Check if we actually used ws2_32 for anything // and unload it if not. // if (ws2_32RefCount==0 && hWs2_32!=NULL) { FreeLibrary (hWs2_32); hWs2_32 = NULL; } } else { DPRINTF("Winsock failed to start\n"); failed = TRUE; } } } else { DPRINTF("Couldn't load the winsock library\n"); failed = TRUE; } // // if we failed to find an entry point or WSAStartup() returned an error // then unload the library // if (failed) { // // important: there should be no API calls between determining the // failure and coming here to get the error code // // if error == ERROR_SUCCESS then we have to get the last error, else // it is the error returned by WSAStartup() // if (error == ERROR_SUCCESS) { error = GetLastError(); INET_ASSERT(error != ERROR_SUCCESS); } DPRINTF("Unloading winsock due to error 0x%x\n", error); UnloadWinsock(); } } else { // // just increment the number of times we have called LoadWinsock() // without a corresponding call to UnloadWinsock(); // ++WinsockLoadCount; } LeaveCriticalSection(&InitializationLock); // // if we failed for any reason, need to report that TCP/IP not available // if (error != ERROR_SUCCESS) { error = ERROR_INTERNET_TCPIP_NOT_INSTALLED; } DEBUG_LEAVE(error); return error; } VOID UnloadWinsock( VOID ) /*++ Routine Description: Unloads winsock DLL and prepares hWinsock and SocketsFunctions[] for reload Arguments: None. Return Value: None. --*/ { DEBUG_ENTER((DBG_SOCKETS, None, "UnloadWinsock", NULL )); // // ensure no 2 threads are trying to modify the loaded state of winsock at // the same time // EnterCriticalSection(&InitializationLock); // // only unload the DLL if it has been mapped into process memory // if (hWinsock != NULL) { // // and only if this is the last load instance // if (WinsockLoadCount == 0) { INET_ASSERT(_I_WSACleanup != NULL); if (_I_WSACleanup != NULL) { // // need to terminate async support too - it is reliant on // Winsock // TerminateAsyncSupport(); int serr = _I_WSACleanup(); if (serr != 0) { DEBUG_PRINT(SOCKETS, ERROR, ("WSACleanup() returns %d; WSA error = %d\n", serr, (_I_WSAGetLastError != NULL) ? _I_WSAGetLastError() : -1 )); } } if (hAddrResLib != NULL) { *(FARPROC *)&_I_getaddrinfo = (FARPROC)NULL; *(FARPROC *)&_I_freeaddrinfo = (FARPROC)NULL; *(FARPROC *)&_I_getnameinfo = (FARPROC)NULL; if (hAddrResLib!=hWs2_32) { FreeLibrary(hAddrResLib); } hAddrResLib = NULL; } if (hWs2_32!=NULL) { *(FARPROC *)&_I_WSALookupServiceBeginW = (FARPROC)NULL; *(FARPROC *)&_I_WSALookupServiceEnd = (FARPROC)NULL; *(FARPROC *)&_I_WSALookupServiceNextW = (FARPROC)NULL; *(FARPROC *)&_I_WSANSPIoctl = (FARPROC)NULL; FreeLibrary (hWs2_32); hWs2_32 = NULL; } for (int i = 0; i < ARRAY_ELEMENTS(SocketsFunctions); ++i) { *SocketsFunctions[i].FunctionAddress = (FARPROC)NULL; } FreeLibrary(hWinsock); hWinsock = NULL; } else { // // if there have been multiple virtual loads, then just reduce the // load count // --WinsockLoadCount; } } LeaveCriticalSection(&InitializationLock); DEBUG_LEAVE(0); } // // Following is v4-only version of getaddrinfo and friends. // // Note that we use LocalAlloc/LocalFree instead of malloc/free // to avoid introducing a dependency on msvcrt.dll. // //* LimitedFreeAddrInfo - Free an addrinfo structure (or chain of structures). // // As specified in RFC 2553, Section 6.4. // void WSAAPI LimitedFreeAddrInfo( struct addrinfo *Free) // Structure (chain) to free. { struct addrinfo *Next; for (Next = Free; Next != NULL; Free = Next) { if (Free->ai_canonname != NULL) LocalFree(Free->ai_canonname); if (Free->ai_addr != NULL) LocalFree(Free->ai_addr); Next = Free->ai_next; LocalFree(Free); } } //* NewAddrInfo - Allocate an addrinfo structure and populate some fields. // // Internal function, not exported. Expects to be called with valid // arguments, does no checking. // // Returns a partially filled-in addrinfo struct, or NULL if out of memory. // static struct addrinfo * NewAddrInfo( int SocketType, // SOCK_*. Can be wildcarded (zero). int Protocol, // IPPROTO_*. Can be wildcarded (zero). struct addrinfo ***Prev) // In/out param for accessing previous ai_next. { struct addrinfo *New; // // Allocate a new addrinfo structure. // New = (struct addrinfo *)LocalAlloc(0, sizeof(struct addrinfo)); if (New == NULL) return NULL; // // Fill in the easy stuff. // New->ai_flags = 0; New->ai_family = PF_INET; New->ai_socktype = SocketType; New->ai_protocol = Protocol; New->ai_addrlen = sizeof(struct sockaddr_in); New->ai_canonname = NULL; New->ai_addr = (LPSOCKADDR)LocalAlloc(0, New->ai_addrlen); if (New->ai_addr == NULL) { LocalFree(New); return NULL; } New->ai_next = NULL; // // Link this one onto the end of the chain. // **Prev = New; *Prev = &New->ai_next; return New; } //* LookupNode - Resolve a nodename and add any addresses found to the list. // // Internal function, not exported. Expects to be called with valid // arguments, does no checking. // // Returns 0 on success, an EAI_* style error value otherwise. // static int LookupNode( const char *NodeName, // Name of node to resolve. int SocketType, // SOCK_*. Can be wildcarded (zero). int Protocol, // IPPROTO_*. Can be wildcarded (zero). int Flags, // Flags. struct addrinfo ***Prev) // In/out param for accessing previous ai_next. { struct addrinfo *CurrentInfo; struct sockaddr_in *sin; struct hostent *hA; char **addrs; int Error = 0; hA = _I_gethostbyname(NodeName); if (hA != NULL) { if ((hA->h_addrtype == AF_INET) && (hA->h_length == sizeof(struct in_addr))) { // // Loop through all the addresses returned by gethostbyname, // allocating an addrinfo structure and filling in the address // field for each. // for (addrs = hA->h_addr_list; *addrs != NULL; addrs++) { CurrentInfo = NewAddrInfo(SocketType, Protocol, Prev); if (CurrentInfo == NULL) { Error = EAI_MEMORY; break; } // // We fill in the ai_canonname field in the first addrinfo // structure that we return if we've been asked to do so. // if (Flags & AI_CANONNAME) { if (hA->h_name != NULL) { int NameLength; NameLength = strlen(hA->h_name) + 1; CurrentInfo->ai_canonname = (char *)LocalAlloc(0, NameLength); if (CurrentInfo->ai_canonname == NULL) { Error = EAI_MEMORY; break; } memcpy(CurrentInfo->ai_canonname, hA->h_name, NameLength); } // Turn off flag so we only do this once. Flags &= ~AI_CANONNAME; } // // We're returning IPv4 addresses. // sin = (struct sockaddr_in *)CurrentInfo->ai_addr; sin->sin_family = AF_INET; sin->sin_port = 0; memcpy(&sin->sin_addr, (struct in_addr *)*addrs, sizeof sin->sin_addr); memset(sin->sin_zero, 0, sizeof sin->sin_zero); } } } else { Error = _I_WSAGetLastError(); if (Error == WSANO_DATA) { Error = EAI_NODATA; } else if (Error == WSAHOST_NOT_FOUND) { Error = EAI_NONAME; } else { Error = EAI_FAIL; } } return Error; } //* ParseV4Address // // Helper function for parsing a literal v4 address, because // WSAStringToAddress is too liberal in what it accepts. // Returns FALSE if there is an error, TRUE for success. // // The syntax is a.b.c.d, where each number is between 0 - 255. // static int ParseV4Address(const char *String, struct in_addr *Addr) { u_int Number; int NumChars; char Char; int i; for (i = 0; i < 4; i++) { Number = 0; NumChars = 0; for (;;) { Char = *String++; if (Char == '\0') { if ((NumChars > 0) && (i == 3)) break; else return FALSE; } else if (Char == '.') { if ((NumChars > 0) && (i < 3)) break; else return FALSE; } else if (('0' <= Char) && (Char <= '9')) { if ((NumChars != 0) && (Number == 0)) return FALSE; else if (++NumChars <= 3) Number = 10 * Number + (Char - '0'); else return FALSE; } else return FALSE; } if (Number > 255) return FALSE; ((u_char *)Addr)[i] = (u_char)Number; } return TRUE; } //* LimitedGetAddrInfo - Protocol-independent name-to-address translation. // // As specified in RFC 2553, Section 6.4. // // This is the hacked version that only supports IPv4. // // Returns zero if successful, an EAI_* error code if not. // int WSAAPI LimitedGetAddrInfo( const char *NodeName, // Node name to lookup. const char *ServiceName, // Service name to lookup. const struct addrinfo *Hints, // Hints about how to process request. struct addrinfo **Result) // Where to return result. { struct addrinfo *CurrentInfo, **Next; int ProtocolId = 0; u_short ProtocolFamily = PF_UNSPEC; int SocketType = 0; int Flags = 0; int Error; struct sockaddr_in *sin; struct in_addr TempAddr; // // This special cut-down version for wininet doesn't do service lookup. // So the request must be for nodename lookup. // INET_ASSERT(ServiceName == NULL); INET_ASSERT(NodeName != NULL); // // In case we have to bail early, make it clear to our caller // that we haven't allocated an addrinfo structure. // *Result = NULL; Next = Result; // // Validate hints argument. // if (Hints != NULL) { // // Wininet can be trusted to call us correctly. // INET_ASSERT((Hints->ai_addrlen == 0) && (Hints->ai_canonname == NULL) && (Hints->ai_addr == NULL) && (Hints->ai_next == NULL)); Flags = Hints->ai_flags; INET_ASSERT(!((Flags & AI_CANONNAME) && (Flags & AI_NUMERICHOST))); ProtocolFamily = (u_short)Hints->ai_family; INET_ASSERT((ProtocolFamily == PF_UNSPEC) || (ProtocolFamily == PF_INET)); SocketType = Hints->ai_socktype; INET_ASSERT((SocketType == 0) || (SocketType == SOCK_STREAM) || (SocketType == SOCK_DGRAM)); ProtocolId = Hints->ai_protocol; } // // We have a node name (either alpha or numeric) we need to look up. // // // First, see if this is a numeric string address that we can // just convert to a binary address. // if (ParseV4Address(NodeName, &TempAddr)) { // // Conversion from IPv4 numeric string to binary address // was sucessfull. Create an addrinfo structure to hold it, // and return it to the user. // CurrentInfo = NewAddrInfo(SocketType, ProtocolId, &Next); if (CurrentInfo == NULL) { Error = EAI_MEMORY; goto Bail; } sin = (struct sockaddr_in *)CurrentInfo->ai_addr; sin->sin_family = AF_INET; sin->sin_port = 0; sin->sin_addr = TempAddr; memset(sin->sin_zero, 0, sizeof sin->sin_zero); return 0; // Success! } // // It's not a numeric string address. If our caller only wants us // to convert numeric addresses, give up now. // if (Flags & AI_NUMERICHOST) { Error = EAI_NONAME; goto Bail; } // // Since it's non-numeric, we have to do a regular node name lookup. // Error = LookupNode(NodeName, SocketType, ProtocolId, Flags, &Next); if (Error != 0) goto Bail; return 0; // Success! Bail: if (*Result != NULL) { LimitedFreeAddrInfo(*Result); *Result = NULL; } return Error; } //* LimitedGetNameInfo - Protocol-independent address-to-name translation. // // As specified in RFC 2553, Section 6.5. // // This is a special version for wininet that only supports IPv4. // All extraneous checks have been removed, only the specific calls // that wininet makes are supported. // // Note that unless the IE team decides to attempt the FTP EPRT command // for IPv4 as well as IPv6 (see comments in ftp\protocol.cxx), this // routine will never be called. // int WSAAPI LimitedGetNameInfo( const struct sockaddr *SocketAddress, // Socket address to translate. socklen_t SocketAddressLength, // Length of above socket address. char *NodeName, // Where to return the node name. size_t NodeBufferSize, // Size of above buffer. char *ServiceName, // Where to return the service name. size_t ServiceBufferSize, // Size of above buffer. int Flags) // Flags of type NI_*. { // // Wininet doesn't do service lookup. // INET_ASSERT((ServiceName == NULL) && (ServiceBufferSize == 0)); // // Wininet can be trusted to call us correctly. // INET_ASSERT((NodeName != NULL) && (SocketAddress != NULL) && (SocketAddressLength == sizeof(struct sockaddr_in))); // // This version is IPv4 only. // INET_ASSERT(SocketAddress->sa_family == AF_INET); // // Wininet will only call this routine to translate the given address // to an IPv4 address literal. // INET_ASSERT(Flags & NI_NUMERICHOST); INET_ASSERT(NodeBufferSize >= sizeof("255.255.255.255")); strcpy(NodeName, _I_inet_ntoa(((struct sockaddr_in *)SocketAddress)->sin_addr)); return 0; } DWORD SafeCloseSocket( IN SOCKET Socket ) /*++ Routine Description: closesocket() call protected by exception handler in case winsock DLL has been unloaded by system before Wininet DLL unloaded Arguments: Socket - socket handle to close Return Value: DWORD Success - ERROR_SUCCESS Failure - socket error mapped to ERROR_INTERNET_ error --*/ { int serr; __try { serr = _I_closesocket(Socket); } __except(EXCEPTION_EXECUTE_HANDLER) { serr = 0; } ENDEXCEPT return (serr == SOCKET_ERROR) ? MapInternetError(_I_WSAGetLastError()) : ERROR_SUCCESS; } #if INET_DEBUG // // debug data types // SOCKET PASCAL FAR _II_socket( int af, int type, int protocol ); int PASCAL FAR _II_closesocket( SOCKET s ); SOCKET PASCAL FAR _II_accept( SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen ); GLOBAL SOCKET (PASCAL FAR * _P_accept)( SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen ) = NULL; GLOBAL int (PASCAL FAR * _P_closesocket)( SOCKET s ) = NULL; GLOBAL SOCKET (PASCAL FAR * _P_socket)( int af, int type, int protocol ) = NULL; #define MAX_STACK_TRACE 5 #define MAX_SOCK_ENTRIES 1000 typedef struct _DEBUG_SOCK_ENTRY { SOCKET Socket; DWORD StackTraceLength; PVOID StackTrace[ MAX_STACK_TRACE ]; } DEBUG_SOCK_ENTRY, *LPDEBUG_SOCK_ENTRY; CRITICAL_SECTION DebugSockLock; DEBUG_SOCK_ENTRY GlobalSockEntry[MAX_SOCK_ENTRIES]; DWORD GlobalSocketsCount = 0; #define LOCK_DEBUG_SOCK() EnterCriticalSection( &DebugSockLock ) #define UNLOCK_DEBUG_SOCK() LeaveCriticalSection( &DebugSockLock ) HINSTANCE NtDllHandle; typedef USHORT (*RTL_CAPTURE_STACK_BACK_TRACE)( IN ULONG FramesToSkip, IN ULONG FramesToCapture, OUT PVOID *BackTrace, OUT PULONG BackTraceHash ); RTL_CAPTURE_STACK_BACK_TRACE pRtlCaptureStackBackTrace; VOID InitDebugSock( VOID ) { InitializeCriticalSection( &DebugSockLock ); memset( GlobalSockEntry, 0x0, sizeof(GlobalSockEntry) ); GlobalSocketsCount = 0; return; } VOID TerminateDebugSock( VOID ) { DeleteCriticalSection(&DebugSockLock); } VOID SetupSocketsTracing( VOID ) { if (!(InternetDebugCategoryFlags & DBG_TRACE_SOCKETS)) { return ; } if (!IsPlatformWinNT()) { return ; } if ((NtDllHandle = LoadLibrary("ntdll.dll")) == NULL) { return ; } if ((pRtlCaptureStackBackTrace = (RTL_CAPTURE_STACK_BACK_TRACE) GetProcAddress(NtDllHandle, "RtlCaptureStackBackTrace")) == NULL) { FreeLibrary(NtDllHandle); return ; } //#ifdef DONT_DO_FOR_NOW _P_accept = _I_accept; _I_accept = _II_accept; _P_closesocket = _I_closesocket; _I_closesocket = _II_closesocket; _P_socket = _I_socket; _I_socket = _II_socket; //#endif } VOID AddSockEntry( SOCKET S ) { DWORD i; DWORD Hash; if (!(InternetDebugCategoryFlags & DBG_TRACE_SOCKETS)) { return ; } LOCK_DEBUG_SOCK(); // // search for a free entry. // for( i = 0; i < MAX_SOCK_ENTRIES; i++ ) { if( GlobalSockEntry[i].Socket == 0 ) { DWORD Hash; // // found a free entry. // GlobalSockEntry[i].Socket = S; // // get caller stack. // #if i386 Hash = 0; GlobalSockEntry[i].StackTraceLength = pRtlCaptureStackBackTrace( 2, MAX_STACK_TRACE, GlobalSockEntry[i].StackTrace, &Hash ); #else // i386 GlobalSockEntry[i].StackTraceLength = 0; #endif // i386 GlobalSocketsCount++; DEBUG_PRINT(SOCKETS, INFO, ("socket count = %ld\n", GlobalSocketsCount )); DPRINTF("%d sockets\n", GlobalSocketsCount); UNLOCK_DEBUG_SOCK(); return; } } // // we have reached a high handle limit, which is unusal, needs to be // debugged. // INET_ASSERT( FALSE ); UNLOCK_DEBUG_SOCK(); return; } VOID RemoveSockEntry( SOCKET S ) { DWORD i; if (!(InternetDebugCategoryFlags & DBG_TRACE_SOCKETS)) { return ; } LOCK_DEBUG_SOCK(); for( i = 0; i < MAX_SOCK_ENTRIES; i++ ) { if( GlobalSockEntry[i].Socket == S ) { // // found the entry. Free it now. // memset( &GlobalSockEntry[i], 0x0, sizeof(DEBUG_SOCK_ENTRY) ); GlobalSocketsCount--; #ifdef IWINSOCK_DEBUG_PRINT DEBUG_PRINT(SOCKETS, INFO, ("count(%ld), RemoveSock(%lx)\n", GlobalSocketsCount, S )); #endif // IWINSOCK_DEBUG_PRINT DPRINTF("%d sockets\n", GlobalSocketsCount); UNLOCK_DEBUG_SOCK(); return; } } #ifdef IWINSOCK_DEBUG_PRINT DEBUG_PRINT(SOCKETS, INFO, ("count(%ld), UnknownSock(%lx)\n", GlobalSocketsCount, S )); #endif // IWINSOCK_DEBUG_PRINT // // socket entry is not found. // // INET_ASSERT( FALSE ); UNLOCK_DEBUG_SOCK(); return; } SOCKET PASCAL FAR _II_socket( int af, int type, int protocol ) { SOCKET S; S = _P_socket( af, type, protocol ); AddSockEntry( S ); return( S ); } int PASCAL FAR _II_closesocket( SOCKET s ) { int Ret; RemoveSockEntry( s ); Ret = _P_closesocket( s ); return( Ret ); } SOCKET PASCAL FAR _II_accept( SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen ) { SOCKET S; S = _P_accept( s, addr, addrlen ); AddSockEntry( S ); return( S ); } VOID IWinsockCheckSockets( VOID ) { DEBUG_PRINT(SOCKETS, INFO, ("GlobalSocketsCount = %d\n", GlobalSocketsCount )); for (DWORD i = 0; i < MAX_SOCK_ENTRIES; ++i) { SOCKET sock; if ((sock = GlobalSockEntry[i].Socket) != 0) { DEBUG_PRINT(SOCKETS, INFO, ("Socket %#x\n", sock )); } } } #endif // INET_DEBUG #if defined(__cplusplus) } #endif