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.
950 lines
26 KiB
950 lines
26 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1996.
|
|
//
|
|
// File: mach.cxx
|
|
//
|
|
// Contents:
|
|
// Machine naming helper objects
|
|
//
|
|
// History:
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "act.hxx"
|
|
#include <mach.hxx>
|
|
#include <misc.hxx>
|
|
|
|
// Singleton instance:
|
|
CMachineName gMachineName;
|
|
|
|
// Defn of global ptr external parties use:
|
|
CMachineName * gpMachineName = &gMachineName;
|
|
|
|
CIPAddrs::CIPAddrs() :
|
|
_lRefs(1), // constructed with non-zero refcount!
|
|
_pIPAddresses(NULL)
|
|
{
|
|
}
|
|
CIPAddrs::~CIPAddrs()
|
|
{
|
|
ASSERT(_lRefs == 0);
|
|
|
|
if (_pIPAddresses)
|
|
{
|
|
PrivMemFree(_pIPAddresses);
|
|
}
|
|
}
|
|
void CIPAddrs::IncRefCount()
|
|
{
|
|
InterlockedIncrement(&_lRefs);
|
|
}
|
|
void CIPAddrs::DecRefCount()
|
|
{
|
|
LONG lRefs = InterlockedDecrement(&_lRefs);
|
|
if (lRefs == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
const DWORD MAX_IPV4_STRING_BUF = ((INET_ADDRSTRLEN+1) * sizeof(WCHAR));
|
|
const DWORD MAX_IPV6_STRING_BUF = ((INET6_ADDRSTRLEN+1) * sizeof(WCHAR));
|
|
|
|
CMachineName::CMachineName()
|
|
{
|
|
_pIPAddresses = NULL;
|
|
_pwszNetBiosName = NULL;
|
|
_pwszDNSName = NULL;
|
|
_bIPV4AddrsChanged = TRUE;
|
|
_bIPV6AddrsChanged = TRUE;
|
|
_bInitialized = FALSE;
|
|
|
|
InitAQD(&_aqdIPV4, AF_INET);
|
|
InitAQD(&_aqdIPV6, AF_INET6);
|
|
|
|
ZeroMemory(&_csMachineNameLock, sizeof(CRITICAL_SECTION));
|
|
}
|
|
|
|
|
|
void
|
|
CMachineName::InitAQD(
|
|
ADDRESS_QUERY_DATA* paqd,
|
|
int addrfamily)
|
|
{
|
|
ASSERT(paqd);
|
|
|
|
ZeroMemory(paqd, sizeof(ADDRESS_QUERY_DATA));
|
|
paqd->dwSig = ADDRQUERYDATA_SIG;
|
|
paqd->socket = INVALID_SOCKET;
|
|
paqd->addrfamily = addrfamily;
|
|
if (addrfamily == AF_INET)
|
|
{
|
|
paqd->pfnIsGlobalAddress = CMachineName::IsIPV4AddressGlobal;
|
|
paqd->dwMaxAddrStringBufSize = MAX_IPV4_STRING_BUF;
|
|
}
|
|
else if (addrfamily == AF_INET6)
|
|
{
|
|
paqd->pfnIsGlobalAddress = CMachineName::IsIPV6AddressGlobal;
|
|
paqd->dwMaxAddrStringBufSize = MAX_IPV6_STRING_BUF;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
|
|
CMachineName::~CMachineName()
|
|
{
|
|
// CODEWORK: This object does not cleanup its resources. This
|
|
// could be fixed, but is pointless at the present time since the
|
|
// object lives for the life of the RPCSS svchost, which lives for
|
|
// the life of the machine boot.
|
|
}
|
|
|
|
BOOL CMachineName::Initialize()
|
|
{
|
|
NTSTATUS status;
|
|
|
|
// Initialize lock
|
|
status = RtlInitializeCriticalSection(&_csMachineNameLock);
|
|
_bInitialized = NT_SUCCESS(status);
|
|
|
|
return _bInitialized;
|
|
}
|
|
|
|
void
|
|
CMachineName::IPAddrsChanged(int addrfamily)
|
|
{
|
|
ASSERT(_bInitialized);
|
|
|
|
switch (addrfamily)
|
|
{
|
|
case AF_INET:
|
|
// IPV4 addresses changed
|
|
_bIPV4AddrsChanged = TRUE;
|
|
break;
|
|
case AF_INET6:
|
|
// IPV6 addresses changed
|
|
ASSERT(gAddrRefreshMgr.IsIPV6Installed() == TRUE);
|
|
_bIPV6AddrsChanged = TRUE;
|
|
break;
|
|
default:
|
|
ASSERT(0 && "Address change signalled on unknown addr family");
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
CMachineName::Compare( IN WCHAR * pwszName )
|
|
{
|
|
CIPAddrs* pIPAddrs = NULL;
|
|
|
|
ASSERT(_bInitialized);
|
|
|
|
if (!pwszName)
|
|
return FALSE;
|
|
|
|
// Okay to check hard-coded names first (they never change)
|
|
if (CompareHardCodedLocalHostNames(pwszName))
|
|
return TRUE;
|
|
|
|
// Get netbios name if we haven't done so already:
|
|
if (!_pwszNetBiosName)
|
|
{
|
|
SetNetBIOSName();
|
|
}
|
|
|
|
if (_pwszNetBiosName && !lstrcmpiW(pwszName, _pwszNetBiosName))
|
|
return TRUE;
|
|
|
|
// Don't go any farther unless we're actually using TCP
|
|
if (!gAddrRefreshMgr.ListenedOnTCP())
|
|
return FALSE;
|
|
|
|
// Get DNS name if we haven't done so already.
|
|
if (! _pwszDNSName)
|
|
{
|
|
SetDNSName();
|
|
}
|
|
|
|
if (_pwszDNSName && !lstrcmpiW(pwszName, _pwszDNSName))
|
|
return TRUE;
|
|
|
|
// review: there are other names we could be checking for here, see docs
|
|
// on GetComputerNameEx. Need to be wary of cluster names though.
|
|
|
|
pIPAddrs = GetIPAddrs();
|
|
if (pIPAddrs)
|
|
{
|
|
NetworkAddressVector* pNetworkAddrVector = pIPAddrs->_pIPAddresses;
|
|
for ( DWORD n = 0; n < pNetworkAddrVector->Count; n++ )
|
|
{
|
|
if (!lstrcmpiW(pwszName, pNetworkAddrVector->NetworkAddresses[n]))
|
|
{
|
|
pIPAddrs->DecRefCount();
|
|
return TRUE;
|
|
}
|
|
}
|
|
pIPAddrs->DecRefCount();
|
|
}
|
|
|
|
// We do this last because a side-effect of calling GetIPAddrs is that it will
|
|
// populate the list of IPV4 & IPV6 localhost names, if any.
|
|
if (CompareDynamicLocalHostNames(pwszName))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// CMachineName::GetIPAddrs()
|
|
//
|
|
// Returns a pointer to a refcounted CIPAddrs for this
|
|
// machine. If we don't yet have a non-localhost ip,
|
|
// then we keep trying to get one.
|
|
//
|
|
CIPAddrs*
|
|
CMachineName::GetIPAddrs()
|
|
{
|
|
ASSERT(_bInitialized);
|
|
|
|
CMutexLock lock(&_csMachineNameLock);
|
|
|
|
// should we call gAddrRefreshMgr.ListenedOnTCP() here
|
|
// and simply return an empty vector if so?
|
|
|
|
// If anything changed, or if we don't have any
|
|
// addresses at all, go query for the current stuff
|
|
if (_bIPV4AddrsChanged ||
|
|
_bIPV6AddrsChanged ||
|
|
!_pIPAddresses)
|
|
{
|
|
// Release old addresses, if any
|
|
if (_pIPAddresses)
|
|
{
|
|
_pIPAddresses->DecRefCount();
|
|
_pIPAddresses = NULL;
|
|
}
|
|
|
|
_pIPAddresses = QueryAddresses();
|
|
if (!_pIPAddresses)
|
|
return NULL;
|
|
}
|
|
|
|
ASSERT(_pIPAddresses);
|
|
_pIPAddresses->IncRefCount();
|
|
|
|
return _pIPAddresses;
|
|
}
|
|
|
|
WCHAR*
|
|
CMachineName::NetBiosName()
|
|
{
|
|
ASSERT(_bInitialized);
|
|
|
|
if (!_pwszNetBiosName)
|
|
{
|
|
SetNetBIOSName();
|
|
}
|
|
|
|
return _pwszNetBiosName;
|
|
}
|
|
|
|
WCHAR*
|
|
CMachineName::DNSName()
|
|
{
|
|
ASSERT(_bInitialized);
|
|
|
|
if (!_pwszDNSName)
|
|
{
|
|
SetDNSName();
|
|
}
|
|
|
|
return _pwszDNSName;
|
|
}
|
|
|
|
CIPAddrs*
|
|
CMachineName::QueryAddresses()
|
|
{
|
|
// Assumes we hold _csMachineNameLock
|
|
|
|
// If necessary free old ipv4 addresses and requery
|
|
if (_bIPV4AddrsChanged)
|
|
{
|
|
_bIPV4AddrsChanged = FALSE;
|
|
BOOL fRet = QueryAddressesSpecific(&_aqdIPV4);
|
|
if (!fRet)
|
|
{
|
|
// If something went wrong, just keep going. The only thing
|
|
// we could do here is refuse to supply bindings. If we keep
|
|
// going though, the worst thing that could occur (IMHO) is
|
|
// that the bindings might only contain a DNS name. Better
|
|
// to degrade gracefully than refuse service altogether.
|
|
}
|
|
}
|
|
|
|
// If necessary free old ipv6 addresses and requery
|
|
if (_bIPV6AddrsChanged && gAddrRefreshMgr.IsIPV6Installed())
|
|
{
|
|
_bIPV6AddrsChanged = FALSE;
|
|
BOOL fRet = QueryAddressesSpecific(&_aqdIPV6);
|
|
if (!fRet)
|
|
{
|
|
// If something went wrong, just keep going. The only thing
|
|
// we could do here is refuse to supply bindings. If we keep
|
|
// going though, the worst thing that could occur (IMHO) is
|
|
// that the bindings might only contain a DNS name. Better
|
|
// to degrade gracefully than refuse service altogether.
|
|
}
|
|
}
|
|
|
|
// We now have the current IPV4 + IPV6 addresses, now
|
|
// just merge them.
|
|
CIPAddrs* pIPAddrs = MergeAddresses();
|
|
if (!pIPAddrs)
|
|
return NULL;
|
|
|
|
return pIPAddrs;
|
|
}
|
|
|
|
CIPAddrs*
|
|
CMachineName::MergeAddresses()
|
|
{
|
|
DWORD dwCurrentAddress = 0;
|
|
DWORD dwTotalAddresses = 0;
|
|
CIPAddrs* pIPAddrs = NULL;
|
|
NetworkAddressVector* pVector = NULL;
|
|
DWORD dwTotalVectorStorageNeeded = 0;
|
|
|
|
// Assumes we hold _csMachineNameLock
|
|
|
|
// Allocate new wrapper object
|
|
pIPAddrs = new CIPAddrs();
|
|
if (!pIPAddrs)
|
|
return NULL;
|
|
|
|
// Figure out how many addresses we have
|
|
if (_aqdIPV4.pAddresses)
|
|
{
|
|
dwTotalAddresses += _aqdIPV4.pAddresses->Count;
|
|
}
|
|
if (_aqdIPV6.pAddresses)
|
|
{
|
|
dwTotalAddresses += _aqdIPV6.pAddresses->Count;
|
|
}
|
|
|
|
//
|
|
// Handle case with zero addresses
|
|
//
|
|
if (dwTotalAddresses == 0)
|
|
{
|
|
// Allocate an empty vector
|
|
pVector = (NetworkAddressVector*)PrivMemAlloc(sizeof(NetworkAddressVector));
|
|
if (pVector)
|
|
{
|
|
pVector->Count = 0;
|
|
pVector->StringBufferSpace = 0;
|
|
pVector->NetworkAddresses = NULL;
|
|
pIPAddrs->_pIPAddresses = pVector;
|
|
}
|
|
else
|
|
{
|
|
pIPAddrs->DecRefCount();
|
|
pIPAddrs = NULL;
|
|
}
|
|
return pIPAddrs;
|
|
}
|
|
|
|
//
|
|
// Calculate total memory needed for combined vector and
|
|
// allocate it
|
|
//
|
|
dwTotalVectorStorageNeeded = sizeof(NetworkAddressVector);
|
|
dwTotalVectorStorageNeeded += (sizeof(WCHAR*) * dwTotalAddresses);
|
|
if (_aqdIPV4.pAddresses)
|
|
{
|
|
dwTotalVectorStorageNeeded += _aqdIPV4.pAddresses->StringBufferSpace;
|
|
}
|
|
if (_aqdIPV6.pAddresses)
|
|
{
|
|
dwTotalVectorStorageNeeded += _aqdIPV6.pAddresses->StringBufferSpace;
|
|
}
|
|
|
|
pVector = (NetworkAddressVector*)PrivMemAlloc(dwTotalVectorStorageNeeded);
|
|
if (!pVector)
|
|
{
|
|
pIPAddrs->DecRefCount();
|
|
pIPAddrs = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Init struct and set it up for copying
|
|
//
|
|
ZeroMemory(pVector, dwTotalVectorStorageNeeded);
|
|
pVector->Count = dwTotalAddresses;
|
|
if (_aqdIPV4.pAddresses)
|
|
{
|
|
pVector->StringBufferSpace = _aqdIPV4.pAddresses->StringBufferSpace;
|
|
}
|
|
if (_aqdIPV6.pAddresses)
|
|
{
|
|
pVector->StringBufferSpace += _aqdIPV6.pAddresses->StringBufferSpace;
|
|
}
|
|
|
|
pVector->NetworkAddresses = (WCHAR**)&pVector[1];
|
|
|
|
//
|
|
// Copy addresses into the vector
|
|
//
|
|
dwCurrentAddress = 0;
|
|
WCHAR* pszCurrentAddress = (WCHAR*)(&pVector->NetworkAddresses[dwTotalAddresses]);
|
|
if (_aqdIPV4.pAddresses)
|
|
{
|
|
CopySrcVectorToTargetVector (pVector,
|
|
_aqdIPV4.pAddresses,
|
|
&dwCurrentAddress,
|
|
&pszCurrentAddress);
|
|
ASSERT(dwCurrentAddress == _aqdIPV4.pAddresses->Count);
|
|
}
|
|
if (_aqdIPV6.pAddresses)
|
|
{
|
|
CopySrcVectorToTargetVector(pVector,
|
|
_aqdIPV6.pAddresses,
|
|
&dwCurrentAddress,
|
|
&pszCurrentAddress);
|
|
}
|
|
ASSERT(dwCurrentAddress == dwTotalAddresses);
|
|
|
|
ValidateVector(pVector);
|
|
|
|
//
|
|
// Success
|
|
//
|
|
pIPAddrs->_pIPAddresses = pVector;
|
|
return pIPAddrs;
|
|
}
|
|
|
|
void
|
|
CMachineName::CopySrcVectorToTargetVector (
|
|
NetworkAddressVector* pTargetVector, // The vector to copy into
|
|
NetworkAddressVector* pSourceVector, // The vector to copy from
|
|
DWORD * pdwCurrentAddress, // On input, the index in the vector to start copying into.
|
|
WCHAR** ppszCurrentAddress) // On input, the buffer position to start copying into.
|
|
{
|
|
// Do nothing in case of an empty source vector
|
|
if (pSourceVector->Count == 0)
|
|
return;
|
|
|
|
// Copy down the [in,out] parameters for shorthand purposes.
|
|
DWORD dwCurrent = *pdwCurrentAddress;
|
|
WCHAR *pszCurrent = *ppszCurrentAddress;
|
|
for (DWORD i = 0; i < pSourceVector->Count; i++)
|
|
{
|
|
// Lay the source network address into the buffer at wszCurrent.
|
|
lstrcpy(pszCurrent, pSourceVector->NetworkAddresses[i]);
|
|
|
|
// Now set the pointer in pTargetVector to point to wszCurrent...
|
|
pTargetVector->NetworkAddresses[dwCurrent] = pszCurrent;
|
|
//
|
|
// ...and advance wszCurrent to the next free spot in the buffer.
|
|
pszCurrent += (lstrlen(pszCurrent) + 1);
|
|
dwCurrent++;
|
|
}
|
|
|
|
// Update the [in,out] params.
|
|
*pdwCurrentAddress = dwCurrent;
|
|
*ppszCurrentAddress = pszCurrent;
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
CMachineName::IsIPV4AddressGlobal(LPSOCKADDR psockaddr)
|
|
{
|
|
ASSERT(psockaddr);
|
|
|
|
// undone? what do we need to check here? do we even see localhost
|
|
// addresses from a socket query? (not usually)
|
|
|
|
//in6_addr* pin6addr = &(((sockaddr_in6*)psockaddr)->sin6_addr);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
CMachineName::IsIPV6AddressGlobal(LPSOCKADDR psockaddr)
|
|
{
|
|
ASSERT(psockaddr);
|
|
|
|
in6_addr* pin6addr = &(((sockaddr_in6*)psockaddr)->sin6_addr);
|
|
|
|
// This macro just checks for ::1%1
|
|
if (IN6_IS_ADDR_LOOPBACK(pin6addr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Unfortunately, the IN6_IS_ADDR_LOOPBACK macro doesn't cover all
|
|
// cases. Specifically, it doesn't work for fe80::1. Here we
|
|
// have a hard-coded check for this:
|
|
if ((pin6addr->s6_words[0] == 0x80fe) &&
|
|
(pin6addr->s6_words[1] == 0x0) &&
|
|
(pin6addr->s6_words[2] == 0x0) &&
|
|
(pin6addr->s6_words[3] == 0x0) &&
|
|
(pin6addr->s6_words[4] == 0x0) &&
|
|
(pin6addr->s6_words[5] == 0x0) &&
|
|
(pin6addr->s6_words[6] == 0x0) &&
|
|
(pin6addr->s6_words[7] == 0x0100))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// No link-local addresses
|
|
if (IN6_IS_ADDR_LINKLOCAL(pin6addr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// No site local addresses.
|
|
if (IN6_IS_ADDR_SITELOCAL(pin6addr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// At this point, we have concluded that the IPV6 address in question is
|
|
// a "global" scope address, and hence is okay to put in the bindings.
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
CMachineName::SetNetBIOSName()
|
|
{
|
|
CMutexLock lock(&_csMachineNameLock);
|
|
if (!_pwszNetBiosName)
|
|
{
|
|
SetComputerNameHelper(ComputerNamePhysicalNetBIOS, &_pwszNetBiosName);
|
|
}
|
|
}
|
|
|
|
void
|
|
CMachineName::SetDNSName()
|
|
{
|
|
CMutexLock lock(&_csMachineNameLock);
|
|
if (!_pwszDNSName)
|
|
{
|
|
SetComputerNameHelper(ComputerNamePhysicalDnsFullyQualified, &_pwszDNSName);
|
|
}
|
|
}
|
|
|
|
void
|
|
CMachineName::SetComputerNameHelper(
|
|
COMPUTER_NAME_FORMAT format,
|
|
WCHAR** ppwszName
|
|
)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
DWORD dwSize = 0;
|
|
|
|
ASSERT(ppwszName);
|
|
|
|
*ppwszName = NULL;
|
|
|
|
// Get needed buffer size
|
|
fRet = GetComputerNameEx(format, NULL, &dwSize);
|
|
if (!fRet)
|
|
{
|
|
// Be resilient if failures occur, sometimes network
|
|
// apis can be flaky during machine boot (for instance)
|
|
if (GetLastError() == ERROR_MORE_DATA)
|
|
{
|
|
ASSERT(dwSize > 0);
|
|
if (dwSize == 0)
|
|
return;
|
|
|
|
WCHAR* pwszName = new WCHAR[dwSize];
|
|
if (!pwszName)
|
|
return;
|
|
|
|
// Get name for real
|
|
fRet = GetComputerNameEx(format, pwszName, &dwSize);
|
|
if (fRet)
|
|
{
|
|
// success
|
|
*ppwszName = pwszName;
|
|
}
|
|
else
|
|
{
|
|
// still failed? hmm
|
|
ASSERT(0 && "Unexpected failure from GetComputerNameEx");
|
|
delete [] pwszName;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
CMachineName::CompareHardCodedLocalHostNames(WCHAR* pwszName)
|
|
{
|
|
ASSERT(pwszName);
|
|
|
|
// Some localhost aliases don't show up in socket queries.
|
|
|
|
if (!lstrcmpi(L"localhost", pwszName))
|
|
return TRUE;
|
|
|
|
if (!lstrcmpi(L"127.0.0.1", pwszName))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CMachineName::CompareDynamicLocalHostNames(WCHAR* pwszName)
|
|
{
|
|
DWORD i;
|
|
|
|
ASSERT(pwszName);
|
|
|
|
CMutexLock lock(&_csMachineNameLock);
|
|
|
|
if (_aqdIPV4.pLocalOnlyAddresses)
|
|
{
|
|
for (i = 0; i < _aqdIPV4.pLocalOnlyAddresses->Count; i++)
|
|
{
|
|
if (!lstrcmpi(_aqdIPV4.pLocalOnlyAddresses->NetworkAddresses[i],
|
|
pwszName))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_aqdIPV6.pLocalOnlyAddresses)
|
|
{
|
|
for (i = 0; i < _aqdIPV6.pLocalOnlyAddresses->Count; i++)
|
|
{
|
|
if (!lstrcmpi(_aqdIPV6.pLocalOnlyAddresses->NetworkAddresses[i],
|
|
pwszName))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CMachineName::QueryAddressesSpecific(ADDRESS_QUERY_DATA* paqd)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves a SOCKET_ADDRESS_LIST containing addresses supported by this
|
|
machine for the specified address family (ipv4 or ipv6) and stores
|
|
it in either _pIPV4Addrs or _pIPV6Addrs.
|
|
|
|
Arguments:
|
|
|
|
paqd - structure containing data with which to query addresses on
|
|
|
|
Return Value:
|
|
|
|
TRUE -- valid results are stored in paqd
|
|
FALSE -- error occurred. One or both of paqd->pAddresses and
|
|
paqd->pLocalAddresses may be NULL.
|
|
|
|
--*/
|
|
{
|
|
int ret;
|
|
BOOL fReturn = TRUE;
|
|
DWORD dwBytesReturned;
|
|
DWORD i;
|
|
SOCKET* pSocket = NULL;
|
|
SOCKET_ADDRESS_LIST** ppSocketAddrList = NULL;
|
|
DWORD* pdwSocketAddrBufferSize = NULL;
|
|
|
|
ASSERT(paqd->dwSig == ADDRQUERYDATA_SIG);
|
|
|
|
// Allocate socket if we haven't already
|
|
if (paqd->socket == INVALID_SOCKET)
|
|
{
|
|
paqd->socket = WSASocket(paqd->addrfamily,
|
|
SOCK_STREAM,
|
|
IPPROTO_TCP,
|
|
NULL,
|
|
0,
|
|
0
|
|
);
|
|
if (paqd->socket == INVALID_SOCKET)
|
|
return NULL;
|
|
|
|
// else we got a socket for this addrfamily, which we keep forever.
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
ret = WSAIoctl(paqd->socket,
|
|
SIO_ADDRESS_LIST_QUERY,
|
|
NULL,
|
|
0,
|
|
(BYTE*)paqd->pSockAddrList,
|
|
paqd->dwSockAddrListBufferSize,
|
|
&dwBytesReturned,
|
|
NULL,
|
|
NULL);
|
|
|
|
paqd->dwTotalTimesQueried++;
|
|
|
|
if (ret == 0)
|
|
{
|
|
// Success
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Failed. If need bigger buffer, allocate it
|
|
// and try again. Otherwise fail.
|
|
if (WSAGetLastError() == WSAEFAULT)
|
|
{
|
|
ASSERT(dwBytesReturned > paqd->dwSockAddrListBufferSize);
|
|
|
|
if (paqd->pSockAddrList)
|
|
{
|
|
ASSERT(paqd->dwSockAddrListBufferSize > 0);
|
|
PrivMemFree(paqd->pSockAddrList);
|
|
paqd->pSockAddrList = NULL;
|
|
paqd->dwSockAddrListBufferSize = 0;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(paqd->dwSockAddrListBufferSize == 0);
|
|
}
|
|
|
|
paqd->pSockAddrList = (SOCKET_ADDRESS_LIST*)PrivMemAlloc(dwBytesReturned);
|
|
if (!(paqd->pSockAddrList))
|
|
{
|
|
fReturn = FALSE;
|
|
break;
|
|
}
|
|
|
|
paqd->dwSockAddrListBufferSize = dwBytesReturned;
|
|
}
|
|
else
|
|
{
|
|
// some other error
|
|
fReturn = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fReturn)
|
|
{
|
|
// Build local-only vector
|
|
if (!BuildVector(paqd, FALSE))
|
|
fReturn = FALSE;
|
|
|
|
// Build non-local-only vector
|
|
if (!BuildVector(paqd, TRUE))
|
|
fReturn = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// if we failed above before calling BuildVector, then we need to
|
|
// free and NULL pAddresses & pLocalAddresses so that downstream
|
|
// code doesn't assume that they are pointing to valid contents.
|
|
PrivMemFree(paqd->pAddresses);
|
|
paqd->pAddresses = NULL;
|
|
paqd->dwAddrVectorSize = 0;
|
|
PrivMemFree(paqd->pLocalOnlyAddresses);
|
|
paqd->pLocalOnlyAddresses = NULL;
|
|
paqd->dwLocalAddrVectorSize = 0;
|
|
}
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
BOOL
|
|
CMachineName::BuildVector(ADDRESS_QUERY_DATA* paqd, BOOL fLocalOnly)
|
|
{
|
|
int i = 0;
|
|
DWORD* pdwVectorSize = NULL;
|
|
NetworkAddressVector** ppVector = NULL;
|
|
DWORD dwTotalAddrs = 0;
|
|
BOOL fIsGlobalAddress;
|
|
|
|
ASSERT(paqd->dwSig == ADDRQUERYDATA_SIG);
|
|
|
|
//
|
|
// Sum up how many addresses of the non-local or local-only type there
|
|
// are, so we know how much memory to allocate.
|
|
//
|
|
for (i = 0; i < paqd->pSockAddrList->iAddressCount; i++)
|
|
{
|
|
fIsGlobalAddress = (!paqd->pfnIsGlobalAddress ||
|
|
paqd->pfnIsGlobalAddress(paqd->pSockAddrList->Address[i].lpSockaddr));
|
|
if ((fIsGlobalAddress && !fLocalOnly) || (!fIsGlobalAddress && fLocalOnly))
|
|
{
|
|
dwTotalAddrs++;
|
|
}
|
|
}
|
|
|
|
if (fLocalOnly)
|
|
{
|
|
pdwVectorSize = &paqd->dwLocalAddrVectorSize;
|
|
ppVector = &paqd->pLocalOnlyAddresses;
|
|
}
|
|
else
|
|
{
|
|
pdwVectorSize = &paqd->dwAddrVectorSize;
|
|
ppVector = &paqd->pAddresses;
|
|
}
|
|
|
|
//
|
|
// Build a vector of the specified addresses
|
|
//
|
|
DWORD dwBufSizeNeeded;
|
|
|
|
//
|
|
// Figure out how much space we need. See comment below (right above call
|
|
// to WSAAddressToString) regarding string buffer space required per address.
|
|
//
|
|
dwBufSizeNeeded = sizeof(NetworkAddressVector) +
|
|
(dwTotalAddrs * sizeof(WCHAR*)) +
|
|
(dwTotalAddrs * paqd->dwMaxAddrStringBufSize);
|
|
|
|
// Figure out if we can use the old vector buffer, or allocate a new one
|
|
if (*pdwVectorSize < dwBufSizeNeeded)
|
|
{
|
|
ASSERT((*pdwVectorSize > 0) ? (*ppVector != NULL) : (*ppVector == NULL));
|
|
PrivMemFree(*ppVector);
|
|
*pdwVectorSize = 0;
|
|
*ppVector = (NetworkAddressVector*)PrivMemAlloc(dwBufSizeNeeded);
|
|
if (!(*ppVector))
|
|
return FALSE;
|
|
*pdwVectorSize = dwBufSizeNeeded;
|
|
}
|
|
|
|
// Zero the buffer
|
|
ASSERT(*ppVector);
|
|
ZeroMemory(*ppVector, *pdwVectorSize);
|
|
|
|
// Fill in the vector
|
|
if (dwTotalAddrs > 0)
|
|
{
|
|
NetworkAddressVector* pVector = *ppVector;
|
|
|
|
pVector->Count = dwTotalAddrs;
|
|
pVector->StringBufferSpace = (dwTotalAddrs * paqd->dwMaxAddrStringBufSize);
|
|
pVector->NetworkAddresses = (WCHAR**)&pVector[1];
|
|
|
|
DWORD dwCurrentVectorAddress = 0;
|
|
WCHAR* pszNextAddress = (WCHAR*)&pVector->NetworkAddresses[dwTotalAddrs];
|
|
|
|
for (i = 0;
|
|
(i < paqd->pSockAddrList->iAddressCount) && (dwCurrentVectorAddress < dwTotalAddrs);
|
|
i++)
|
|
{
|
|
BOOL fUseCurrentAddress;
|
|
|
|
// Re-calculate if the current address in the sockaddrlist is going to
|
|
// be put into the vector.
|
|
fIsGlobalAddress = (!(paqd->pfnIsGlobalAddress) ||
|
|
paqd->pfnIsGlobalAddress(paqd->pSockAddrList->Address[i].lpSockaddr));
|
|
|
|
fUseCurrentAddress = (fIsGlobalAddress && !fLocalOnly) || (!fIsGlobalAddress && fLocalOnly);
|
|
|
|
if (fUseCurrentAddress)
|
|
{
|
|
int ret;
|
|
DWORD dwAddrBufLen;
|
|
|
|
dwAddrBufLen = paqd->dwMaxAddrStringBufSize;
|
|
|
|
// Note that we are being slightly sloppy here. In theory we could be
|
|
// callling WSAAddressToString with a zero-size output buffer, and WSATS
|
|
// would come back with an error and the exact required size of the buffer;
|
|
// We could then do this across all addresses going into the vector. However,
|
|
// there are some network stack bugs that don't let this work for every
|
|
// address type (ie, ipv6). Hence, we play it safe and allocate a "maximum"
|
|
// buffer space for every address.
|
|
ret = WSAAddressToString(paqd->pSockAddrList->Address[i].lpSockaddr,
|
|
paqd->pSockAddrList->Address[i].iSockaddrLength,
|
|
NULL,
|
|
pszNextAddress,
|
|
&dwAddrBufLen);
|
|
if (ret != 0)
|
|
{
|
|
// An error occurred. We don't expect "insufficient buffer" here, and other
|
|
// errors are not something we can recover from. Cleanup and return NULL.
|
|
PrivMemFree(*ppVector);
|
|
*ppVector = NULL;
|
|
*pdwVectorSize = 0;
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(dwAddrBufLen <= paqd->dwMaxAddrStringBufSize);
|
|
ASSERT((((DWORD)lstrlenW(pszNextAddress) + 1) * sizeof(WCHAR)) <= paqd->dwMaxAddrStringBufSize);
|
|
|
|
// Write in address of just copied string
|
|
pVector->NetworkAddresses[dwCurrentVectorAddress] = pszNextAddress;
|
|
|
|
// Advance pointers to next free spot in the buffer
|
|
dwCurrentVectorAddress++;
|
|
pszNextAddress += (lstrlen(pszNextAddress) + 1);
|
|
}
|
|
}
|
|
// else skip this address
|
|
}
|
|
|
|
ASSERT(dwCurrentVectorAddress == dwTotalAddrs);
|
|
}
|
|
else
|
|
{
|
|
// Zero addresses and the vector is already "empty" (it was
|
|
// zeroed out above). So nothing to do.
|
|
}
|
|
|
|
ValidateVector(*ppVector);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
CMachineName::ValidateVector(NetworkAddressVector* pVector)
|
|
{
|
|
#ifdef DBG
|
|
ASSERT(pVector);
|
|
if (pVector->Count > 0)
|
|
{
|
|
ASSERT(pVector->StringBufferSpace > 0);
|
|
ASSERT(pVector->NetworkAddresses != NULL);
|
|
DWORD i;
|
|
DWORD cchTotalStringSpace = 0;
|
|
for (i = 0; i < pVector->Count; i++)
|
|
{
|
|
BYTE* pStart = (BYTE*)&pVector->NetworkAddresses[pVector->Count];
|
|
BYTE* pEnd = pStart + pVector->StringBufferSpace;
|
|
ASSERT((BYTE*)pVector->NetworkAddresses[i] >= pStart);
|
|
ASSERT((BYTE*)pVector->NetworkAddresses[i] < pEnd);
|
|
cchTotalStringSpace += (lstrlenW(pVector->NetworkAddresses[i]) + 1);
|
|
}
|
|
|
|
ASSERT((cchTotalStringSpace * sizeof(WCHAR)) <= pVector->StringBufferSpace);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(pVector->StringBufferSpace == 0);
|
|
ASSERT(pVector->NetworkAddresses == NULL);
|
|
}
|
|
#endif
|
|
}
|
|
|