/*++ Copyright (c) 1997 Microsoft Corporation Module Name: caddrlst.cxx Abstract: Contains CAddressList class definition Contents: CAddressList::FreeList CAddressList::SetList CAddressList::SetList CAddressList::GetNextAddress CAddressList::InvalidateAddress CAddressList::ResolveHost CFsm_ResolveHost::RunSM (CAddressList::IPAddressToAddressList) (CAddressList::HostentToAddressList) Author: Richard L Firth (rfirth) 19-Apr-1997 Environment: Win32 user-mode DLL Revision History: 19-Apr-1997 rfirth Created 28-Jan-1998 rfirth No longer randomly index address list. NT5 and Win98 are modified to return the address list in decreasing order of desirability by RTT/ route --*/ #include #include //#define TEST_CODE //Thread-procedure for async gethostbyname DWORD WINAPI AsyncGetHostByName(LPVOID lpParameter); //The destructor is called only when all refcounts have dropped to 0. // i.e. when the INTERNET_HANDLE_OBJECT has released its reference, // AND when all the GHBN threads are done. // At this point, we can flush the hostent cache and terminate list. CResolverCache::~CResolverCache() { FlushHostentCache(&_ResolverCache); TerminateSerializedList(&_ResolverCache); if (_pHandlesList) delete _pHandlesList; } void CResolverCache::ForceEmptyAndDeleteHandlesList() { INET_ASSERT(_pHandlesList); _pHandlesList->LockList(); CListItem* pItem = _pHandlesList->GetHead(); while(pItem) { CListItem* pNext = pItem->GetNext(); (((CGetHostItem*)pItem)->ForceDelete()); delete pItem; _pHandlesList->ReduceCount(); pItem = pNext; } //it's not going to be reused after this, so head and tail don't have to be set to NULL // on _pHandlesList _pHandlesList->UnlockList(); } void CResolverCache::EmptyHandlesList() { if (_pHandlesList) { _pHandlesList->LockList(); CListItem* pItem = _pHandlesList->GetHead(); while(pItem) { CListItem* pNext = pItem->GetNext(); (((CGetHostItem*)pItem)->WaitDelete()); delete pItem; _pHandlesList->ReduceCount(); pItem = pNext; } //it's not going to be reused after this, so head and tail don't have to be set to NULL // on _pHandlesList _pHandlesList->UnlockList(); } } void CResolverCache::TrimHandlesListSize(ULONG nTrimSize) { _pHandlesList->LockList(); if (_pHandlesList->GetCount() >= nTrimSize) { CListItem* pItem = _pHandlesList->GetHead(); CListItem* pPrev = NULL; while(pItem) { CListItem* pNext = pItem->GetNext(); if (((CGetHostItem*)pItem)->CanBeDeleted()) { if (pPrev) { pPrev->SetNext(pNext); } else { //The item being removed WAS the head. _pHandlesList->SetHead(pNext); } if (!pNext) { //The item being removed WAS the tail. _pHandlesList->SetTail(pPrev); } delete pItem; _pHandlesList->ReduceCount(); } else { pPrev = pItem; } pItem = pNext; } } _pHandlesList->UnlockList(); } BOOL CResolverCache::AddToHandlesList(HANDLE hThread, CGetHostItem* pGetHostItem) { BOOL bRetval = TRUE; INET_ASSERT(_pHandlesList); pGetHostItem->SetThreadHandle(hThread); TrimHandlesListSize(); _pHandlesList->AddToTail(pGetHostItem); return bRetval; } DWORD WINAPI AsyncGetHostByName(LPVOID lpParameter) { #ifdef WINHTTP_FOR_MSXML // // MSXML needs to initialize is thread local storage data. // It does not do this during DLL_THREAD_ATTACH, so our // worker thread must explicitly call into MSXML to initialize // its TLS for this thread. // InitializeMsxmlTLS(); #endif CGetHostItem* pGetHostItem = (CGetHostItem*)lpParameter; LPSTR lpszHostName = pGetHostItem->GetHostName(); LPHOSTENT lpHostEnt; DWORD dwError = 0; if (!(lpHostEnt = _I_gethostbyname(lpszHostName))) { dwError = _I_WSAGetLastError(); } else { VOID* pAlloc = pGetHostItem->GetAllocPointer(); CacheHostent((pGetHostItem->GetResolverCache())->GetResolverCacheList(), lpszHostName, lpHostEnt, LIVE_DEFAULT, &pAlloc, pGetHostItem->GetAllocSize()); if (pAlloc) { //pAlloc is overwritten to NULL in CacheHostent if the memory is used, //we need to delete the alloced memory only if non-NULL pGetHostItem->SetDelete(); } } return dwError; } // // methods // VOID CAddressList::FreeList( VOID ) /*++ Routine Description: Free address list Arguments: None. Return Value: None. --*/ { if (m_Addresses != NULL) { m_Addresses = (LPRESOLVED_ADDRESS)FREE_MEMORY((HLOCAL)m_Addresses); INET_ASSERT(m_Addresses == NULL); m_AddressCount = 0; m_BadAddressCount = 0; m_CurrentAddress = 0; } } DWORD CAddressList::SetList( IN DWORD dwIpAddress ) /*++ Routine Description: Sets the list contents from the IP address Arguments: dwIpAddress - IP address from which to create list contents Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_NOT_ENOUGH_MEMORY --*/ { if (!Acquire()) { return ERROR_NOT_ENOUGH_MEMORY; } FreeList(); DWORD error = IPAddressToAddressList(dwIpAddress); Release(); return error; } DWORD CAddressList::SetList( IN LPHOSTENT lpHostent ) /*++ Routine Description: Sets the list contents from the hostent Arguments: lpHostent - pointer to hostent containing resolved addresses to add Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_NOT_ENOUGH_MEMORY --*/ { if (!Acquire()) { return ERROR_NOT_ENOUGH_MEMORY; } FreeList(); DWORD error = HostentToAddressList(lpHostent); Release(); return error; } BOOL CAddressList::GetNextAddress( OUT LPDWORD lpdwResolutionId, IN OUT LPDWORD lpdwIndex, IN INTERNET_PORT nPort, OUT LPCSADDR_INFO lpAddressInfo ) /*++ Routine Description: Get next address to use when connecting. If we already have a preferred address, use that. We make a copy of the address to use in the caller's data space Arguments: lpdwResolutionId - used to determine whether the address list has been resolved between calls lpdwIndex - IN: current index tried; -1 if we want to try default OUT: index of address address returned if successful nPort - which port we want to connect to lpAddressInfo - pointer to returned address if successful Return Value: BOOL TRUE - lpResolvedAddress contains resolved address to use FALSE - need to (re-)resolve name --*/ { DEBUG_ENTER((DBG_SOCKETS, Bool, "CAddressList::GetNextAddress", "%#x [%d], %#x [%d], %d, %#x", lpdwResolutionId, *lpdwResolutionId, lpdwIndex, *lpdwIndex, nPort, lpAddressInfo )); PERF_ENTER(GetNextAddress); BOOL bOk = TRUE; // // if we tried all the addresses and failed already, re-resolve the name // if (!Acquire()) { bOk = FALSE; goto quit; } if (m_BadAddressCount < m_AddressCount) { if (*lpdwIndex != (DWORD)-1) { INET_ASSERT(m_BadAddressCount < m_AddressCount); INT i = 0; m_CurrentAddress = *lpdwIndex; INET_ASSERT((m_CurrentAddress >= 0) && (m_CurrentAddress < m_AddressCount)); if ((m_CurrentAddress < 0) || (m_CurrentAddress >= m_AddressCount)) { m_CurrentAddress = 0; } do { NextAddress(); if (++i == m_AddressCount) { bOk = FALSE; break; } } while (!IsCurrentAddressValid()); } // // check to make sure this address hasn't expired // //if (!CheckHostentCacheTtl()) { // bOk = FALSE; //} } else { DEBUG_PRINT(SOCKETS, INFO, ("exhausted %d addresses\n", m_BadAddressCount )); bOk = FALSE; } if (bOk) { DWORD dwLocalLength = LocalSockaddrLength(); LPBYTE lpRemoteAddr = (LPBYTE)(lpAddressInfo + 1) + dwLocalLength; memcpy(lpAddressInfo + 1, LocalSockaddr(), dwLocalLength); memcpy(lpRemoteAddr, RemoteSockaddr(), RemoteSockaddrLength()); lpAddressInfo->LocalAddr.lpSockaddr = (LPSOCKADDR)(lpAddressInfo + 1); lpAddressInfo->LocalAddr.iSockaddrLength = dwLocalLength; lpAddressInfo->RemoteAddr.lpSockaddr = (LPSOCKADDR)lpRemoteAddr; lpAddressInfo->RemoteAddr.iSockaddrLength = RemoteSockaddrLength(); lpAddressInfo->iSocketType = SocketType(); lpAddressInfo->iProtocol = Protocol(); ((LPSOCKADDR_IN)lpAddressInfo->RemoteAddr.lpSockaddr)->sin_port = _I_htons((unsigned short)nPort); *lpdwIndex = m_CurrentAddress; DEBUG_PRINT(SOCKETS, INFO, ("current address = %d.%d.%d.%d\n", ((LPBYTE)RemoteSockaddr())[4] & 0xff, ((LPBYTE)RemoteSockaddr())[5] & 0xff, ((LPBYTE)RemoteSockaddr())[6] & 0xff, ((LPBYTE)RemoteSockaddr())[7] & 0xff )); //dprintf("returning address %d.%d.%d.%d, index %d:%d\n", // ((LPBYTE)RemoteSockaddr())[4] & 0xff, // ((LPBYTE)RemoteSockaddr())[5] & 0xff, // ((LPBYTE)RemoteSockaddr())[6] & 0xff, // ((LPBYTE)RemoteSockaddr())[7] & 0xff, // m_ResolutionId, // m_CurrentAddress // ); } *lpdwResolutionId = m_ResolutionId; DEBUG_PRINT(SOCKETS, INFO, ("ResolutionId = %d, Index = %d\n", m_ResolutionId, m_CurrentAddress )); Release(); quit: PERF_LEAVE(GetNextAddress); DEBUG_LEAVE(bOk); return bOk; } VOID CAddressList::InvalidateAddress( IN DWORD dwResolutionId, IN DWORD dwAddressIndex ) /*++ Routine Description: We failed to create a connection. Invalidate the address so other requests will try another address Arguments: dwResolutionId - used to ensure coherency of address list dwAddressIndex - which address to invalidate Return Value: None. --*/ { DEBUG_ENTER((DBG_SOCKETS, None, "CAddressList::InvalidateAddress", "%d, %d", dwResolutionId, dwAddressIndex )); //dprintf("invalidating %d.%d.%d.%d, index %d:%d\n", // ((LPBYTE)RemoteSockaddr())[4] & 0xff, // ((LPBYTE)RemoteSockaddr())[5] & 0xff, // ((LPBYTE)RemoteSockaddr())[6] & 0xff, // ((LPBYTE)RemoteSockaddr())[7] & 0xff, // dwResolutionId, // dwAddressIndex // ); if (!Acquire()) goto quit; // just take the hit of trying again, if we can. // // only do this if the list is the same age as when the caller last tried // an address // if (dwResolutionId == m_ResolutionId) { INET_ASSERT(((INT)dwAddressIndex >= 0) && ((INT)dwAddressIndex < m_AddressCount)); if (dwAddressIndex < (DWORD)m_AddressCount) { m_Addresses[dwAddressIndex].IsValid = FALSE; DEBUG_PRINT(SOCKETS, INFO, ("invalidated address %d.%d.%d.%d\n", ((LPBYTE)RemoteSockaddr())[4] & 0xff, ((LPBYTE)RemoteSockaddr())[5] & 0xff, ((LPBYTE)RemoteSockaddr())[6] & 0xff, ((LPBYTE)RemoteSockaddr())[7] & 0xff )); INET_ASSERT(m_BadAddressCount <= m_AddressCount); if (m_BadAddressCount < m_AddressCount) { ++m_BadAddressCount; if (m_BadAddressCount < m_AddressCount) { for (int i = 0; !IsCurrentAddressValid() && (i < m_AddressCount); ++i) { NextAddress(); } } } } } Release(); quit: DEBUG_LEAVE(0); } DWORD CAddressList::ResolveHost( IN LPSTR lpszHostName, IN OUT LPDWORD lpdwResolutionId, IN DWORD dwFlags ) /*++ Routine Description: Resolves host name (or (IP-)address) BUGBUG: Ideally, we don't want to keep hold of worker threads if we are in the blocking gethostbyname() call. But correctly handling this is difficult, so we always block the thread while we are resolving. For this reason, an async request being run on an app thread should have switched to a worker thread before calling this function. Arguments: lpszHostName - host name (or IP-address) to resolve lpdwResolutionId - used to determine whether entry changed dwFlags - controlling request: SF_INDICATE - if set, make indications via callback SF_FORCE - if set, force (re-)resolve Return Value: DWORD Success - ERROR_SUCCESS Name successfully resolved Failure - ERROR_WINHTTP_NAME_NOT_RESOLVED Couldn't resolve the name ERROR_NOT_ENOUGH_MEMORY Couldn't allocate memory for the FSM --*/ { DEBUG_ENTER((DBG_SOCKETS, Dword, "CAddressList::ResolveHost", "%q, %d, %#x", lpszHostName, *lpdwResolutionId, dwFlags )); DWORD error; error = DoFsm(New CFsm_ResolveHost(lpszHostName, lpdwResolutionId, dwFlags, this )); //quit: DEBUG_LEAVE(error); return error; } DWORD CFsm_ResolveHost::RunSM( IN CFsm * Fsm ) { DEBUG_ENTER((DBG_SESSION, Dword, "CFsm_ResolveHost::RunSM", "%#x", Fsm )); CAddressList * pAddressList = (CAddressList *)Fsm->GetContext(); CFsm_ResolveHost * stateMachine = (CFsm_ResolveHost *)Fsm; DWORD error; switch (Fsm->GetState()) { case FSM_STATE_INIT: case FSM_STATE_CONTINUE: error = pAddressList->ResolveHost_Fsm(stateMachine); break; case FSM_STATE_ERROR: error = Fsm->GetError(); Fsm->SetDone(); break; default: error = ERROR_WINHTTP_INTERNAL_ERROR; Fsm->SetDone(ERROR_WINHTTP_INTERNAL_ERROR); INET_ASSERT(FALSE); break; } DEBUG_LEAVE(error); return error; } DWORD CAddressList::ResolveHost_Fsm( IN CFsm_ResolveHost * Fsm ) { DEBUG_ENTER((DBG_SOCKETS, Dword, "CAddressList::ResolveHost_Fsm", "%#x(%q, %#x [%d], %#x)", Fsm, Fsm->m_lpszHostName, Fsm->m_lpdwResolutionId, *Fsm->m_lpdwResolutionId, Fsm->m_dwFlags )); PERF_ENTER(ResolveHost); // // restore variables from FSM object // CFsm_ResolveHost & fsm = *Fsm; LPSTR lpszHostName = fsm.m_lpszHostName; LPDWORD lpdwResolutionId = fsm.m_lpdwResolutionId; DWORD dwFlags = fsm.m_dwFlags; LPINTERNET_THREAD_INFO lpThreadInfo = fsm.GetThreadInfo(); INTERNET_HANDLE_BASE * pHandle = fsm.GetMappedHandleObject(); DWORD error = ERROR_SUCCESS; CResolverCache* pResolverCache = GetRootHandle(pHandle)->GetResolverCache(); DWORD dwWaitTime; LPHOSTENT lpHostent = NULL; DWORD ttl; // // BUGBUG - RLF 04/23/97 // // This is sub-optimal. We want to block worker FSMs and free up the worker // thread. Sync client threads can wait. However, since a clash is not very // likely, we'll block all threads for now and come up with a better // solution later (XTLock). // // Don't have time to implement the proper solution now // if (!Acquire()) { error = ERROR_NOT_ENOUGH_MEMORY; goto exit; } // // if the resolution id is different then the name has already been resolved // if (*lpdwResolutionId != m_ResolutionId) { goto done; } // // if we're an app thread making an async request then go async now rather // than risk blocking the app thread. This will be the typical scenario for // IE, and we care about little else // // BUGBUG - RLF 05/20/97 // // We should really lock & test the cache first, but let's do that after // Beta2 (its perf work) // // It cannot happen that this condition be true. // WinHttpSendRequest would have queued an async fsm if it was async to begin with. INET_ASSERT(lpThreadInfo->IsAsyncWorkerThread || !pHandle->IsAsyncHandle()); /* if (!lpThreadInfo->IsAsyncWorkerThread && pHandle->IsAsyncHandle() && (fsm.GetAppContext() != NULL)) { DEBUG_PRINT(SOCKETS, INFO, ("async request on app thread - jumping to hyper-drive\n" )); error = Fsm->QueueWorkItem(); goto done; } */ // // throw out current list (if any) // FreeList(); // // let the app know we are resolving the name // if (dwFlags & SF_INDICATE) { error = InternetIndicateStatusString(WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, lpszHostName, TRUE/*bCopyBuffer*/ ); //bail out if aborted before network operation. if (error != ERROR_SUCCESS) { INET_ASSERT(error == ERROR_WINHTTP_OPERATION_CANCELLED); goto done; } } // // figure out if we're being asked to resolve a name or an address. If // inet_addr() succeeds then we were given a string representation of an // address // DWORD ipAddr; //dprintf("resolving %q\n", lpszHostName); ipAddr = _I_inet_addr(lpszHostName); if (ipAddr != INADDR_NONE) { // // IP address was passed in. Simply convert to address list and quit // error = SetList(ipAddr); goto quit; } // // 255.255.255.255 (or 65535.65535 or 16777215.255) would never work anyway // INET_ASSERT(lstrcmp(lpszHostName, "255.255.255.255")); // // now try to find the name or address in the cache. If it's not in the // cache then resolve it // if (!(dwFlags & SF_FORCE) && QueryHostentCache(pResolverCache->GetResolverCacheList(), lpszHostName, NULL, &lpHostent, &ttl)) { error = SetList(lpHostent); ReleaseHostentCacheEntry(pResolverCache->GetResolverCacheList(), lpHostent); ++m_ResolutionId; goto quit; } // // if we call winsock gethostbyname() then we don't get to find out the // time-to-live as returned by DNS, so we have to use the default value // (LIVE_DEFAULT) // dwWaitTime = GetTimeoutValue(WINHTTP_OPTION_RESOLVE_TIMEOUT); // if a resolve timeout is specified by the application, then honor it. // If anything fails in the async pathway, DON'T default to sync GHBN. if (dwWaitTime != INFINITE) { DWORD dwThreadId; LPSTR lpszCopyHostName = NewString(lpszHostName); if (lpszCopyHostName) { #define SZ_AVG_RESOLVER_ENTRY_BYTES 512 VOID* pAlloc = ALLOCATE_MEMORY(LMEM_FIXED, SZ_AVG_RESOLVER_ENTRY_BYTES); CGetHostItem* pGetHostItem = New CGetHostItem(lpszCopyHostName, pResolverCache, pAlloc, pAlloc?SZ_AVG_RESOLVER_ENTRY_BYTES:0); if (!pGetHostItem) { FREE_FIXED_MEMORY(lpszCopyHostName); goto failed; } HANDLE hThread = 0; WRAP_REVERT_USER(CreateThread, (NULL, 0, &AsyncGetHostByName, pGetHostItem, 0, &dwThreadId), hThread); // HANDLE hThread = CreateThread(NULL, 0, &AsyncGetHostByName, // pGetHostItem, 0, &dwThreadId); if (!hThread) { delete pGetHostItem; goto failed; } DWORD dwWaitResponse = WaitForSingleObject(hThread, dwWaitTime); if (dwWaitResponse == WAIT_OBJECT_0) { DWORD dwError; BOOL fRet = GetExitCodeThread(hThread, &dwError); //want to use this error? INET_ASSERT(dwError != STILL_ACTIVE); if (fRet && !dwError && QueryHostentCache(pResolverCache->GetResolverCacheList(), lpszCopyHostName, NULL, &lpHostent, &ttl)) { error = SetList(lpHostent); ReleaseHostentCacheEntry(pResolverCache->GetResolverCacheList(), lpHostent); ++m_ResolutionId; } CloseHandle(hThread); delete pGetHostItem; DEBUG_PRINT(SOCKETS, INFO, ("%q %sresolved\n", lpszHostName, lpHostent ? "" : "NOT " )); } else //(dwWaitResponse == WAIT_TIMEOUT) { //let thread die and if it successfully resolved host, it can add to cache. pResolverCache->AddToHandlesList(hThread, pGetHostItem); } } //lpszCopyHostName }// dwWaitTime (specified on this handle) else { //synchronous get host by name lpHostent = _I_gethostbyname(lpszHostName); DEBUG_PRINT(SOCKETS, INFO, ("%q %sresolved\n", lpszHostName, lpHostent ? "" : "NOT " )); if (lpHostent != NULL) { CacheHostent(pResolverCache->GetResolverCacheList(), lpszHostName, lpHostent, LIVE_DEFAULT); error = SetList(lpHostent); ++m_ResolutionId; } } failed: if (!lpHostent) { error = ERROR_WINHTTP_NAME_NOT_RESOLVED; } quit: if ((error == ERROR_SUCCESS) && (dwFlags & SF_INDICATE)) { // // inform the app that we have resolved the name // InternetIndicateStatusAddress(WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, RemoteSockaddr(), RemoteSockaddrLength() ); } *lpdwResolutionId = m_ResolutionId; done: Release(); exit: if (error != ERROR_IO_PENDING) { fsm.SetDone(); //PERF_LEAVE(ResolveHost); } PERF_LEAVE(ResolveHost); DEBUG_LEAVE(error); return error; } // // private methods // PRIVATE DWORD CAddressList::IPAddressToAddressList( IN DWORD ipAddr ) /*++ Routine Description: Converts an IP-address to a RESOLVED_ADDRESS Arguments: ipAddr - IP address to convert Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_NOT_ENOUGH_MEMORY --*/ { LPRESOLVED_ADDRESS address = (LPRESOLVED_ADDRESS)ALLOCATE_MEMORY( LMEM_FIXED, sizeof(RESOLVED_ADDRESS) // // 1 local and 1 remote // socket address // + 2 * sizeof(SOCKADDR) ); if (address == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } LPBYTE lpVariable; LPSOCKADDR_IN lpSin; lpVariable = (LPBYTE)address + (sizeof(RESOLVED_ADDRESS)); // // for each IP address in the hostent, build a CSADDR_INFO structure: // create a local SOCKADDR containing only the address family (AF_INET), // everything else is zeroed; create a remote SOCKADDR containing the // address family (AF_INET), zero port value and the IP address // presented in the arguments // address->AddrInfo.LocalAddr.lpSockaddr = (LPSOCKADDR)lpVariable; address->AddrInfo.LocalAddr.iSockaddrLength = sizeof(SOCKADDR); lpSin = (LPSOCKADDR_IN)lpVariable; lpVariable += sizeof(*lpSin); lpSin->sin_family = AF_INET; lpSin->sin_port = 0; *(LPDWORD)&lpSin->sin_addr = INADDR_ANY; memset(lpSin->sin_zero, 0, sizeof(lpSin->sin_zero)); address->AddrInfo.RemoteAddr.lpSockaddr = (LPSOCKADDR)lpVariable; address->AddrInfo.RemoteAddr.iSockaddrLength = sizeof(SOCKADDR); lpSin = (LPSOCKADDR_IN)lpVariable; lpVariable += sizeof(*lpSin); lpSin->sin_family = AF_INET; lpSin->sin_port = 0; *(LPDWORD)&lpSin->sin_addr = ipAddr; memset(lpSin->sin_zero, 0, sizeof(lpSin->sin_zero)); address->AddrInfo.iSocketType = SOCK_STREAM; address->AddrInfo.iProtocol = IPPROTO_TCP; address->IsValid = TRUE; // // update the object // INET_ASSERT(m_AddressCount == 0); INET_ASSERT(m_Addresses == NULL); m_AddressCount = 1; m_BadAddressCount = 0; m_Addresses = address; m_CurrentAddress = 0; // only one to choose from return ERROR_SUCCESS; } PRIVATE DWORD CAddressList::HostentToAddressList( IN LPHOSTENT lpHostent ) /*++ Routine Description: Converts a HOSTENT structure to an array of RESOLVED_ADDRESSs Arguments: lpHostent - pointer to HOSTENT to convert Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_NOT_ENOUGH_MEMORY --*/ { INET_ASSERT(lpHostent != NULL); LPBYTE * addressList = (LPBYTE *)lpHostent->h_addr_list; INET_ASSERT(addressList[0]); // // first off, figure out how many addresses there are in the hostent // int nAddrs; if (fDontUseDNSLoadBalancing) { nAddrs = 1; } else { for (nAddrs = 0; addressList[nAddrs] != NULL; ++nAddrs) { /* NOTHING */ } #ifdef TEST_CODE nAddrs = 4; #endif } LPRESOLVED_ADDRESS addresses = (LPRESOLVED_ADDRESS)ALLOCATE_MEMORY( LMEM_FIXED, nAddrs * (sizeof(RESOLVED_ADDRESS) // // need 1 local and 1 remote socket // address for each // + 2 * sizeof(SOCKADDR)) ); if (addresses == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } // // for each IP address in the hostent, build a RESOLVED_ADDRESS structure: // create a local SOCKADDR containing only the address family (AF_INET), // everything else is zeroed; create a remote SOCKADDR containing the // address family (AF_INET), zero port value, and the IP address from // the hostent presented in the arguments // LPBYTE lpVariable = (LPBYTE)addresses + (nAddrs * sizeof(RESOLVED_ADDRESS)); LPSOCKADDR_IN lpSin; for (int i = 0; i < nAddrs; ++i) { addresses[i].AddrInfo.LocalAddr.lpSockaddr = (LPSOCKADDR)lpVariable; addresses[i].AddrInfo.LocalAddr.iSockaddrLength = sizeof(SOCKADDR); lpSin = (LPSOCKADDR_IN)lpVariable; lpVariable += sizeof(*lpSin); lpSin->sin_family = AF_INET; lpSin->sin_port = 0; *(LPDWORD)&lpSin->sin_addr = INADDR_ANY; memset(lpSin->sin_zero, 0, sizeof(lpSin->sin_zero)); addresses[i].AddrInfo.RemoteAddr.lpSockaddr = (LPSOCKADDR)lpVariable; addresses[i].AddrInfo.RemoteAddr.iSockaddrLength = sizeof(SOCKADDR); lpSin = (LPSOCKADDR_IN)lpVariable; lpVariable += sizeof(*lpSin); lpSin->sin_family = AF_INET; lpSin->sin_port = 0; #ifdef TEST_CODE //if (i) { *(LPDWORD)&lpSin->sin_addr = 0x04030201; //*(LPDWORD)&lpSin->sin_addr = 0x1cfe379d; //} #else *(LPDWORD)&lpSin->sin_addr = *(LPDWORD)addressList[i]; #endif memset(lpSin->sin_zero, 0, sizeof(lpSin->sin_zero)); addresses[i].AddrInfo.iSocketType = SOCK_STREAM; addresses[i].AddrInfo.iProtocol = IPPROTO_TCP; addresses[i].IsValid = TRUE; } #ifdef TEST_CODE *((LPDWORD)&((LPSOCKADDR_IN)addresses[3].AddrInfo.RemoteAddr.lpSockaddr)->sin_addr) = *(LPDWORD)addressList[0]; //((LPSOCKADDR_IN)addresses[7].AddrInfo.RemoteAddr.lpSockaddr)->sin_addr = ((LPSOCKADDR_IN)addresses[0].AddrInfo.RemoteAddr.lpSockaddr)->sin_addr; //*((LPDWORD)&((LPSOCKADDR_IN)addresses[0].AddrInfo.RemoteAddr.lpSockaddr)->sin_addr) = 0x04030201; #endif // // update the object // INET_ASSERT(m_AddressCount == 0); INET_ASSERT(m_Addresses == NULL); m_AddressCount = nAddrs; m_BadAddressCount = 0; m_Addresses = addresses; m_CurrentAddress = 0; return ERROR_SUCCESS; }