Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2447 lines
61 KiB

/*++
Copyright (C) 2001 Microsoft Corporation
All rights reserved.
Module Name:
NCnamecache.cxx
Abstract:
Implemntation of the name relosution cache classes and functions.
Author:
Felix Maxa (AMaxa) 16 May 2001 - Created
Felix Maxa (AMaxa) 30 Sept 2001 - Added the cache for failures
Revision History:
--*/
#include "precomp.h"
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <lm.h>
#include "ncnamecache.hxx"
#include "ncsockets.hxx"
#include "ncclusfunc.hxx"
using namespace NCoreLibrary;
/*++
Explanation of how the cache works.
The basic idea is that every local or cluster pIniSpooler has an associated node in the cache. Each node keeps
a list of IPs that are handled by the node name, and a list of alternate names (FQDN, aliases etc.). When a
node is created only the IP lists are popoluated. The alternate names are popoluated dynalically, when a request
to add a name to the cache comes in.
Example of cache. List of nodes after the pIniSpooler for the local machine is created.
AMAXA-NODE1 (this is the node name)
IP Addresses: 172.31.173.91 <--- populated by the cache with the result of getaddrinfo("amaxa-node1")
Alternate names: Empty
Nodes for cluster pIniSpooler are always added in the head of the list, so they are searched first. We search
the nodes when we add a new name to the cache. We need to find the node where to add the new name. The algorithm is
the following:
If getaddrinfo(NewName) succeeds, then we get a list of IPs, NewNameIPs. We start searching if the NewNameIPs
are a subset of the IP addresses of nodes in the cache. For cluster nodes, every IP in NewNameIPs must be found
in the set of IP addresses for the node. For local nodes, one single IP in NewNameIPs must be found in IP Addresses.
If getaddrinfo(NewName) fails, then we check if the server name is in the list of transports enumerated by
NetServerTransportEnum. If positive, then it is an emulated server and we add it to the list of alternate names
for the node representing the local machine (AMAXA_NODE1 in the example)
Example of node list with cluster node.
AMAXA-CLS1 (this is the node name)
IP Addresses: 172.31.172.31
172.31.172.32
Alternate names: AMAXA-CLS1.NTDEV.MICROSOFT.COM
AMAXA-NODE1 (this is the node name)
IP Addresses: 172.31.173.91
Alternate names:
Note that a spooler cluster resource can be dependent on more than one IP address. That is why when cluster nodes
are created one needs to specify whet IP addresses it recognizes.
Let's say we want to add "AMAXA-NODE1.NTDEV.MICROSOFT.COM" to the cache. If the cluster IP resources are online,
then getaddrinfo("amaxxa-node1.ntdev.microsoft.com") returns 172.31.173.91 (IP gotten at boot) and 172.31.172.31,
172.31.172.32, IPs registered by the cluster. Based on the search algorithm above, the result will be:
AMAXA-CLS1 (this is the node name)
IP Addresses: 172.31.172.31
172.31.172.32
Alternate names: AMAXA-CLS1.NTDEV.MICROSOFT.COM
AMAXA-NODE1 (this is the node name)
IP Addresses: 172.31.173.91
Alternate names: AMAXA-NODE1.NTDEV.MICROSOFT.COM
Special cases when the cluster service is running on a machine.
Scenario 1)
a) spooler is stopped
b) cluster is running and resitered the IPs: 172.31.172.31, 172.31.172.32
c) spooler starts
d) the cache initializes and a node for the local machine is added
e) getaddrinfo(local machine) returns also the cluster IPs
f) local node has the list: 172.31.172.31, 172.31.172.32, 172.31.173.91
g) before the cluster ini spooler is created a call to OpenPrinter("\\AMAXA-CLS1") comes in
h) getaddrinfo("AMAXA-CLS1") resolves to 172.31.172.31
i) 172.31.172.31 is in the list of IPs for the local machine, so AMAXA-CLS1 is added to the alternate names for AMAXA-NODE1
This is NOT wnat we want! So we need to get rid of the cluster IPs in the list of the local node. When a local node is created,
the list of IP addresses is set to
Set of IPs returned by getaddrinfo() - Set of IPs belonging to cluster resources.
A PnP event is triggered when the local machine gets a new IP either via ipconfig or because a cluster IP resource comes
online or goes offline. We cannot distinguish if the event was triggered by cluster activity or by ipconfig. So when
we get such an event we refresh only the local node(s). We refresh it by deleting it from the cache and then adding it back.
That way the list of IPs is repopoluated.
The cluster nodes do not need refreshing. A spooler resource needs to be taken offline in order to make changes in its dependent
resources. In that case the cluster node is removed from the cache.
The failed cache.
Once the name cache was checked in we ran into bug 470345 "CSNW:Printing operations painfully slow". We had a printer connection
to a NetWare server. getaddrinfo(NetWareServerName) failed and took about 2.3 seconds to complete. This meant a huge performance
loss when printing to the NetWare server, because for each single OpenPrinter the spooler took at least 2 seconds before the
name cache failed in getaddrinfo(). So there was a need for a cache of failed server names. If getaddrinfo fails on a name, we add
the name to the fast cache. We keep the name in the cache for 1 minute. We won't call getaddrinfo on the name before that minute expires.
--*/
/*++
Name:
TNameResolutionCache::TNameResolutionCache
Description:
CTOR for TNameResolutionCache
Arguments:
None
Return Value:
None
--*/
TNameResolutionCache::
TNameResolutionCache(
IN DWORD AgingTime
) : m_AgingTime(AgingTime)
{
}
/*++
Name:
TNameResolutionCache::~TNameResolutionCache
Description:
DTOR for TNameResolutionCache
Arguments:
None
Return Value:
None
--*/
TNameResolutionCache::
~TNameResolutionCache(
VOID
)
{
}
/*++
Name:
TNameResolutionCache::IsValid
Description:
Returns S_OK if the TNameResolutionCache object was intialized
properly, any other HRESULT if initialization failed.
Arguments:
None
Return Value:
HRESULT
--*/
HRESULT
TNameResolutionCache::
IsValid(
VOID
)
{
TStatusH hRet;
hRet DBGCHK = m_Lock.IsValid();
if (SUCCEEDED(hRet))
{
hRet DBGCHK = m_FailedCache.IsValid();
}
return hRet;
}
/*++
Name:
TNameResolutionCache::GetNetBiosInfo
Description:
Gets the list of emulated sever names for the local machine. Checks if pszName
is in the list of emulated names. If it is, then paliasedServer will contain
the name of the machine the alias refers to (the local machine)
Arguments:
pszName - server alis to search for
pAliasedServer - will receive the server name that pszName refers to
Return Value:
S_OK - pszName is an alias for the local machine
S_FALSE - pszName is not an alias for the local machine
any other HR - an error occurred
--*/
HRESULT
TNameResolutionCache::
GetNetBiosInfo(
IN LPCSTR pszName,
IN TString *pAliasedServer
)
{
TStatusH hRetval;
hRetval DBGNOCHK = E_INVALIDARG;
if (pszName && *pszName && pAliasedServer)
{
PSERVER_TRANSPORT_INFO_0 TransportInfo = NULL;
DWORD EntriesRead;
DWORD EntriesTotal;
NET_API_STATUS Status;
Status = NetServerTransportEnum(NULL,
0,
reinterpret_cast<PBYTE *>(&TransportInfo),
MAX_PREFERRED_LENGTH,
&EntriesRead,
&EntriesTotal,
NULL);
hRetval DBGCHK = Status == NERR_Success ? S_OK : HRESULT_FROM_WIN32(Status);
if (SUCCEEDED(hRetval))
{
DWORD Index;
DWORD NameLength = strlen(pszName);
//
// pszName not found
//
hRetval DBGNOCHK = S_FALSE;
for (Index = 0; Index < EntriesRead; Index++)
{
if (TransportInfo[Index].svti0_transportaddresslength == NameLength)
{
//
// Check if pszName matches any of the addresses returned by NetServerTransportEnum
//
if (!_strnicmp(pszName,
reinterpret_cast<LPSTR>(TransportInfo[Index].svti0_transportaddress),
NameLength))
{
hRetval DBGCHK = pAliasedServer->Update(szMachineName + 2);
//
// No need to continue looping
//
break;
}
}
}
NetApiBufferFree(TransportInfo);
}
}
return hRetval;
}
/*++
Name:
TNameResolutionCache::GetAddrInfo
Description:
This function calls getaddrinfo on a server name. Then it populates
2 lists with the results of getaddrinfo. One list contains all
the IP address, the other one all the names associated with the
server name.
Arguments:
pszName - server name. Cannot be prefixed wth "\\".
pIPAddresses - pointer to list where to append the IP addresses
pAlternateNames - pointer to list where to append the names (normally the FQDN)
Return Value:
S_OK - getaddrinfo succeeded and the lists were populated
other HRESULT - an error occurred
--*/
HRESULT
TNameResolutionCache::
GetAddrInfo(
IN LPCSTR pszName,
IN NCoreLibrary::TList<TStringNode> *pIPAddresses,
IN NCoreLibrary::TList<TStringNode> *pAlternateNames
)
{
TStatusH hRetval;
hRetval DBGNOCHK = E_INVALIDARG;
if (pszName && *pszName && pIPAddresses && pAlternateNames)
{
TWinsockStart WsaStart;
hRetval DBGCHK = WsaStart.Valid();
if (SUCCEEDED(hRetval))
{
DWORD Error;
ADDRINFO *pAddrInfo;
ADDRINFO Hint = {0};
Hint.ai_flags = AI_CANONNAME;
Hint.ai_family = PF_INET;
Error = getaddrinfo(pszName, NULL, &Hint, &pAddrInfo);
hRetval DBGCHK = Error == ERROR_SUCCESS ? S_OK : GetWSAErrorAsHResult();
if (SUCCEEDED(hRetval))
{
ADDRINFO *pTemp;
LPWSTR pszBuffer = NULL;
DWORD cchBuffer = kBufferAllocHint;
DWORD cchNeeded = cchBuffer;
INT WsaError;
pszBuffer = new WCHAR[cchBuffer];
hRetval DBGCHK = pszBuffer ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hRetval))
{
for (pTemp = pAddrInfo; pTemp; pTemp = pTemp->ai_next)
{
WsaError = WSAAddressToString(pTemp->ai_addr,
pTemp->ai_addrlen,
NULL,
pszBuffer,
&cchNeeded);
if (WsaError == SOCKET_ERROR)
{
hRetval DBGCHK = GetWSAErrorAsHResult();
if (SCODE_CODE(hRetval) == WSAEFAULT)
{
delete [] pszBuffer;
cchBuffer = cchNeeded;
pszBuffer = new WCHAR[cchBuffer];
hRetval DBGCHK = pszBuffer ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hRetval))
{
WsaError = WSAAddressToString(pTemp->ai_addr,
pTemp->ai_addrlen,
NULL,
pszBuffer,
&cchNeeded);
hRetval DBGCHK = !WsaError ? S_OK : GetWSAErrorAsHResult();
}
else
{
//
// Fatal error. Mem alloc failed.
//
break;
}
}
}
if (SUCCEEDED(hRetval))
{
TStringNode *pItem = new TStringNode(pszBuffer);
hRetval DBGCHK = pItem ? pItem->Valid() : E_OUTOFMEMORY;
if (SUCCEEDED(hRetval))
{
hRetval DBGCHK = pIPAddresses->AddAtHead(pItem);
}
}
if (pTemp->ai_canonname)
{
LPWSTR pszUnicode = NULL;
hRetval DBGCHK = AnsiToUnicodeWithAlloc(pTemp->ai_canonname, &pszUnicode);
if (SUCCEEDED(hRetval))
{
TStringNode *pItem = new TStringNode(pszUnicode);
hRetval DBGCHK = pItem ? pItem->Valid() : E_OUTOFMEMORY;
if (SUCCEEDED(hRetval))
{
hRetval DBGCHK = pAlternateNames->AddAtHead(pItem);
}
delete [] pszUnicode;
}
}
} // for loop
delete [] pszBuffer;
}
freeaddrinfo(pAddrInfo);
}
}
}
return hRetval;
}
/*++
Name:
TNameResolutionCache::ExcludeIPsUsedByCluster
Description:
Excludes from pList all the names present in pClusterIPs.
Arguments:
pClusterIPs - list of IP addresses used by the cluster resources
pList - list of IP addresses
Return Value:
S_OK - all the elements in pClusterIPs were eliminated from pList
any other HRESULT - an error occurred
--*/
HRESULT
TNameResolutionCache::
ExcludeIPsUsedByCluster(
IN TList<TStringNode> *pClusterIPs,
IN TList<TStringNode> *pList
)
{
TStatusH hRetval;
hRetval DBGCHK = pClusterIPs ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hRetval))
{
TList<TStringNode>::TIterator ItClus(pClusterIPs);
for (ItClus.First(); !ItClus.IsDone(); ItClus.Next())
{
LPCWSTR pszClusString = ItClus.Current()->m_String;
DBGMSG(DBG_TRACE, ("IP used by cluster %ws\n", pszClusString));
TList<TStringNode>::TIterator ItList(pList);
for (ItList.First(); !ItList.IsDone(); ItList.Next())
{
if (!_wcsicmp(ItList.Current()->m_String, pszClusString))
{
TStringNode *pNodeToElim = ItList.Current();
hRetval DBGCHK = pList->Remove(pNodeToElim);
delete pNodeToElim;
break;
}
}
}
}
return hRetval;
}
/*++
Name:
TNameResolutionCache::IsNodeInCache
Description:
Checks if there is a node called pszName in the cache.
This function locks the cache.
Arguments:
pszNode - name to look for in the node list
Return Value:
S_OK - a node with name "pszName" is present in the list
S_FALSE - no node with name "pszName" is present
other HR - an error occrred while searching for pszName node
--*/
HRESULT
TNameResolutionCache::
IsNodeInCache(
IN LPCWSTR pszNode
)
{
TStatusH hRetval;
hRetval DBGCHK = pszNode ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hRetval))
{
NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
hRetval DBGNOCHK = S_FALSE;
for (It.First(); !It.IsDone(); It.Next())
{
hRetval DBGNOCHK = It.Current()->IsSameName(pszNode);
if (hRetval == S_OK)
{
break;
}
}
}
return hRetval;
}
/*++
Name:
TNameResolutionCache::DeleteNode
Description:
Deletes a node from the cache. The function locks the cache
Arguments:
pszNode - node to delete
Return Value:
S_OK - node was deleted
S_FALSE - node was not found
other HR - an error occrred while performing operation
--*/
HRESULT
TNameResolutionCache::
DeleteNode(
IN LPCWSTR pszNode
)
{
TStatusH hRetval;
hRetval DBGCHK = pszNode && *pszNode ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hRetval))
{
TResolutionCacheNode *pNode = NULL;
m_Lock.Enter();
TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
//
// We have not found a node containing pszName
//
hRetval DBGNOCHK = S_FALSE;
for (It.First(); !It.IsDone(); It.Next())
{
//
// Check if pszName is the name of the node
//
if (!_wcsicmp(It.Current()->m_Name, pszNode))
{
hRetval DBGCHK = S_OK;
pNode = It.Current();
if (pNode)
{
hRetval DBGCHK = m_NodeList.Remove(pNode);
}
break;
}
}
m_Lock.Leave();
delete pNode;
}
return hRetval;
}
/*++
Name:
TNameResolutionCache::IsNameInNodeCache
Description:
Looks for pszName in the list of IPs and names associated with the
node named pszNode. The function locks the cache
Arguments:
pszNode - node where to search for pszName
pszName - name to search for in a node
Return Value:
S_OK - a pszName is present in the list of names associated to pszNode
S_FALSE - a pszName is NOT present in the list of names associated to pszNode
other HR - an error occrred while searching for pszName node
--*/
HRESULT
TNameResolutionCache::
IsNameInNodeCache(
IN LPCWSTR pszNode,
IN LPCWSTR pszName
)
{
TStatusH hRetval;
hRetval DBGCHK = pszNode && pszName ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hRetval))
{
NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
hRetval DBGNOCHK = S_FALSE;
for (It.First(); !It.IsDone(); It.Next())
{
//
// Check of pszNode matches the name of the node pointer by the iterator
//
if (It.Current()->IsSameName(pszNode) == S_OK)
{
hRetval DBGCHK = It.Current()->IsNameInNodeCache(pszName);
break;
}
}
}
return hRetval;;
}
/*++
Name:
TNameResolutionCache::IsNameInCache
Description:
Checks is pszName is in the list of IPs and names associated with a
node in the cache. The function locks the cache.
Arguments:
pszName - name to search for in the cache
Return Value:
S_OK - a pszName is present in the cache
S_FALSE - a pszName is NOT present in the cache
other HR - an error occrred while searching for pszName
--*/
HRESULT
TNameResolutionCache::
IsNameInCache(
IN LPCWSTR pszName
)
{
TStatusH hRetval;
hRetval DBGCHK = pszName ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hRetval))
{
NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
hRetval DBGNOCHK = S_FALSE;
for (It.First(); !It.IsDone(); It.Next())
{
hRetval DBGNOCHK = It.Current()->IsNameInNodeCache(pszName);
if (hRetval == S_OK)
{
break;
}
}
}
return hRetval;
}
/*++
Name:
TNameResolutionCache::FindNameInCacheAndAgeOut
Description:
Searches for a certain name in the cache. If the name is found in the cache,
but it is aged out, the name is eliminated and S_FALSE is returned. The aging
time of names is specified as argument to the constructor of TNameResolutionCache
Arguments:
pszName - name to look for
Return Value:
S_OK - name was found
S_FALSE - name was not found or it is aged out (and was eliminated therefore)
any other HR - an error occurred
--*/
HRESULT
TNameResolutionCache::
FindNameInCacheAndAgeOut(
IN LPCWSTR pszName
)
{
TStatusH hRetval;
hRetval DBGCHK = pszName ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hRetval))
{
TStringNode *pStringNode = NULL;
m_Lock.Enter();
TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
//
// We have not found a node containing pszName
//
hRetval DBGNOCHK = S_FALSE;
for (It.First(); !It.IsDone(); It.Next())
{
//
// Check if pszName is the name of the node
//
if (!_wcsicmp(It.Current()->m_Name, pszName))
{
hRetval DBGCHK = S_OK;
break;
}
else
{
//
// Check if the name is in the list of alternate names
//
hRetval DBGNOCHK = It.Current()->GetStringNodeFromName(pszName, &pStringNode);
if (hRetval == S_OK)
{
//
// We found pszName in the node pointed to by pStringNode
//
DWORD CurrentTick = GetTickCount();
//
// Check if we need to age out the node
//
if (CurrentTick - pStringNode->m_Time > m_AgingTime)
{
hRetval DBGCHK = It.Current()->m_pAlternateNames->Remove(pStringNode);
if (SUCCEEDED(hRetval))
{
//
// Since the entry was aged out, we pretend we did not find it
//
hRetval DBGNOCHK = S_FALSE;
}
}
else
{
//
// Not aged out, prevent it from being deleted
//
pStringNode = NULL;
}
break;
}
}
}
m_Lock.Leave();
delete pStringNode;
}
return hRetval;;
}
/*++
Name:
TNameResolutionCache::CreateAndAddNodeWithIPAddresses
Description:
Checks if a node with pszName name is already in the cache. If not,
it creates a new node and populates it with data. Then it adds the
node to the list of nodes. pszName cannot be an IP address.
This function does not call getaddrinfo on pszName. It blindly fills
in the IP list for the node with data in ppszIPAddresses
Arguments:
pszName - name to create a new node for
bClusterNode - specifies if this node corresponds to a cluster ini spooler
ppszIPAddresses - array of strings representing IPs
cIPAddresses - count of lements in the array
Return Value:
S_OK - a new node was created and added to the cache
S_FALSE - a node with pszName name already existed in the cache
any other HR - an error occurred while performing the operation
--*/
HRESULT
TNameResolutionCache::
CreateAndAddNodeWithIPAddresses(
IN LPCWSTR pszName,
IN BOOL bClusterNode,
IN LPCWSTR *ppszIPAddresses,
IN DWORD cIPAddresses
)
{
TStatusH hRetval;
hRetval DBGCHK = pszName &&
ppszIPAddresses &&
*ppszIPAddresses &&
cIPAddresses > 0 ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hRetval))
{
//
// The function IsNodeInCache will lock the cache for the search
//
hRetval DBGCHK = IsNodeInCache(pszName);
//
// Not found in the cache
//
if (hRetval == S_FALSE)
{
TResolutionCacheNode *pNode = new TResolutionCacheNode(pszName, bClusterNode);
hRetval DBGCHK = pNode ? pNode->Valid() : E_OUTOFMEMORY;
//
// Add all the supported IP addresses
//
if (SUCCEEDED(hRetval))
{
DWORD Index;
for (Index = 0; Index < cIPAddresses && SUCCEEDED(hRetval); Index++)
{
TStringNode *pItem = new TStringNode(ppszIPAddresses[Index]);
hRetval DBGCHK = pItem ? pItem->Valid() : E_OUTOFMEMORY;
if (SUCCEEDED(hRetval))
{
hRetval DBGCHK = pNode->m_pIPAddresses->AddAtHead(pItem);
if (SUCCEEDED(hRetval))
{
//
// List tooks ownership of string node object
//
pItem = NULL;
}
}
delete pItem;
}
}
if (SUCCEEDED(hRetval))
{
NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
//
// Make sure a node with pszName was not added to the cache while
// we were calling the TResolutionCacheNode CTOR
//
hRetval DBGCHK = IsNodeInCache(pszName);
//
// Node not found
//
if (hRetval == S_FALSE)
{
if (bClusterNode)
{
hRetval DBGCHK = m_NodeList.AddAtHead(pNode);
}
else
{
hRetval DBGCHK = m_NodeList.AddAtTail(pNode);
}
if (SUCCEEDED(hRetval))
{
//
// The cache took owership of pNode
//
pNode = NULL;
}
}
}
delete pNode;
}
}
return hRetval;
}
/*++
Name:
TNameResolutionCache::Purge
Description:
Purges the cache. The function locks the cache.
Arguments:
None
Return Value:
S_OK - cache was purged
any other HR - an error occurred
--*/
HRESULT
TNameResolutionCache::
Purge(
VOID
)
{
m_Lock.Enter();
while (!m_NodeList.IsEmpty())
{
TResolutionCacheNode *pItem = m_NodeList.RemoveAtHead();
delete pItem;
}
m_Lock.Leave();
return S_OK;
}
/*++
Name:
TNameResolutionCache::CreateAndAddNode
Description:
Checks if a node with pszName name is already in the cache. If not,
it creates a new node and populates it with data. Then it adds the
node to the list of nodes. pszName cannot be an IP address.
getaddrinfo must succeed on pszName. pszName cannot be an emulated
server name. The function locks the cache.
Arguments:
pszName - name to create a new node for
bClusterNode - specifies if this node corresponds to a cluster ini spooler
Return Value:
S_OK - a new node was created and added to the cache
S_FALSE - a node with pszName name already existed in the cache
any other HR - an error occurred while performing the operation
--*/
HRESULT
TNameResolutionCache::
CreateAndAddNode(
IN LPCWSTR pszName,
IN BOOL bClusterNode
)
{
TStatusH hRetval;
hRetval DBGCHK = pszName ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hRetval))
{
//
// The function will lock the cache for the search
//
hRetval DBGCHK = IsNodeInCache(pszName);
//
// Not found in the cache
//
if (hRetval == S_FALSE)
{
hRetval DBGCHK = IsIPAddress(pszName);
if (hRetval == S_OK)
{
//
// We do not add IPs to our cache
//
hRetval DBGCHK = E_FAIL;
}
else
{
LPSTR pszAnsiName;
hRetval DBGCHK = UnicodeToAnsiWithAlloc(pszName, &pszAnsiName);
if (SUCCEEDED(hRetval))
{
TResolutionCacheNode *pNode = new TResolutionCacheNode(pszName, bClusterNode);
hRetval DBGCHK = pNode ? pNode->Valid() : E_OUTOFMEMORY;
if (SUCCEEDED(hRetval))
{
hRetval DBGCHK = ResolveNameToAddress(pszAnsiName, pNode->m_pIPAddresses, pNode->m_pAlternateNames);
if (SUCCEEDED(hRetval))
{
TList<TStringNode> AddressesUsedByCluster;
//
// Check if the local machine has any IPs used by the cluster service
//
hRetval DBGCHK = GetClusterIPAddresses(&AddressesUsedByCluster);
if (SUCCEEDED(hRetval))
{
hRetval DBGCHK = ExcludeIPsUsedByCluster(&AddressesUsedByCluster, pNode->m_pIPAddresses);
}
}
else if (SCODE_CODE(hRetval) == WSAHOST_NOT_FOUND)
{
//
// There are situations when the name of the local machine cannot be resolved
// to an IP. This can happen for example when networking is partly initialized
// In this case we want the spooler to function. The only thing is that it
// won't keep an IP address associated with the node corresponding to the name
// of the machine. Basically this is the node created by
// SplCreateSpooler(\\local-machine-name). If the machine gets an IP address
// later, then we will be notified and we can refresh the node.
//
if (!_wcsicmp(pszName, szMachineName + 2))
{
hRetval DBGNOCHK = S_OK;
}
}
if (SUCCEEDED(hRetval))
{
NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
//
// Make sure a node with pszName was not added to the cache while
// we were calling the TResolutionCacheNode CTOR
//
hRetval DBGCHK = IsNodeInCache(pszName);
//
// Node not found
//
if (hRetval == S_FALSE)
{
if (bClusterNode)
{
hRetval DBGCHK = m_NodeList.AddAtHead(pNode);
}
else
{
//
// A local node cache is always added at the tail of the list, so
// it is searched after cluster nodes.
//
hRetval DBGCHK = m_NodeList.AddAtTail(pNode);
}
if (SUCCEEDED(hRetval))
{
//
// The cache took owership of pNode
//
pNode = NULL;
}
}
}
}
delete pNode;
delete [] pszAnsiName;
}
}
}
}
return hRetval;
}
/*++
Name:
TNameResolutionCache::ResolveNameToAddress
Description:
Arguments:
Return Value:
--*/
HRESULT
TNameResolutionCache::
ResolveNameToAddress(
IN LPCSTR pszName,
IN TList<TStringNode> *pAddresses,
IN TList<TStringNode> *pNames
)
{
TStatusH hRetval;
hRetval DBGNOCHK = E_INVALIDARG;
if (pszName && *pszName && pAddresses && pNames)
{
hRetval DBGCHK = m_FailedCache.IsStringInCache(pszName);
if (hRetval == S_FALSE)
{
hRetval DBGCHK = GetAddrInfo(pszName, pAddresses, pNames);
if (SCODE_CODE(hRetval) == WSAHOST_NOT_FOUND)
{
//
// We intentionally ignore the error returned by this function
// because we do not want to overwrite hRetval. Out callers need
// the exact error code returned by GetAddrInfo
//
m_FailedCache.AddString(pszName);
}
}
else
{
hRetval DBGNOCHK = HRESULT_FROM_WIN32(WSAHOST_NOT_FOUND);
}
}
return hRetval;
}
/*++
Name:
TNameResolutionCache::RefreshNode
Description:
Checks if a node with pszName name is already in the cache. If not,
it creates a new node and populates it with data. Then it adds the
node to the list of nodes. pszName cannot be an IP address.
getaddrinfo must succeed on pszName. pszName cannot be an emulated
server name. The function locks the cache.
Arguments:
pszName - name to create a new node for
bClusterNode - specifies if this node corresponds to a cluster ini spooler
Return Value:
S_OK - a new node was created and added to the cache
S_FALSE - a node with pszName name already existed in the cache
any other HR - an error occurred while performing the operation
--*/
HRESULT
TNameResolutionCache::
RefreshNode(
IN LPCWSTR pszName
)
{
TStatusH hRetval;
hRetval DBGCHK = pszName ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hRetval))
{
//
// Find the node matching pszName
//
hRetval DBGCHK = IsNodeInCache(pszName);
//
// We found the node that we need to refresh
//
if (hRetval == S_OK)
{
LPSTR pszAnsiName;
hRetval DBGCHK = UnicodeToAnsiWithAlloc(pszName, &pszAnsiName);
if (SUCCEEDED(hRetval))
{
TList<TStringNode> AddressesUsedByCluster;
TList<TStringNode> *pNewAddresses = new TList<TStringNode>;
TList<TStringNode> *pNewNames = new TList<TStringNode>;
hRetval DBGCHK = pNewAddresses && pNewNames ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hRetval))
{
//
// Get new IPs and names
//
hRetval DBGCHK = ResolveNameToAddress(pszAnsiName, pNewAddresses, pNewNames);
if (SUCCEEDED(hRetval))
{
//
// Check if the local machine has any IPs coming from the cluster service
//
hRetval DBGCHK = GetClusterIPAddresses(&AddressesUsedByCluster);
if (SUCCEEDED(hRetval))
{
hRetval DBGCHK = ExcludeIPsUsedByCluster(&AddressesUsedByCluster, pNewAddresses);
}
}
//
// Replace current IP and name list for the node with the new IPs and names
//
if (SUCCEEDED(hRetval))
{
TResolutionCacheNode *pCacheNode = NULL;
TList<TStringNode> *pOldIPAddresses = NULL;
TList<TStringNode> *pOldNames = NULL;
m_Lock.Enter();
//
// Find the node macthing pszName. We need to make sure our node was not deleted while we
// were out of the crit sec
//
TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
hRetval DBGNOCHK = S_FALSE;
for (It.First(); !It.IsDone(); It.Next())
{
hRetval DBGNOCHK = It.Current()->IsSameName(pszName);
if (hRetval == S_OK)
{
pCacheNode = It.Current();
break;
}
}
if (hRetval == S_OK)
{
pOldIPAddresses = pCacheNode->m_pIPAddresses;
pOldNames = pCacheNode->m_pAlternateNames;
pCacheNode->m_pIPAddresses = pNewAddresses;
pCacheNode->m_pAlternateNames = pNewNames;
//
// The node took ownership of the lists
//
pNewAddresses = NULL;
pNewNames = NULL;
}
m_Lock.Leave();
delete pOldIPAddresses;
delete pOldNames;
}
}
delete pNewAddresses;
delete pNewNames;
}
delete [] pszAnsiName;
}
}
return hRetval;
}
/*++
Name:
TNameResolutionCache::AddName
Description:
Adds a name to the cache. We try adding a name to the cache
by fining out to which node it belongs. The membership is determined
based on the IP(s) associated with the server name. If getaddrinfo
fails on pszName, when we look if the name is an alias to the local
machine added with NetServerComputerNameAdd.
Arguments:
pszName - name to add to the cache
Return Value:
S_OK - name was present in the cache or was added
E_FAIL - the name is an IP and was not added to the cache
other HRESULT - an error occurred while performing the operation
--*/
HRESULT
TNameResolutionCache::
AddName(
IN LPCWSTR pszName
)
{
TStatusH hRetval;
hRetval DBGCHK = FindNameInCacheAndAgeOut(pszName);
//
// Not found in the cache
//
if (hRetval == S_FALSE)
{
hRetval DBGCHK = IsIPAddress(pszName);
if (hRetval == S_OK)
{
//
// We do not add IPs to our cache
//
hRetval DBGCHK = E_FAIL;
}
else
{
LPSTR pszAnsiName;
hRetval DBGCHK = UnicodeToAnsiWithAlloc(pszName, &pszAnsiName);
if (SUCCEEDED(hRetval))
{
//
// Get IP and alternates names lists
//
TList<TStringNode> IPAddresses;
TList<TStringNode> AlternateNames;
hRetval DBGCHK = ResolveNameToAddress(pszAnsiName, &IPAddresses, &AlternateNames);
if (hRetval == S_OK)
{
//
// We found IP(s) for pszName
//
hRetval DBGCHK = AddNameUsingIPList(pszName, IPAddresses);
}
else if (SCODE_CODE(hRetval) == WSAHOST_NOT_FOUND)
{
//
// Check if the name is an emulated server name
//
TString AliasedServer;
hRetval DBGCHK = GetNetBiosInfo(pszAnsiName, &AliasedServer);
if (hRetval == S_OK)
{
hRetval DBGCHK = AddNetBiosName(AliasedServer, pszName);
}
}
delete [] pszAnsiName;
}
}
}
return hRetval;
}
/*++
Name:
TNameResolutionCache::AddNetBiosName
Description:
Searches in the cache the node called pszNode. Then it adds pszAlias to
the list of supported names. Locks the cache.
Arguments:
pszNode - name of node where to add pszAlias
pszAlias - name to be added to the Alternate names list
Return Value:
None
--*/
HRESULT
TNameResolutionCache::
AddNetBiosName(
IN LPCWSTR pszNode,
IN LPCWSTR pszAlias
)
{
TStatusH hRetval;
TStringNode *pStringNode = new TStringNode(pszAlias);
hRetval DBGCHK = pStringNode ? pStringNode->Valid() : E_OUTOFMEMORY;
if (SUCCEEDED(hRetval))
{
NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
hRetval DBGNOCHK = S_FALSE;
for (It.First(); !It.IsDone(); It.Next())
{
if (It.Current()->IsSameName(pszNode) == S_OK)
{
hRetval DBGNOCHK = It.Current()->IsNameInNodeCache(pszAlias);
//
// We didn't find the alias in the cache
//
if (hRetval == S_FALSE)
{
hRetval DBGCHK = It.Current()->m_pAlternateNames->AddAtTail(pStringNode);
if (SUCCEEDED(hRetval))
{
//
// The cache took ownership of the node.
//
pStringNode = NULL;
}
}
break;
}
}
}
delete pStringNode;
return hRetval;
}
/*++
Name:
TNameResolutionCache::AddNameUsingIPList
Description:
Searches in the cache the node called pszNode. Then it adds pszAdditionalName to
the list of supported names. Locks the cache.
Arguments:
pszNode - name of node where to add pszAlias
pszAlias - name to be added to the Alternate names list
Return Value:
None
--*/
HRESULT
TNameResolutionCache::
AddNameUsingIPList(
IN LPCWSTR pszAdditionalName,
IN TList<TStringNode>& pIPAddresses
)
{
TStatusH hRetval;
TStringNode *pStringNode = new TStringNode(pszAdditionalName);
hRetval DBGCHK = pStringNode ? pStringNode->Valid() : E_OUTOFMEMORY;
if (SUCCEEDED(hRetval))
{
NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
TList<TResolutionCacheNode>::TIterator ItCache(&m_NodeList);
//
// Assume success
//
hRetval DBGNOCHK = S_OK;
//
// Iterate through the list of nodes
//
for (ItCache.First(); !ItCache.IsDone(); ItCache.Next())
{
//
// For each node in the cache we need to see if all the IPs
// for the name to be added match the IPs of the node
//
TList<TStringNode>::TIterator ItAddresses(&pIPAddresses);
for (ItAddresses.First(); !ItAddresses.IsDone(); ItAddresses.Next())
{
hRetval DBGCHK = ItCache.Current()->IsNameInNodeCache(ItAddresses.Current()->m_String);
//
// For clusters all IPs need to be found in the node's list of IPs
// For local nodes, one IP must be found in the node's list f IPs
//
if (hRetval != S_OK && ItCache.Current()->m_bClusterNode)
{
break;
}
}
if (hRetval == S_OK)
{
TStringNode *pExistingNode = NULL;
//
// Our IPs match the IPs of the current node. Now we need to make sure a node
// with the name pszAdditionalName was not added while we were out of the
// critical section
//
hRetval DBGCHK = ItCache.Current()->GetStringNodeFromName(pszAdditionalName, &pExistingNode);
if (hRetval == S_OK)
{
//
// We do not remove the node in this case, we just update the time.
//
pExistingNode->m_Time = GetTickCount();
}
else if (hRetval == S_FALSE)
{
hRetval DBGCHK = ItCache.Current()->m_pAlternateNames->AddAtTail(pStringNode);
if (SUCCEEDED(hRetval))
{
//
// The cache took ownership of the node.
//
pStringNode = NULL;
}
}
break;
}
}
}
delete pStringNode;
return hRetval;
}
/*++
Name:
TStringNode::TStringNode
Description:
Constructor
Arguments:
pszString - name of the node
Return Value:
None
--*/
TStringNode::
TStringNode(
IN LPCWSTR pszString
)
{
m_hr = m_String.Update(pszString);
m_Time = GetTickCount();
}
/*++
Name:
TStringNode::~TStringNode
Description:
Destructor
Arguments:
None
Return Value:
None
--*/
TStringNode::
~TStringNode(
VOID
)
{
}
/*++
Name:
TStringNode::Valid
Description:
Valid
Arguments:
None
Return Value:
S_OK - the object is valid
any other HR - the object is invalid
--*/
HRESULT
TStringNode::
Valid(
VOID
) const
{
return m_hr;
}
/*++
Name:
TStringNode::RefreshTimeStamp
Description:
Refreshes the time stamp stored in the node
Arguments:
None
Return Value:
None
--*/
VOID
TStringNode::
RefreshTimeStamp(
VOID
)
{
m_Time = GetTickCount();
}
/*++
Name:
TResolutionCacheNode::TResolutionCacheNode
Description:
CTOR TResolutionCacheNode. The function tries to gather information
about the server pszName by calling getaddrinfo.
Arguments:
None
Return Value:
None
--*/
TResolutionCacheNode::
TResolutionCacheNode(
IN LPCWSTR pszName,
IN BOOL bClusterNode
) : m_bClusterNode(bClusterNode)
{
m_hr = E_INVALIDARG;
if (pszName && *pszName)
{
m_hr = m_Name.Update(pszName);
if (SUCCEEDED(m_hr))
{
m_pIPAddresses = new NCoreLibrary::TList<TStringNode>;
m_pAlternateNames = new NCoreLibrary::TList<TStringNode>;
m_hr = m_pIPAddresses && m_pAlternateNames ? S_OK : E_OUTOFMEMORY;
}
}
}
/*++
Name:
TResolutionCacheNode::~TResolutionCacheNode
Description:
TResolutionCacheNode
Arguments:
None
Return Value:
None
--*/
TResolutionCacheNode::
~TResolutionCacheNode(
VOID
)
{
delete m_pIPAddresses;
delete m_pAlternateNames;
}
/*++
Name:
TResolutionCacheNode::Valid
Description:
Return m_hr.
Arguments:
None
Return Value:
S_OK - object is valid
any other HR - object is not valid
--*/
HRESULT
TResolutionCacheNode::
Valid(
VOID
) const
{
return m_hr;
}
/*++
Name:
TResolutionCacheNode::IsSameName
Description:
Checks if pszName matches the name of the node
Arguments:
pszName - name to look for in the node name
Return Value:
S_OK - same names
S_FALSE - not same names
--*/
HRESULT
TResolutionCacheNode::
IsSameName(
IN LPCWSTR pszName
)
{
HRESULT hr = S_FALSE;
if (pszName && *pszName && !_wcsicmp(pszName, m_Name))
{
hr = S_OK;
}
return hr;
}
/*++
Name:
TResolutionCacheNode::IsNameInNodeCache
Description:
Checks if pszName is the IP or in the AlternateNames list for this node
Arguments:
pszName - name to look for
Return Value:
S_OK - pszName found
S_FALSE - pszName not found
any other HR - an error occurred
--*/
HRESULT
TResolutionCacheNode::
IsNameInNodeCache(
IN LPCWSTR pszName
)
{
TStatusH hRetval;
hRetval DBGCHK = IsSameName(pszName);
if (hRetval == S_FALSE)
{
TList<TStringNode>::TIterator It(m_pIPAddresses);
for (It.First(); !It.IsDone(); It.Next())
{
if (!_wcsicmp(pszName, (LPCWSTR)It.Current()->m_String))
{
hRetval DBGCHK = S_OK;
break;
}
}
}
if (hRetval == S_FALSE)
{
//
// This list can age out
//
TList<TStringNode>::TIterator It(m_pAlternateNames);
for (It.First(); !It.IsDone(); It.Next())
{
if (!_wcsicmp(pszName, (LPCWSTR)It.Current()->m_String))
{
hRetval DBGCHK = S_OK;
break;
}
}
}
return hRetval;
}
/*++
Name:
TResolutionCacheNode::GetStringNodeFromName
Description:
Checks if pszName is the the AlternateNames list for this node.
Note: IsNameInNodeCache looks for both AlternateNames and IPs.
Returns the string node that contains the pszName
Arguments:
pszName - name to look for
pStringNode - pointer to string node containing the pszName
Return Value:
S_OK - pszName found
S_FALSE - pszName not found
any other HR - an error occurred
--*/
HRESULT
TResolutionCacheNode::
GetStringNodeFromName(
IN LPCWSTR pszName,
OUT TStringNode **pStringNode
)
{
TStatusH hRetval;
hRetval DBGCHK = pStringNode && pszName ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hRetval))
{
//
// StringNode not found
//
hRetval DBGCHK = S_FALSE;
//
// This list can age out
//
TList<TStringNode>::TIterator It(m_pAlternateNames);
for (It.First(); !It.IsDone(); It.Next())
{
if (!_wcsicmp(pszName, (LPCWSTR)It.Current()->m_String))
{
hRetval DBGCHK = S_OK;
*pStringNode = It.Current();
break;
}
}
}
return hRetval;
}
/*++
Name:
TFastCache::TFastCache
Description:
Constructs the fast cache object
Arguments:
TimeToLive - time to live of an element in the fast cache
CacheSize - number of elements in the cache
MaxStrLen - maximum length of strings stored in the cache
Return Value:
None. Call IsValid before using a fast cache object
--*/
TFastCache::
TFastCache(
IN DWORD TimeToLive,
IN DWORD CacheSize,
IN DWORD MaxStrLen
) : m_ArraySize(CacheSize),
m_TTL(TimeToLive),
m_MaxStrLen(MaxStrLen),
m_CurrentCount(0)
{
m_pArray = new TFastCacheElement[m_ArraySize];
m_hr = m_pArray ? m_Lock.IsValid() : E_OUTOFMEMORY;
}
/*++
Name:
TFastCache::~TFastCache
Description:
Destroys the fast cache object
Arguments:
None.
Return Value:
None.
--*/
TFastCache::
~TFastCache(
VOID
)
{
delete [] m_pArray;
}
/*++
Name:
TFastCache::IsValid
Description:
Checks if the object was constructed correctly
Arguments:
None.
Return Value:
S_OK - fast cache object can be used
other HRESULT - do not use the cache
--*/
HRESULT
TFastCache::
IsValid(
VOID
)
{
return m_hr;
}
/*++
Name:
TFastCache::IsStringInCache
Description:
Verifies if a string is in the cache. Takes into account the TTL.
Arguments:
pszString - string to look for. Note that the cache does not
accept NULL or "" as valid strings.
Return Value:
S_OK - pszString is in the cache
S_FALSE - pszString is not in the cache (or TTL expired)
--*/
HRESULT
TFastCache::
IsStringInCache(
IN PCSTR pszString
)
{
HRESULT hRet = S_FALSE;
if (pszString && *pszString)
{
NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
DWORD CurrentTick = GetTickCount();
for (DWORD i = 0; i < m_CurrentCount; i++)
{
if (m_pArray[i].m_pszString &&
CurrentTick - m_pArray[i].m_TimeStamp < m_TTL &&
!_stricmp(pszString, m_pArray[i].m_pszString))
{
hRet = S_OK;
break;
}
}
}
return hRet;
}
/*++
Name:
TFastCache::AddString
Description:
Adds a string to the cache. Algorithm is explained below.
scan the array
if we find an emptry spot, put the string there and exit
else if the string==string in array
update timestamp and exit
else if TTL os string in array is expired replace string in array and exit
else check how old the timestamp is
end scan
if we didn't place the string so far, replace the oldest element in the array
Note that the function exits as soon as pszString is placed. so the function does not
eliminate expired elemets after pszString is placed in the right spot.
Arguments:
pszString - string to add to the cache.
Return Value:
S_OK - string was added to the cache
other HRESULT - string was not added to the cache
--*/
HRESULT
TFastCache::
AddString(
IN PCSTR pszString
)
{
HRESULT hRet = E_INVALIDARG;
if (pszString && *pszString && strlen(pszString) <= m_MaxStrLen)
{
NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
DWORD CurrentTick = GetTickCount();
DWORD OldestElemIndex = 0;
DWORD OldestAge = 0;
hRet = S_FALSE;
for (DWORD i = 0; i < m_CurrentCount; i++)
{
if (m_pArray[i].m_pszString)
{
if (!_stricmp(pszString, m_pArray[i].m_pszString))
{
m_pArray[i].m_TimeStamp = GetTickCount();
hRet = S_OK;
break;
}
else if (CurrentTick - m_pArray[i].m_TimeStamp > m_TTL)
{
hRet = m_pArray[i].UpdateElement(pszString);
break;
}
else if (CurrentTick - m_pArray[i].m_TimeStamp > OldestAge)
{
OldestAge = CurrentTick - m_pArray[i].m_TimeStamp;
OldestElemIndex = i;
}
}
else
{
hRet = m_pArray[i].UpdateElement(pszString);
break;
}
}
if (hRet == S_FALSE)
{
if (m_CurrentCount < m_ArraySize)
{
hRet = m_pArray[m_CurrentCount].UpdateElement(pszString);
if (SUCCEEDED(hRet))
{
//
// Note that we never decrease the value of m_CurrentCount.
// For performance reasons, we do not have a function that
// checks the timestamps of the elements in the cache and then
// eliminates aged elements. Aged elements are always replaced,
// but never eliminated. Generally, we won't have more than
// a couple of elements in the fast cache, so m_CurrentCount
// does help. If you call OpenPrinter/EnumPrinters/etc at the same
// time (meaning all calls within a minute or whatever the TTL is)
// on m_ArraySize number of server names which cannot be resolved,
// then the m_CurrentCount would be equal to m_ArraySize and would
// never drop. This scenario is very rare.
//
m_CurrentCount++;
}
}
else
{
hRet = m_pArray[OldestElemIndex].UpdateElement(pszString);
}
}
}
return hRet;
}
/*++
Name:
TFastCacheElement::TFastCacheElement
Description:
CTOR. Initilizes members of element
Arguments:
None.
Return Value:
None.
--*/
TFastCacheElement::
TFastCacheElement(
VOID
) : m_pszString(NULL),
m_TimeStamp(0)
{
}
/*++
Name:
TFastCacheElement::TFastCacheElement
Description:
DTOR. Frees the string held by element
Arguments:
None.
Return Value:
None.
--*/
TFastCacheElement::
~TFastCacheElement(
VOID
)
{
delete [] m_pszString;
}
/*++
Name:
TFastCacheElement::UpdateElement
Description:
Helper function.
Arguments:
pszString - string to store in TFastCacheElement
Return Value:
S_OK - pszString was stored in *pElement
other HRESULT - an error occurred
--*/
HRESULT
TFastCacheElement::
UpdateElement(
IN PCSTR pszString
)
{
HRESULT hRet = S_OK;
delete [] m_pszString;
m_pszString = new CHAR[strlen(pszString) + 1];
if (m_pszString)
{
StringCchCopyA(m_pszString, strlen(pszString) + 1, pszString);
m_TimeStamp = GetTickCount();
}
else
{
hRet = E_OUTOFMEMORY;
}
return hRet;
}