1198 lines
31 KiB
C++
1198 lines
31 KiB
C++
/*++
|
||
|
||
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 <wininetp.h>
|
||
#include <perfdiag.hxx>
|
||
|
||
//#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;
|
||
}
|