You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1197 lines
31 KiB
1197 lines
31 KiB
/*++
|
|
|
|
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;
|
|
}
|