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

  1. /*++
  2. Copyright (C) 2001 Microsoft Corporation
  3. All rights reserved.
  4. Module Name:
  5. NCnamecache.cxx
  6. Abstract:
  7. Implemntation of the name relosution cache classes and functions.
  8. Author:
  9. Felix Maxa (AMaxa) 16 May 2001 - Created
  10. Felix Maxa (AMaxa) 30 Sept 2001 - Added the cache for failures
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #include <winsock2.h>
  15. #include <Ws2tcpip.h>
  16. #include <lm.h>
  17. #include "ncnamecache.hxx"
  18. #include "ncsockets.hxx"
  19. #include "ncclusfunc.hxx"
  20. using namespace NCoreLibrary;
  21. /*++
  22. Explanation of how the cache works.
  23. The basic idea is that every local or cluster pIniSpooler has an associated node in the cache. Each node keeps
  24. a list of IPs that are handled by the node name, and a list of alternate names (FQDN, aliases etc.). When a
  25. node is created only the IP lists are popoluated. The alternate names are popoluated dynalically, when a request
  26. to add a name to the cache comes in.
  27. Example of cache. List of nodes after the pIniSpooler for the local machine is created.
  28. AMAXA-NODE1 (this is the node name)
  29. IP Addresses: 172.31.173.91 <--- populated by the cache with the result of getaddrinfo("amaxa-node1")
  30. Alternate names: Empty
  31. Nodes for cluster pIniSpooler are always added in the head of the list, so they are searched first. We search
  32. 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
  33. the following:
  34. If getaddrinfo(NewName) succeeds, then we get a list of IPs, NewNameIPs. We start searching if the NewNameIPs
  35. are a subset of the IP addresses of nodes in the cache. For cluster nodes, every IP in NewNameIPs must be found
  36. in the set of IP addresses for the node. For local nodes, one single IP in NewNameIPs must be found in IP Addresses.
  37. If getaddrinfo(NewName) fails, then we check if the server name is in the list of transports enumerated by
  38. NetServerTransportEnum. If positive, then it is an emulated server and we add it to the list of alternate names
  39. for the node representing the local machine (AMAXA_NODE1 in the example)
  40. Example of node list with cluster node.
  41. AMAXA-CLS1 (this is the node name)
  42. IP Addresses: 172.31.172.31
  43. 172.31.172.32
  44. Alternate names: AMAXA-CLS1.NTDEV.MICROSOFT.COM
  45. AMAXA-NODE1 (this is the node name)
  46. IP Addresses: 172.31.173.91
  47. Alternate names:
  48. Note that a spooler cluster resource can be dependent on more than one IP address. That is why when cluster nodes
  49. are created one needs to specify whet IP addresses it recognizes.
  50. Let's say we want to add "AMAXA-NODE1.NTDEV.MICROSOFT.COM" to the cache. If the cluster IP resources are online,
  51. then getaddrinfo("amaxxa-node1.ntdev.microsoft.com") returns 172.31.173.91 (IP gotten at boot) and 172.31.172.31,
  52. 172.31.172.32, IPs registered by the cluster. Based on the search algorithm above, the result will be:
  53. AMAXA-CLS1 (this is the node name)
  54. IP Addresses: 172.31.172.31
  55. 172.31.172.32
  56. Alternate names: AMAXA-CLS1.NTDEV.MICROSOFT.COM
  57. AMAXA-NODE1 (this is the node name)
  58. IP Addresses: 172.31.173.91
  59. Alternate names: AMAXA-NODE1.NTDEV.MICROSOFT.COM
  60. Special cases when the cluster service is running on a machine.
  61. Scenario 1)
  62. a) spooler is stopped
  63. b) cluster is running and resitered the IPs: 172.31.172.31, 172.31.172.32
  64. c) spooler starts
  65. d) the cache initializes and a node for the local machine is added
  66. e) getaddrinfo(local machine) returns also the cluster IPs
  67. f) local node has the list: 172.31.172.31, 172.31.172.32, 172.31.173.91
  68. g) before the cluster ini spooler is created a call to OpenPrinter("\\AMAXA-CLS1") comes in
  69. h) getaddrinfo("AMAXA-CLS1") resolves to 172.31.172.31
  70. 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
  71. 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,
  72. the list of IP addresses is set to
  73. Set of IPs returned by getaddrinfo() - Set of IPs belonging to cluster resources.
  74. A PnP event is triggered when the local machine gets a new IP either via ipconfig or because a cluster IP resource comes
  75. online or goes offline. We cannot distinguish if the event was triggered by cluster activity or by ipconfig. So when
  76. 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.
  77. That way the list of IPs is repopoluated.
  78. The cluster nodes do not need refreshing. A spooler resource needs to be taken offline in order to make changes in its dependent
  79. resources. In that case the cluster node is removed from the cache.
  80. The failed cache.
  81. Once the name cache was checked in we ran into bug 470345 "CSNW:Printing operations painfully slow". We had a printer connection
  82. to a NetWare server. getaddrinfo(NetWareServerName) failed and took about 2.3 seconds to complete. This meant a huge performance
  83. loss when printing to the NetWare server, because for each single OpenPrinter the spooler took at least 2 seconds before the
  84. 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
  85. 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.
  86. --*/
  87. /*++
  88. Name:
  89. TNameResolutionCache::TNameResolutionCache
  90. Description:
  91. CTOR for TNameResolutionCache
  92. Arguments:
  93. None
  94. Return Value:
  95. None
  96. --*/
  97. TNameResolutionCache::
  98. TNameResolutionCache(
  99. IN DWORD AgingTime
  100. ) : m_AgingTime(AgingTime)
  101. {
  102. }
  103. /*++
  104. Name:
  105. TNameResolutionCache::~TNameResolutionCache
  106. Description:
  107. DTOR for TNameResolutionCache
  108. Arguments:
  109. None
  110. Return Value:
  111. None
  112. --*/
  113. TNameResolutionCache::
  114. ~TNameResolutionCache(
  115. VOID
  116. )
  117. {
  118. }
  119. /*++
  120. Name:
  121. TNameResolutionCache::IsValid
  122. Description:
  123. Returns S_OK if the TNameResolutionCache object was intialized
  124. properly, any other HRESULT if initialization failed.
  125. Arguments:
  126. None
  127. Return Value:
  128. HRESULT
  129. --*/
  130. HRESULT
  131. TNameResolutionCache::
  132. IsValid(
  133. VOID
  134. )
  135. {
  136. TStatusH hRet;
  137. hRet DBGCHK = m_Lock.IsValid();
  138. if (SUCCEEDED(hRet))
  139. {
  140. hRet DBGCHK = m_FailedCache.IsValid();
  141. }
  142. return hRet;
  143. }
  144. /*++
  145. Name:
  146. TNameResolutionCache::GetNetBiosInfo
  147. Description:
  148. Gets the list of emulated sever names for the local machine. Checks if pszName
  149. is in the list of emulated names. If it is, then paliasedServer will contain
  150. the name of the machine the alias refers to (the local machine)
  151. Arguments:
  152. pszName - server alis to search for
  153. pAliasedServer - will receive the server name that pszName refers to
  154. Return Value:
  155. S_OK - pszName is an alias for the local machine
  156. S_FALSE - pszName is not an alias for the local machine
  157. any other HR - an error occurred
  158. --*/
  159. HRESULT
  160. TNameResolutionCache::
  161. GetNetBiosInfo(
  162. IN LPCSTR pszName,
  163. IN TString *pAliasedServer
  164. )
  165. {
  166. TStatusH hRetval;
  167. hRetval DBGNOCHK = E_INVALIDARG;
  168. if (pszName && *pszName && pAliasedServer)
  169. {
  170. PSERVER_TRANSPORT_INFO_0 TransportInfo = NULL;
  171. DWORD EntriesRead;
  172. DWORD EntriesTotal;
  173. NET_API_STATUS Status;
  174. Status = NetServerTransportEnum(NULL,
  175. 0,
  176. reinterpret_cast<PBYTE *>(&TransportInfo),
  177. MAX_PREFERRED_LENGTH,
  178. &EntriesRead,
  179. &EntriesTotal,
  180. NULL);
  181. hRetval DBGCHK = Status == NERR_Success ? S_OK : HRESULT_FROM_WIN32(Status);
  182. if (SUCCEEDED(hRetval))
  183. {
  184. DWORD Index;
  185. DWORD NameLength = strlen(pszName);
  186. //
  187. // pszName not found
  188. //
  189. hRetval DBGNOCHK = S_FALSE;
  190. for (Index = 0; Index < EntriesRead; Index++)
  191. {
  192. if (TransportInfo[Index].svti0_transportaddresslength == NameLength)
  193. {
  194. //
  195. // Check if pszName matches any of the addresses returned by NetServerTransportEnum
  196. //
  197. if (!_strnicmp(pszName,
  198. reinterpret_cast<LPSTR>(TransportInfo[Index].svti0_transportaddress),
  199. NameLength))
  200. {
  201. hRetval DBGCHK = pAliasedServer->Update(szMachineName + 2);
  202. //
  203. // No need to continue looping
  204. //
  205. break;
  206. }
  207. }
  208. }
  209. NetApiBufferFree(TransportInfo);
  210. }
  211. }
  212. return hRetval;
  213. }
  214. /*++
  215. Name:
  216. TNameResolutionCache::GetAddrInfo
  217. Description:
  218. This function calls getaddrinfo on a server name. Then it populates
  219. 2 lists with the results of getaddrinfo. One list contains all
  220. the IP address, the other one all the names associated with the
  221. server name.
  222. Arguments:
  223. pszName - server name. Cannot be prefixed wth "\\".
  224. pIPAddresses - pointer to list where to append the IP addresses
  225. pAlternateNames - pointer to list where to append the names (normally the FQDN)
  226. Return Value:
  227. S_OK - getaddrinfo succeeded and the lists were populated
  228. other HRESULT - an error occurred
  229. --*/
  230. HRESULT
  231. TNameResolutionCache::
  232. GetAddrInfo(
  233. IN LPCSTR pszName,
  234. IN NCoreLibrary::TList<TStringNode> *pIPAddresses,
  235. IN NCoreLibrary::TList<TStringNode> *pAlternateNames
  236. )
  237. {
  238. TStatusH hRetval;
  239. hRetval DBGNOCHK = E_INVALIDARG;
  240. if (pszName && *pszName && pIPAddresses && pAlternateNames)
  241. {
  242. TWinsockStart WsaStart;
  243. hRetval DBGCHK = WsaStart.Valid();
  244. if (SUCCEEDED(hRetval))
  245. {
  246. DWORD Error;
  247. ADDRINFO *pAddrInfo;
  248. ADDRINFO Hint = {0};
  249. Hint.ai_flags = AI_CANONNAME;
  250. Hint.ai_family = PF_INET;
  251. Error = getaddrinfo(pszName, NULL, &Hint, &pAddrInfo);
  252. hRetval DBGCHK = Error == ERROR_SUCCESS ? S_OK : GetWSAErrorAsHResult();
  253. if (SUCCEEDED(hRetval))
  254. {
  255. ADDRINFO *pTemp;
  256. LPWSTR pszBuffer = NULL;
  257. DWORD cchBuffer = kBufferAllocHint;
  258. DWORD cchNeeded = cchBuffer;
  259. INT WsaError;
  260. pszBuffer = new WCHAR[cchBuffer];
  261. hRetval DBGCHK = pszBuffer ? S_OK : E_OUTOFMEMORY;
  262. if (SUCCEEDED(hRetval))
  263. {
  264. for (pTemp = pAddrInfo; pTemp; pTemp = pTemp->ai_next)
  265. {
  266. WsaError = WSAAddressToString(pTemp->ai_addr,
  267. pTemp->ai_addrlen,
  268. NULL,
  269. pszBuffer,
  270. &cchNeeded);
  271. if (WsaError == SOCKET_ERROR)
  272. {
  273. hRetval DBGCHK = GetWSAErrorAsHResult();
  274. if (SCODE_CODE(hRetval) == WSAEFAULT)
  275. {
  276. delete [] pszBuffer;
  277. cchBuffer = cchNeeded;
  278. pszBuffer = new WCHAR[cchBuffer];
  279. hRetval DBGCHK = pszBuffer ? S_OK : E_OUTOFMEMORY;
  280. if (SUCCEEDED(hRetval))
  281. {
  282. WsaError = WSAAddressToString(pTemp->ai_addr,
  283. pTemp->ai_addrlen,
  284. NULL,
  285. pszBuffer,
  286. &cchNeeded);
  287. hRetval DBGCHK = !WsaError ? S_OK : GetWSAErrorAsHResult();
  288. }
  289. else
  290. {
  291. //
  292. // Fatal error. Mem alloc failed.
  293. //
  294. break;
  295. }
  296. }
  297. }
  298. if (SUCCEEDED(hRetval))
  299. {
  300. TStringNode *pItem = new TStringNode(pszBuffer);
  301. hRetval DBGCHK = pItem ? pItem->Valid() : E_OUTOFMEMORY;
  302. if (SUCCEEDED(hRetval))
  303. {
  304. hRetval DBGCHK = pIPAddresses->AddAtHead(pItem);
  305. }
  306. }
  307. if (pTemp->ai_canonname)
  308. {
  309. LPWSTR pszUnicode = NULL;
  310. hRetval DBGCHK = AnsiToUnicodeWithAlloc(pTemp->ai_canonname, &pszUnicode);
  311. if (SUCCEEDED(hRetval))
  312. {
  313. TStringNode *pItem = new TStringNode(pszUnicode);
  314. hRetval DBGCHK = pItem ? pItem->Valid() : E_OUTOFMEMORY;
  315. if (SUCCEEDED(hRetval))
  316. {
  317. hRetval DBGCHK = pAlternateNames->AddAtHead(pItem);
  318. }
  319. delete [] pszUnicode;
  320. }
  321. }
  322. } // for loop
  323. delete [] pszBuffer;
  324. }
  325. freeaddrinfo(pAddrInfo);
  326. }
  327. }
  328. }
  329. return hRetval;
  330. }
  331. /*++
  332. Name:
  333. TNameResolutionCache::ExcludeIPsUsedByCluster
  334. Description:
  335. Excludes from pList all the names present in pClusterIPs.
  336. Arguments:
  337. pClusterIPs - list of IP addresses used by the cluster resources
  338. pList - list of IP addresses
  339. Return Value:
  340. S_OK - all the elements in pClusterIPs were eliminated from pList
  341. any other HRESULT - an error occurred
  342. --*/
  343. HRESULT
  344. TNameResolutionCache::
  345. ExcludeIPsUsedByCluster(
  346. IN TList<TStringNode> *pClusterIPs,
  347. IN TList<TStringNode> *pList
  348. )
  349. {
  350. TStatusH hRetval;
  351. hRetval DBGCHK = pClusterIPs ? S_OK : E_OUTOFMEMORY;
  352. if (SUCCEEDED(hRetval))
  353. {
  354. TList<TStringNode>::TIterator ItClus(pClusterIPs);
  355. for (ItClus.First(); !ItClus.IsDone(); ItClus.Next())
  356. {
  357. LPCWSTR pszClusString = ItClus.Current()->m_String;
  358. DBGMSG(DBG_TRACE, ("IP used by cluster %ws\n", pszClusString));
  359. TList<TStringNode>::TIterator ItList(pList);
  360. for (ItList.First(); !ItList.IsDone(); ItList.Next())
  361. {
  362. if (!_wcsicmp(ItList.Current()->m_String, pszClusString))
  363. {
  364. TStringNode *pNodeToElim = ItList.Current();
  365. hRetval DBGCHK = pList->Remove(pNodeToElim);
  366. delete pNodeToElim;
  367. break;
  368. }
  369. }
  370. }
  371. }
  372. return hRetval;
  373. }
  374. /*++
  375. Name:
  376. TNameResolutionCache::IsNodeInCache
  377. Description:
  378. Checks if there is a node called pszName in the cache.
  379. This function locks the cache.
  380. Arguments:
  381. pszNode - name to look for in the node list
  382. Return Value:
  383. S_OK - a node with name "pszName" is present in the list
  384. S_FALSE - no node with name "pszName" is present
  385. other HR - an error occrred while searching for pszName node
  386. --*/
  387. HRESULT
  388. TNameResolutionCache::
  389. IsNodeInCache(
  390. IN LPCWSTR pszNode
  391. )
  392. {
  393. TStatusH hRetval;
  394. hRetval DBGCHK = pszNode ? S_OK : E_INVALIDARG;
  395. if (SUCCEEDED(hRetval))
  396. {
  397. NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
  398. TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
  399. hRetval DBGNOCHK = S_FALSE;
  400. for (It.First(); !It.IsDone(); It.Next())
  401. {
  402. hRetval DBGNOCHK = It.Current()->IsSameName(pszNode);
  403. if (hRetval == S_OK)
  404. {
  405. break;
  406. }
  407. }
  408. }
  409. return hRetval;
  410. }
  411. /*++
  412. Name:
  413. TNameResolutionCache::DeleteNode
  414. Description:
  415. Deletes a node from the cache. The function locks the cache
  416. Arguments:
  417. pszNode - node to delete
  418. Return Value:
  419. S_OK - node was deleted
  420. S_FALSE - node was not found
  421. other HR - an error occrred while performing operation
  422. --*/
  423. HRESULT
  424. TNameResolutionCache::
  425. DeleteNode(
  426. IN LPCWSTR pszNode
  427. )
  428. {
  429. TStatusH hRetval;
  430. hRetval DBGCHK = pszNode && *pszNode ? S_OK : E_INVALIDARG;
  431. if (SUCCEEDED(hRetval))
  432. {
  433. TResolutionCacheNode *pNode = NULL;
  434. m_Lock.Enter();
  435. TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
  436. //
  437. // We have not found a node containing pszName
  438. //
  439. hRetval DBGNOCHK = S_FALSE;
  440. for (It.First(); !It.IsDone(); It.Next())
  441. {
  442. //
  443. // Check if pszName is the name of the node
  444. //
  445. if (!_wcsicmp(It.Current()->m_Name, pszNode))
  446. {
  447. hRetval DBGCHK = S_OK;
  448. pNode = It.Current();
  449. if (pNode)
  450. {
  451. hRetval DBGCHK = m_NodeList.Remove(pNode);
  452. }
  453. break;
  454. }
  455. }
  456. m_Lock.Leave();
  457. delete pNode;
  458. }
  459. return hRetval;
  460. }
  461. /*++
  462. Name:
  463. TNameResolutionCache::IsNameInNodeCache
  464. Description:
  465. Looks for pszName in the list of IPs and names associated with the
  466. node named pszNode. The function locks the cache
  467. Arguments:
  468. pszNode - node where to search for pszName
  469. pszName - name to search for in a node
  470. Return Value:
  471. S_OK - a pszName is present in the list of names associated to pszNode
  472. S_FALSE - a pszName is NOT present in the list of names associated to pszNode
  473. other HR - an error occrred while searching for pszName node
  474. --*/
  475. HRESULT
  476. TNameResolutionCache::
  477. IsNameInNodeCache(
  478. IN LPCWSTR pszNode,
  479. IN LPCWSTR pszName
  480. )
  481. {
  482. TStatusH hRetval;
  483. hRetval DBGCHK = pszNode && pszName ? S_OK : E_INVALIDARG;
  484. if (SUCCEEDED(hRetval))
  485. {
  486. NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
  487. TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
  488. hRetval DBGNOCHK = S_FALSE;
  489. for (It.First(); !It.IsDone(); It.Next())
  490. {
  491. //
  492. // Check of pszNode matches the name of the node pointer by the iterator
  493. //
  494. if (It.Current()->IsSameName(pszNode) == S_OK)
  495. {
  496. hRetval DBGCHK = It.Current()->IsNameInNodeCache(pszName);
  497. break;
  498. }
  499. }
  500. }
  501. return hRetval;;
  502. }
  503. /*++
  504. Name:
  505. TNameResolutionCache::IsNameInCache
  506. Description:
  507. Checks is pszName is in the list of IPs and names associated with a
  508. node in the cache. The function locks the cache.
  509. Arguments:
  510. pszName - name to search for in the cache
  511. Return Value:
  512. S_OK - a pszName is present in the cache
  513. S_FALSE - a pszName is NOT present in the cache
  514. other HR - an error occrred while searching for pszName
  515. --*/
  516. HRESULT
  517. TNameResolutionCache::
  518. IsNameInCache(
  519. IN LPCWSTR pszName
  520. )
  521. {
  522. TStatusH hRetval;
  523. hRetval DBGCHK = pszName ? S_OK : E_INVALIDARG;
  524. if (SUCCEEDED(hRetval))
  525. {
  526. NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
  527. TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
  528. hRetval DBGNOCHK = S_FALSE;
  529. for (It.First(); !It.IsDone(); It.Next())
  530. {
  531. hRetval DBGNOCHK = It.Current()->IsNameInNodeCache(pszName);
  532. if (hRetval == S_OK)
  533. {
  534. break;
  535. }
  536. }
  537. }
  538. return hRetval;
  539. }
  540. /*++
  541. Name:
  542. TNameResolutionCache::FindNameInCacheAndAgeOut
  543. Description:
  544. Searches for a certain name in the cache. If the name is found in the cache,
  545. but it is aged out, the name is eliminated and S_FALSE is returned. The aging
  546. time of names is specified as argument to the constructor of TNameResolutionCache
  547. Arguments:
  548. pszName - name to look for
  549. Return Value:
  550. S_OK - name was found
  551. S_FALSE - name was not found or it is aged out (and was eliminated therefore)
  552. any other HR - an error occurred
  553. --*/
  554. HRESULT
  555. TNameResolutionCache::
  556. FindNameInCacheAndAgeOut(
  557. IN LPCWSTR pszName
  558. )
  559. {
  560. TStatusH hRetval;
  561. hRetval DBGCHK = pszName ? S_OK : E_INVALIDARG;
  562. if (SUCCEEDED(hRetval))
  563. {
  564. TStringNode *pStringNode = NULL;
  565. m_Lock.Enter();
  566. TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
  567. //
  568. // We have not found a node containing pszName
  569. //
  570. hRetval DBGNOCHK = S_FALSE;
  571. for (It.First(); !It.IsDone(); It.Next())
  572. {
  573. //
  574. // Check if pszName is the name of the node
  575. //
  576. if (!_wcsicmp(It.Current()->m_Name, pszName))
  577. {
  578. hRetval DBGCHK = S_OK;
  579. break;
  580. }
  581. else
  582. {
  583. //
  584. // Check if the name is in the list of alternate names
  585. //
  586. hRetval DBGNOCHK = It.Current()->GetStringNodeFromName(pszName, &pStringNode);
  587. if (hRetval == S_OK)
  588. {
  589. //
  590. // We found pszName in the node pointed to by pStringNode
  591. //
  592. DWORD CurrentTick = GetTickCount();
  593. //
  594. // Check if we need to age out the node
  595. //
  596. if (CurrentTick - pStringNode->m_Time > m_AgingTime)
  597. {
  598. hRetval DBGCHK = It.Current()->m_pAlternateNames->Remove(pStringNode);
  599. if (SUCCEEDED(hRetval))
  600. {
  601. //
  602. // Since the entry was aged out, we pretend we did not find it
  603. //
  604. hRetval DBGNOCHK = S_FALSE;
  605. }
  606. }
  607. else
  608. {
  609. //
  610. // Not aged out, prevent it from being deleted
  611. //
  612. pStringNode = NULL;
  613. }
  614. break;
  615. }
  616. }
  617. }
  618. m_Lock.Leave();
  619. delete pStringNode;
  620. }
  621. return hRetval;;
  622. }
  623. /*++
  624. Name:
  625. TNameResolutionCache::CreateAndAddNodeWithIPAddresses
  626. Description:
  627. Checks if a node with pszName name is already in the cache. If not,
  628. it creates a new node and populates it with data. Then it adds the
  629. node to the list of nodes. pszName cannot be an IP address.
  630. This function does not call getaddrinfo on pszName. It blindly fills
  631. in the IP list for the node with data in ppszIPAddresses
  632. Arguments:
  633. pszName - name to create a new node for
  634. bClusterNode - specifies if this node corresponds to a cluster ini spooler
  635. ppszIPAddresses - array of strings representing IPs
  636. cIPAddresses - count of lements in the array
  637. Return Value:
  638. S_OK - a new node was created and added to the cache
  639. S_FALSE - a node with pszName name already existed in the cache
  640. any other HR - an error occurred while performing the operation
  641. --*/
  642. HRESULT
  643. TNameResolutionCache::
  644. CreateAndAddNodeWithIPAddresses(
  645. IN LPCWSTR pszName,
  646. IN BOOL bClusterNode,
  647. IN LPCWSTR *ppszIPAddresses,
  648. IN DWORD cIPAddresses
  649. )
  650. {
  651. TStatusH hRetval;
  652. hRetval DBGCHK = pszName &&
  653. ppszIPAddresses &&
  654. *ppszIPAddresses &&
  655. cIPAddresses > 0 ? S_OK : E_INVALIDARG;
  656. if (SUCCEEDED(hRetval))
  657. {
  658. //
  659. // The function IsNodeInCache will lock the cache for the search
  660. //
  661. hRetval DBGCHK = IsNodeInCache(pszName);
  662. //
  663. // Not found in the cache
  664. //
  665. if (hRetval == S_FALSE)
  666. {
  667. TResolutionCacheNode *pNode = new TResolutionCacheNode(pszName, bClusterNode);
  668. hRetval DBGCHK = pNode ? pNode->Valid() : E_OUTOFMEMORY;
  669. //
  670. // Add all the supported IP addresses
  671. //
  672. if (SUCCEEDED(hRetval))
  673. {
  674. DWORD Index;
  675. for (Index = 0; Index < cIPAddresses && SUCCEEDED(hRetval); Index++)
  676. {
  677. TStringNode *pItem = new TStringNode(ppszIPAddresses[Index]);
  678. hRetval DBGCHK = pItem ? pItem->Valid() : E_OUTOFMEMORY;
  679. if (SUCCEEDED(hRetval))
  680. {
  681. hRetval DBGCHK = pNode->m_pIPAddresses->AddAtHead(pItem);
  682. if (SUCCEEDED(hRetval))
  683. {
  684. //
  685. // List tooks ownership of string node object
  686. //
  687. pItem = NULL;
  688. }
  689. }
  690. delete pItem;
  691. }
  692. }
  693. if (SUCCEEDED(hRetval))
  694. {
  695. NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
  696. //
  697. // Make sure a node with pszName was not added to the cache while
  698. // we were calling the TResolutionCacheNode CTOR
  699. //
  700. hRetval DBGCHK = IsNodeInCache(pszName);
  701. //
  702. // Node not found
  703. //
  704. if (hRetval == S_FALSE)
  705. {
  706. if (bClusterNode)
  707. {
  708. hRetval DBGCHK = m_NodeList.AddAtHead(pNode);
  709. }
  710. else
  711. {
  712. hRetval DBGCHK = m_NodeList.AddAtTail(pNode);
  713. }
  714. if (SUCCEEDED(hRetval))
  715. {
  716. //
  717. // The cache took owership of pNode
  718. //
  719. pNode = NULL;
  720. }
  721. }
  722. }
  723. delete pNode;
  724. }
  725. }
  726. return hRetval;
  727. }
  728. /*++
  729. Name:
  730. TNameResolutionCache::Purge
  731. Description:
  732. Purges the cache. The function locks the cache.
  733. Arguments:
  734. None
  735. Return Value:
  736. S_OK - cache was purged
  737. any other HR - an error occurred
  738. --*/
  739. HRESULT
  740. TNameResolutionCache::
  741. Purge(
  742. VOID
  743. )
  744. {
  745. m_Lock.Enter();
  746. while (!m_NodeList.IsEmpty())
  747. {
  748. TResolutionCacheNode *pItem = m_NodeList.RemoveAtHead();
  749. delete pItem;
  750. }
  751. m_Lock.Leave();
  752. return S_OK;
  753. }
  754. /*++
  755. Name:
  756. TNameResolutionCache::CreateAndAddNode
  757. Description:
  758. Checks if a node with pszName name is already in the cache. If not,
  759. it creates a new node and populates it with data. Then it adds the
  760. node to the list of nodes. pszName cannot be an IP address.
  761. getaddrinfo must succeed on pszName. pszName cannot be an emulated
  762. server name. The function locks the cache.
  763. Arguments:
  764. pszName - name to create a new node for
  765. bClusterNode - specifies if this node corresponds to a cluster ini spooler
  766. Return Value:
  767. S_OK - a new node was created and added to the cache
  768. S_FALSE - a node with pszName name already existed in the cache
  769. any other HR - an error occurred while performing the operation
  770. --*/
  771. HRESULT
  772. TNameResolutionCache::
  773. CreateAndAddNode(
  774. IN LPCWSTR pszName,
  775. IN BOOL bClusterNode
  776. )
  777. {
  778. TStatusH hRetval;
  779. hRetval DBGCHK = pszName ? S_OK : E_INVALIDARG;
  780. if (SUCCEEDED(hRetval))
  781. {
  782. //
  783. // The function will lock the cache for the search
  784. //
  785. hRetval DBGCHK = IsNodeInCache(pszName);
  786. //
  787. // Not found in the cache
  788. //
  789. if (hRetval == S_FALSE)
  790. {
  791. hRetval DBGCHK = IsIPAddress(pszName);
  792. if (hRetval == S_OK)
  793. {
  794. //
  795. // We do not add IPs to our cache
  796. //
  797. hRetval DBGCHK = E_FAIL;
  798. }
  799. else
  800. {
  801. LPSTR pszAnsiName;
  802. hRetval DBGCHK = UnicodeToAnsiWithAlloc(pszName, &pszAnsiName);
  803. if (SUCCEEDED(hRetval))
  804. {
  805. TResolutionCacheNode *pNode = new TResolutionCacheNode(pszName, bClusterNode);
  806. hRetval DBGCHK = pNode ? pNode->Valid() : E_OUTOFMEMORY;
  807. if (SUCCEEDED(hRetval))
  808. {
  809. hRetval DBGCHK = ResolveNameToAddress(pszAnsiName, pNode->m_pIPAddresses, pNode->m_pAlternateNames);
  810. if (SUCCEEDED(hRetval))
  811. {
  812. TList<TStringNode> AddressesUsedByCluster;
  813. //
  814. // Check if the local machine has any IPs used by the cluster service
  815. //
  816. hRetval DBGCHK = GetClusterIPAddresses(&AddressesUsedByCluster);
  817. if (SUCCEEDED(hRetval))
  818. {
  819. hRetval DBGCHK = ExcludeIPsUsedByCluster(&AddressesUsedByCluster, pNode->m_pIPAddresses);
  820. }
  821. }
  822. else if (SCODE_CODE(hRetval) == WSAHOST_NOT_FOUND)
  823. {
  824. //
  825. // There are situations when the name of the local machine cannot be resolved
  826. // to an IP. This can happen for example when networking is partly initialized
  827. // In this case we want the spooler to function. The only thing is that it
  828. // won't keep an IP address associated with the node corresponding to the name
  829. // of the machine. Basically this is the node created by
  830. // SplCreateSpooler(\\local-machine-name). If the machine gets an IP address
  831. // later, then we will be notified and we can refresh the node.
  832. //
  833. if (!_wcsicmp(pszName, szMachineName + 2))
  834. {
  835. hRetval DBGNOCHK = S_OK;
  836. }
  837. }
  838. if (SUCCEEDED(hRetval))
  839. {
  840. NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
  841. //
  842. // Make sure a node with pszName was not added to the cache while
  843. // we were calling the TResolutionCacheNode CTOR
  844. //
  845. hRetval DBGCHK = IsNodeInCache(pszName);
  846. //
  847. // Node not found
  848. //
  849. if (hRetval == S_FALSE)
  850. {
  851. if (bClusterNode)
  852. {
  853. hRetval DBGCHK = m_NodeList.AddAtHead(pNode);
  854. }
  855. else
  856. {
  857. //
  858. // A local node cache is always added at the tail of the list, so
  859. // it is searched after cluster nodes.
  860. //
  861. hRetval DBGCHK = m_NodeList.AddAtTail(pNode);
  862. }
  863. if (SUCCEEDED(hRetval))
  864. {
  865. //
  866. // The cache took owership of pNode
  867. //
  868. pNode = NULL;
  869. }
  870. }
  871. }
  872. }
  873. delete pNode;
  874. delete [] pszAnsiName;
  875. }
  876. }
  877. }
  878. }
  879. return hRetval;
  880. }
  881. /*++
  882. Name:
  883. TNameResolutionCache::ResolveNameToAddress
  884. Description:
  885. Arguments:
  886. Return Value:
  887. --*/
  888. HRESULT
  889. TNameResolutionCache::
  890. ResolveNameToAddress(
  891. IN LPCSTR pszName,
  892. IN TList<TStringNode> *pAddresses,
  893. IN TList<TStringNode> *pNames
  894. )
  895. {
  896. TStatusH hRetval;
  897. hRetval DBGNOCHK = E_INVALIDARG;
  898. if (pszName && *pszName && pAddresses && pNames)
  899. {
  900. hRetval DBGCHK = m_FailedCache.IsStringInCache(pszName);
  901. if (hRetval == S_FALSE)
  902. {
  903. hRetval DBGCHK = GetAddrInfo(pszName, pAddresses, pNames);
  904. if (SCODE_CODE(hRetval) == WSAHOST_NOT_FOUND)
  905. {
  906. //
  907. // We intentionally ignore the error returned by this function
  908. // because we do not want to overwrite hRetval. Out callers need
  909. // the exact error code returned by GetAddrInfo
  910. //
  911. m_FailedCache.AddString(pszName);
  912. }
  913. }
  914. else
  915. {
  916. hRetval DBGNOCHK = HRESULT_FROM_WIN32(WSAHOST_NOT_FOUND);
  917. }
  918. }
  919. return hRetval;
  920. }
  921. /*++
  922. Name:
  923. TNameResolutionCache::RefreshNode
  924. Description:
  925. Checks if a node with pszName name is already in the cache. If not,
  926. it creates a new node and populates it with data. Then it adds the
  927. node to the list of nodes. pszName cannot be an IP address.
  928. getaddrinfo must succeed on pszName. pszName cannot be an emulated
  929. server name. The function locks the cache.
  930. Arguments:
  931. pszName - name to create a new node for
  932. bClusterNode - specifies if this node corresponds to a cluster ini spooler
  933. Return Value:
  934. S_OK - a new node was created and added to the cache
  935. S_FALSE - a node with pszName name already existed in the cache
  936. any other HR - an error occurred while performing the operation
  937. --*/
  938. HRESULT
  939. TNameResolutionCache::
  940. RefreshNode(
  941. IN LPCWSTR pszName
  942. )
  943. {
  944. TStatusH hRetval;
  945. hRetval DBGCHK = pszName ? S_OK : E_INVALIDARG;
  946. if (SUCCEEDED(hRetval))
  947. {
  948. //
  949. // Find the node matching pszName
  950. //
  951. hRetval DBGCHK = IsNodeInCache(pszName);
  952. //
  953. // We found the node that we need to refresh
  954. //
  955. if (hRetval == S_OK)
  956. {
  957. LPSTR pszAnsiName;
  958. hRetval DBGCHK = UnicodeToAnsiWithAlloc(pszName, &pszAnsiName);
  959. if (SUCCEEDED(hRetval))
  960. {
  961. TList<TStringNode> AddressesUsedByCluster;
  962. TList<TStringNode> *pNewAddresses = new TList<TStringNode>;
  963. TList<TStringNode> *pNewNames = new TList<TStringNode>;
  964. hRetval DBGCHK = pNewAddresses && pNewNames ? S_OK : E_OUTOFMEMORY;
  965. if (SUCCEEDED(hRetval))
  966. {
  967. //
  968. // Get new IPs and names
  969. //
  970. hRetval DBGCHK = ResolveNameToAddress(pszAnsiName, pNewAddresses, pNewNames);
  971. if (SUCCEEDED(hRetval))
  972. {
  973. //
  974. // Check if the local machine has any IPs coming from the cluster service
  975. //
  976. hRetval DBGCHK = GetClusterIPAddresses(&AddressesUsedByCluster);
  977. if (SUCCEEDED(hRetval))
  978. {
  979. hRetval DBGCHK = ExcludeIPsUsedByCluster(&AddressesUsedByCluster, pNewAddresses);
  980. }
  981. }
  982. //
  983. // Replace current IP and name list for the node with the new IPs and names
  984. //
  985. if (SUCCEEDED(hRetval))
  986. {
  987. TResolutionCacheNode *pCacheNode = NULL;
  988. TList<TStringNode> *pOldIPAddresses = NULL;
  989. TList<TStringNode> *pOldNames = NULL;
  990. m_Lock.Enter();
  991. //
  992. // Find the node macthing pszName. We need to make sure our node was not deleted while we
  993. // were out of the crit sec
  994. //
  995. TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
  996. hRetval DBGNOCHK = S_FALSE;
  997. for (It.First(); !It.IsDone(); It.Next())
  998. {
  999. hRetval DBGNOCHK = It.Current()->IsSameName(pszName);
  1000. if (hRetval == S_OK)
  1001. {
  1002. pCacheNode = It.Current();
  1003. break;
  1004. }
  1005. }
  1006. if (hRetval == S_OK)
  1007. {
  1008. pOldIPAddresses = pCacheNode->m_pIPAddresses;
  1009. pOldNames = pCacheNode->m_pAlternateNames;
  1010. pCacheNode->m_pIPAddresses = pNewAddresses;
  1011. pCacheNode->m_pAlternateNames = pNewNames;
  1012. //
  1013. // The node took ownership of the lists
  1014. //
  1015. pNewAddresses = NULL;
  1016. pNewNames = NULL;
  1017. }
  1018. m_Lock.Leave();
  1019. delete pOldIPAddresses;
  1020. delete pOldNames;
  1021. }
  1022. }
  1023. delete pNewAddresses;
  1024. delete pNewNames;
  1025. }
  1026. delete [] pszAnsiName;
  1027. }
  1028. }
  1029. return hRetval;
  1030. }
  1031. /*++
  1032. Name:
  1033. TNameResolutionCache::AddName
  1034. Description:
  1035. Adds a name to the cache. We try adding a name to the cache
  1036. by fining out to which node it belongs. The membership is determined
  1037. based on the IP(s) associated with the server name. If getaddrinfo
  1038. fails on pszName, when we look if the name is an alias to the local
  1039. machine added with NetServerComputerNameAdd.
  1040. Arguments:
  1041. pszName - name to add to the cache
  1042. Return Value:
  1043. S_OK - name was present in the cache or was added
  1044. E_FAIL - the name is an IP and was not added to the cache
  1045. other HRESULT - an error occurred while performing the operation
  1046. --*/
  1047. HRESULT
  1048. TNameResolutionCache::
  1049. AddName(
  1050. IN LPCWSTR pszName
  1051. )
  1052. {
  1053. TStatusH hRetval;
  1054. hRetval DBGCHK = FindNameInCacheAndAgeOut(pszName);
  1055. //
  1056. // Not found in the cache
  1057. //
  1058. if (hRetval == S_FALSE)
  1059. {
  1060. hRetval DBGCHK = IsIPAddress(pszName);
  1061. if (hRetval == S_OK)
  1062. {
  1063. //
  1064. // We do not add IPs to our cache
  1065. //
  1066. hRetval DBGCHK = E_FAIL;
  1067. }
  1068. else
  1069. {
  1070. LPSTR pszAnsiName;
  1071. hRetval DBGCHK = UnicodeToAnsiWithAlloc(pszName, &pszAnsiName);
  1072. if (SUCCEEDED(hRetval))
  1073. {
  1074. //
  1075. // Get IP and alternates names lists
  1076. //
  1077. TList<TStringNode> IPAddresses;
  1078. TList<TStringNode> AlternateNames;
  1079. hRetval DBGCHK = ResolveNameToAddress(pszAnsiName, &IPAddresses, &AlternateNames);
  1080. if (hRetval == S_OK)
  1081. {
  1082. //
  1083. // We found IP(s) for pszName
  1084. //
  1085. hRetval DBGCHK = AddNameUsingIPList(pszName, IPAddresses);
  1086. }
  1087. else if (SCODE_CODE(hRetval) == WSAHOST_NOT_FOUND)
  1088. {
  1089. //
  1090. // Check if the name is an emulated server name
  1091. //
  1092. TString AliasedServer;
  1093. hRetval DBGCHK = GetNetBiosInfo(pszAnsiName, &AliasedServer);
  1094. if (hRetval == S_OK)
  1095. {
  1096. hRetval DBGCHK = AddNetBiosName(AliasedServer, pszName);
  1097. }
  1098. }
  1099. delete [] pszAnsiName;
  1100. }
  1101. }
  1102. }
  1103. return hRetval;
  1104. }
  1105. /*++
  1106. Name:
  1107. TNameResolutionCache::AddNetBiosName
  1108. Description:
  1109. Searches in the cache the node called pszNode. Then it adds pszAlias to
  1110. the list of supported names. Locks the cache.
  1111. Arguments:
  1112. pszNode - name of node where to add pszAlias
  1113. pszAlias - name to be added to the Alternate names list
  1114. Return Value:
  1115. None
  1116. --*/
  1117. HRESULT
  1118. TNameResolutionCache::
  1119. AddNetBiosName(
  1120. IN LPCWSTR pszNode,
  1121. IN LPCWSTR pszAlias
  1122. )
  1123. {
  1124. TStatusH hRetval;
  1125. TStringNode *pStringNode = new TStringNode(pszAlias);
  1126. hRetval DBGCHK = pStringNode ? pStringNode->Valid() : E_OUTOFMEMORY;
  1127. if (SUCCEEDED(hRetval))
  1128. {
  1129. NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
  1130. TList<TResolutionCacheNode>::TIterator It(&m_NodeList);
  1131. hRetval DBGNOCHK = S_FALSE;
  1132. for (It.First(); !It.IsDone(); It.Next())
  1133. {
  1134. if (It.Current()->IsSameName(pszNode) == S_OK)
  1135. {
  1136. hRetval DBGNOCHK = It.Current()->IsNameInNodeCache(pszAlias);
  1137. //
  1138. // We didn't find the alias in the cache
  1139. //
  1140. if (hRetval == S_FALSE)
  1141. {
  1142. hRetval DBGCHK = It.Current()->m_pAlternateNames->AddAtTail(pStringNode);
  1143. if (SUCCEEDED(hRetval))
  1144. {
  1145. //
  1146. // The cache took ownership of the node.
  1147. //
  1148. pStringNode = NULL;
  1149. }
  1150. }
  1151. break;
  1152. }
  1153. }
  1154. }
  1155. delete pStringNode;
  1156. return hRetval;
  1157. }
  1158. /*++
  1159. Name:
  1160. TNameResolutionCache::AddNameUsingIPList
  1161. Description:
  1162. Searches in the cache the node called pszNode. Then it adds pszAdditionalName to
  1163. the list of supported names. Locks the cache.
  1164. Arguments:
  1165. pszNode - name of node where to add pszAlias
  1166. pszAlias - name to be added to the Alternate names list
  1167. Return Value:
  1168. None
  1169. --*/
  1170. HRESULT
  1171. TNameResolutionCache::
  1172. AddNameUsingIPList(
  1173. IN LPCWSTR pszAdditionalName,
  1174. IN TList<TStringNode>& pIPAddresses
  1175. )
  1176. {
  1177. TStatusH hRetval;
  1178. TStringNode *pStringNode = new TStringNode(pszAdditionalName);
  1179. hRetval DBGCHK = pStringNode ? pStringNode->Valid() : E_OUTOFMEMORY;
  1180. if (SUCCEEDED(hRetval))
  1181. {
  1182. NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
  1183. TList<TResolutionCacheNode>::TIterator ItCache(&m_NodeList);
  1184. //
  1185. // Assume success
  1186. //
  1187. hRetval DBGNOCHK = S_OK;
  1188. //
  1189. // Iterate through the list of nodes
  1190. //
  1191. for (ItCache.First(); !ItCache.IsDone(); ItCache.Next())
  1192. {
  1193. //
  1194. // For each node in the cache we need to see if all the IPs
  1195. // for the name to be added match the IPs of the node
  1196. //
  1197. TList<TStringNode>::TIterator ItAddresses(&pIPAddresses);
  1198. for (ItAddresses.First(); !ItAddresses.IsDone(); ItAddresses.Next())
  1199. {
  1200. hRetval DBGCHK = ItCache.Current()->IsNameInNodeCache(ItAddresses.Current()->m_String);
  1201. //
  1202. // For clusters all IPs need to be found in the node's list of IPs
  1203. // For local nodes, one IP must be found in the node's list f IPs
  1204. //
  1205. if (hRetval != S_OK && ItCache.Current()->m_bClusterNode)
  1206. {
  1207. break;
  1208. }
  1209. }
  1210. if (hRetval == S_OK)
  1211. {
  1212. TStringNode *pExistingNode = NULL;
  1213. //
  1214. // Our IPs match the IPs of the current node. Now we need to make sure a node
  1215. // with the name pszAdditionalName was not added while we were out of the
  1216. // critical section
  1217. //
  1218. hRetval DBGCHK = ItCache.Current()->GetStringNodeFromName(pszAdditionalName, &pExistingNode);
  1219. if (hRetval == S_OK)
  1220. {
  1221. //
  1222. // We do not remove the node in this case, we just update the time.
  1223. //
  1224. pExistingNode->m_Time = GetTickCount();
  1225. }
  1226. else if (hRetval == S_FALSE)
  1227. {
  1228. hRetval DBGCHK = ItCache.Current()->m_pAlternateNames->AddAtTail(pStringNode);
  1229. if (SUCCEEDED(hRetval))
  1230. {
  1231. //
  1232. // The cache took ownership of the node.
  1233. //
  1234. pStringNode = NULL;
  1235. }
  1236. }
  1237. break;
  1238. }
  1239. }
  1240. }
  1241. delete pStringNode;
  1242. return hRetval;
  1243. }
  1244. /*++
  1245. Name:
  1246. TStringNode::TStringNode
  1247. Description:
  1248. Constructor
  1249. Arguments:
  1250. pszString - name of the node
  1251. Return Value:
  1252. None
  1253. --*/
  1254. TStringNode::
  1255. TStringNode(
  1256. IN LPCWSTR pszString
  1257. )
  1258. {
  1259. m_hr = m_String.Update(pszString);
  1260. m_Time = GetTickCount();
  1261. }
  1262. /*++
  1263. Name:
  1264. TStringNode::~TStringNode
  1265. Description:
  1266. Destructor
  1267. Arguments:
  1268. None
  1269. Return Value:
  1270. None
  1271. --*/
  1272. TStringNode::
  1273. ~TStringNode(
  1274. VOID
  1275. )
  1276. {
  1277. }
  1278. /*++
  1279. Name:
  1280. TStringNode::Valid
  1281. Description:
  1282. Valid
  1283. Arguments:
  1284. None
  1285. Return Value:
  1286. S_OK - the object is valid
  1287. any other HR - the object is invalid
  1288. --*/
  1289. HRESULT
  1290. TStringNode::
  1291. Valid(
  1292. VOID
  1293. ) const
  1294. {
  1295. return m_hr;
  1296. }
  1297. /*++
  1298. Name:
  1299. TStringNode::RefreshTimeStamp
  1300. Description:
  1301. Refreshes the time stamp stored in the node
  1302. Arguments:
  1303. None
  1304. Return Value:
  1305. None
  1306. --*/
  1307. VOID
  1308. TStringNode::
  1309. RefreshTimeStamp(
  1310. VOID
  1311. )
  1312. {
  1313. m_Time = GetTickCount();
  1314. }
  1315. /*++
  1316. Name:
  1317. TResolutionCacheNode::TResolutionCacheNode
  1318. Description:
  1319. CTOR TResolutionCacheNode. The function tries to gather information
  1320. about the server pszName by calling getaddrinfo.
  1321. Arguments:
  1322. None
  1323. Return Value:
  1324. None
  1325. --*/
  1326. TResolutionCacheNode::
  1327. TResolutionCacheNode(
  1328. IN LPCWSTR pszName,
  1329. IN BOOL bClusterNode
  1330. ) : m_bClusterNode(bClusterNode)
  1331. {
  1332. m_hr = E_INVALIDARG;
  1333. if (pszName && *pszName)
  1334. {
  1335. m_hr = m_Name.Update(pszName);
  1336. if (SUCCEEDED(m_hr))
  1337. {
  1338. m_pIPAddresses = new NCoreLibrary::TList<TStringNode>;
  1339. m_pAlternateNames = new NCoreLibrary::TList<TStringNode>;
  1340. m_hr = m_pIPAddresses && m_pAlternateNames ? S_OK : E_OUTOFMEMORY;
  1341. }
  1342. }
  1343. }
  1344. /*++
  1345. Name:
  1346. TResolutionCacheNode::~TResolutionCacheNode
  1347. Description:
  1348. TResolutionCacheNode
  1349. Arguments:
  1350. None
  1351. Return Value:
  1352. None
  1353. --*/
  1354. TResolutionCacheNode::
  1355. ~TResolutionCacheNode(
  1356. VOID
  1357. )
  1358. {
  1359. delete m_pIPAddresses;
  1360. delete m_pAlternateNames;
  1361. }
  1362. /*++
  1363. Name:
  1364. TResolutionCacheNode::Valid
  1365. Description:
  1366. Return m_hr.
  1367. Arguments:
  1368. None
  1369. Return Value:
  1370. S_OK - object is valid
  1371. any other HR - object is not valid
  1372. --*/
  1373. HRESULT
  1374. TResolutionCacheNode::
  1375. Valid(
  1376. VOID
  1377. ) const
  1378. {
  1379. return m_hr;
  1380. }
  1381. /*++
  1382. Name:
  1383. TResolutionCacheNode::IsSameName
  1384. Description:
  1385. Checks if pszName matches the name of the node
  1386. Arguments:
  1387. pszName - name to look for in the node name
  1388. Return Value:
  1389. S_OK - same names
  1390. S_FALSE - not same names
  1391. --*/
  1392. HRESULT
  1393. TResolutionCacheNode::
  1394. IsSameName(
  1395. IN LPCWSTR pszName
  1396. )
  1397. {
  1398. HRESULT hr = S_FALSE;
  1399. if (pszName && *pszName && !_wcsicmp(pszName, m_Name))
  1400. {
  1401. hr = S_OK;
  1402. }
  1403. return hr;
  1404. }
  1405. /*++
  1406. Name:
  1407. TResolutionCacheNode::IsNameInNodeCache
  1408. Description:
  1409. Checks if pszName is the IP or in the AlternateNames list for this node
  1410. Arguments:
  1411. pszName - name to look for
  1412. Return Value:
  1413. S_OK - pszName found
  1414. S_FALSE - pszName not found
  1415. any other HR - an error occurred
  1416. --*/
  1417. HRESULT
  1418. TResolutionCacheNode::
  1419. IsNameInNodeCache(
  1420. IN LPCWSTR pszName
  1421. )
  1422. {
  1423. TStatusH hRetval;
  1424. hRetval DBGCHK = IsSameName(pszName);
  1425. if (hRetval == S_FALSE)
  1426. {
  1427. TList<TStringNode>::TIterator It(m_pIPAddresses);
  1428. for (It.First(); !It.IsDone(); It.Next())
  1429. {
  1430. if (!_wcsicmp(pszName, (LPCWSTR)It.Current()->m_String))
  1431. {
  1432. hRetval DBGCHK = S_OK;
  1433. break;
  1434. }
  1435. }
  1436. }
  1437. if (hRetval == S_FALSE)
  1438. {
  1439. //
  1440. // This list can age out
  1441. //
  1442. TList<TStringNode>::TIterator It(m_pAlternateNames);
  1443. for (It.First(); !It.IsDone(); It.Next())
  1444. {
  1445. if (!_wcsicmp(pszName, (LPCWSTR)It.Current()->m_String))
  1446. {
  1447. hRetval DBGCHK = S_OK;
  1448. break;
  1449. }
  1450. }
  1451. }
  1452. return hRetval;
  1453. }
  1454. /*++
  1455. Name:
  1456. TResolutionCacheNode::GetStringNodeFromName
  1457. Description:
  1458. Checks if pszName is the the AlternateNames list for this node.
  1459. Note: IsNameInNodeCache looks for both AlternateNames and IPs.
  1460. Returns the string node that contains the pszName
  1461. Arguments:
  1462. pszName - name to look for
  1463. pStringNode - pointer to string node containing the pszName
  1464. Return Value:
  1465. S_OK - pszName found
  1466. S_FALSE - pszName not found
  1467. any other HR - an error occurred
  1468. --*/
  1469. HRESULT
  1470. TResolutionCacheNode::
  1471. GetStringNodeFromName(
  1472. IN LPCWSTR pszName,
  1473. OUT TStringNode **pStringNode
  1474. )
  1475. {
  1476. TStatusH hRetval;
  1477. hRetval DBGCHK = pStringNode && pszName ? S_OK : E_INVALIDARG;
  1478. if (SUCCEEDED(hRetval))
  1479. {
  1480. //
  1481. // StringNode not found
  1482. //
  1483. hRetval DBGCHK = S_FALSE;
  1484. //
  1485. // This list can age out
  1486. //
  1487. TList<TStringNode>::TIterator It(m_pAlternateNames);
  1488. for (It.First(); !It.IsDone(); It.Next())
  1489. {
  1490. if (!_wcsicmp(pszName, (LPCWSTR)It.Current()->m_String))
  1491. {
  1492. hRetval DBGCHK = S_OK;
  1493. *pStringNode = It.Current();
  1494. break;
  1495. }
  1496. }
  1497. }
  1498. return hRetval;
  1499. }
  1500. /*++
  1501. Name:
  1502. TFastCache::TFastCache
  1503. Description:
  1504. Constructs the fast cache object
  1505. Arguments:
  1506. TimeToLive - time to live of an element in the fast cache
  1507. CacheSize - number of elements in the cache
  1508. MaxStrLen - maximum length of strings stored in the cache
  1509. Return Value:
  1510. None. Call IsValid before using a fast cache object
  1511. --*/
  1512. TFastCache::
  1513. TFastCache(
  1514. IN DWORD TimeToLive,
  1515. IN DWORD CacheSize,
  1516. IN DWORD MaxStrLen
  1517. ) : m_ArraySize(CacheSize),
  1518. m_TTL(TimeToLive),
  1519. m_MaxStrLen(MaxStrLen),
  1520. m_CurrentCount(0)
  1521. {
  1522. m_pArray = new TFastCacheElement[m_ArraySize];
  1523. m_hr = m_pArray ? m_Lock.IsValid() : E_OUTOFMEMORY;
  1524. }
  1525. /*++
  1526. Name:
  1527. TFastCache::~TFastCache
  1528. Description:
  1529. Destroys the fast cache object
  1530. Arguments:
  1531. None.
  1532. Return Value:
  1533. None.
  1534. --*/
  1535. TFastCache::
  1536. ~TFastCache(
  1537. VOID
  1538. )
  1539. {
  1540. delete [] m_pArray;
  1541. }
  1542. /*++
  1543. Name:
  1544. TFastCache::IsValid
  1545. Description:
  1546. Checks if the object was constructed correctly
  1547. Arguments:
  1548. None.
  1549. Return Value:
  1550. S_OK - fast cache object can be used
  1551. other HRESULT - do not use the cache
  1552. --*/
  1553. HRESULT
  1554. TFastCache::
  1555. IsValid(
  1556. VOID
  1557. )
  1558. {
  1559. return m_hr;
  1560. }
  1561. /*++
  1562. Name:
  1563. TFastCache::IsStringInCache
  1564. Description:
  1565. Verifies if a string is in the cache. Takes into account the TTL.
  1566. Arguments:
  1567. pszString - string to look for. Note that the cache does not
  1568. accept NULL or "" as valid strings.
  1569. Return Value:
  1570. S_OK - pszString is in the cache
  1571. S_FALSE - pszString is not in the cache (or TTL expired)
  1572. --*/
  1573. HRESULT
  1574. TFastCache::
  1575. IsStringInCache(
  1576. IN PCSTR pszString
  1577. )
  1578. {
  1579. HRESULT hRet = S_FALSE;
  1580. if (pszString && *pszString)
  1581. {
  1582. NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
  1583. DWORD CurrentTick = GetTickCount();
  1584. for (DWORD i = 0; i < m_CurrentCount; i++)
  1585. {
  1586. if (m_pArray[i].m_pszString &&
  1587. CurrentTick - m_pArray[i].m_TimeStamp < m_TTL &&
  1588. !_stricmp(pszString, m_pArray[i].m_pszString))
  1589. {
  1590. hRet = S_OK;
  1591. break;
  1592. }
  1593. }
  1594. }
  1595. return hRet;
  1596. }
  1597. /*++
  1598. Name:
  1599. TFastCache::AddString
  1600. Description:
  1601. Adds a string to the cache. Algorithm is explained below.
  1602. scan the array
  1603. if we find an emptry spot, put the string there and exit
  1604. else if the string==string in array
  1605. update timestamp and exit
  1606. else if TTL os string in array is expired replace string in array and exit
  1607. else check how old the timestamp is
  1608. end scan
  1609. if we didn't place the string so far, replace the oldest element in the array
  1610. Note that the function exits as soon as pszString is placed. so the function does not
  1611. eliminate expired elemets after pszString is placed in the right spot.
  1612. Arguments:
  1613. pszString - string to add to the cache.
  1614. Return Value:
  1615. S_OK - string was added to the cache
  1616. other HRESULT - string was not added to the cache
  1617. --*/
  1618. HRESULT
  1619. TFastCache::
  1620. AddString(
  1621. IN PCSTR pszString
  1622. )
  1623. {
  1624. HRESULT hRet = E_INVALIDARG;
  1625. if (pszString && *pszString && strlen(pszString) <= m_MaxStrLen)
  1626. {
  1627. NCoreLibrary::TCriticalSection::TLock Lock(m_Lock);
  1628. DWORD CurrentTick = GetTickCount();
  1629. DWORD OldestElemIndex = 0;
  1630. DWORD OldestAge = 0;
  1631. hRet = S_FALSE;
  1632. for (DWORD i = 0; i < m_CurrentCount; i++)
  1633. {
  1634. if (m_pArray[i].m_pszString)
  1635. {
  1636. if (!_stricmp(pszString, m_pArray[i].m_pszString))
  1637. {
  1638. m_pArray[i].m_TimeStamp = GetTickCount();
  1639. hRet = S_OK;
  1640. break;
  1641. }
  1642. else if (CurrentTick - m_pArray[i].m_TimeStamp > m_TTL)
  1643. {
  1644. hRet = m_pArray[i].UpdateElement(pszString);
  1645. break;
  1646. }
  1647. else if (CurrentTick - m_pArray[i].m_TimeStamp > OldestAge)
  1648. {
  1649. OldestAge = CurrentTick - m_pArray[i].m_TimeStamp;
  1650. OldestElemIndex = i;
  1651. }
  1652. }
  1653. else
  1654. {
  1655. hRet = m_pArray[i].UpdateElement(pszString);
  1656. break;
  1657. }
  1658. }
  1659. if (hRet == S_FALSE)
  1660. {
  1661. if (m_CurrentCount < m_ArraySize)
  1662. {
  1663. hRet = m_pArray[m_CurrentCount].UpdateElement(pszString);
  1664. if (SUCCEEDED(hRet))
  1665. {
  1666. //
  1667. // Note that we never decrease the value of m_CurrentCount.
  1668. // For performance reasons, we do not have a function that
  1669. // checks the timestamps of the elements in the cache and then
  1670. // eliminates aged elements. Aged elements are always replaced,
  1671. // but never eliminated. Generally, we won't have more than
  1672. // a couple of elements in the fast cache, so m_CurrentCount
  1673. // does help. If you call OpenPrinter/EnumPrinters/etc at the same
  1674. // time (meaning all calls within a minute or whatever the TTL is)
  1675. // on m_ArraySize number of server names which cannot be resolved,
  1676. // then the m_CurrentCount would be equal to m_ArraySize and would
  1677. // never drop. This scenario is very rare.
  1678. //
  1679. m_CurrentCount++;
  1680. }
  1681. }
  1682. else
  1683. {
  1684. hRet = m_pArray[OldestElemIndex].UpdateElement(pszString);
  1685. }
  1686. }
  1687. }
  1688. return hRet;
  1689. }
  1690. /*++
  1691. Name:
  1692. TFastCacheElement::TFastCacheElement
  1693. Description:
  1694. CTOR. Initilizes members of element
  1695. Arguments:
  1696. None.
  1697. Return Value:
  1698. None.
  1699. --*/
  1700. TFastCacheElement::
  1701. TFastCacheElement(
  1702. VOID
  1703. ) : m_pszString(NULL),
  1704. m_TimeStamp(0)
  1705. {
  1706. }
  1707. /*++
  1708. Name:
  1709. TFastCacheElement::TFastCacheElement
  1710. Description:
  1711. DTOR. Frees the string held by element
  1712. Arguments:
  1713. None.
  1714. Return Value:
  1715. None.
  1716. --*/
  1717. TFastCacheElement::
  1718. ~TFastCacheElement(
  1719. VOID
  1720. )
  1721. {
  1722. delete [] m_pszString;
  1723. }
  1724. /*++
  1725. Name:
  1726. TFastCacheElement::UpdateElement
  1727. Description:
  1728. Helper function.
  1729. Arguments:
  1730. pszString - string to store in TFastCacheElement
  1731. Return Value:
  1732. S_OK - pszString was stored in *pElement
  1733. other HRESULT - an error occurred
  1734. --*/
  1735. HRESULT
  1736. TFastCacheElement::
  1737. UpdateElement(
  1738. IN PCSTR pszString
  1739. )
  1740. {
  1741. HRESULT hRet = S_OK;
  1742. delete [] m_pszString;
  1743. m_pszString = new CHAR[strlen(pszString) + 1];
  1744. if (m_pszString)
  1745. {
  1746. StringCchCopyA(m_pszString, strlen(pszString) + 1, pszString);
  1747. m_TimeStamp = GetTickCount();
  1748. }
  1749. else
  1750. {
  1751. hRet = E_OUTOFMEMORY;
  1752. }
  1753. return hRet;
  1754. }