Source code of Windows XP (NT5)
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.

919 lines
24 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. compacct.c
  5. Abstract:
  6. Implements DoesComputerAccountExistOnDomain, which determines if a computer
  7. account exists given an NT domain and computer name. This is used to
  8. warn the user if they are going to be joining an NT domain but do not have
  9. a computer account ready.
  10. Author:
  11. Jim Schmidt (jimschm) 02-Jan-1998
  12. Revision History:
  13. jimschm 23-Sep-1998 Added 20 retries for datagram write
  14. --*/
  15. #include "pch.h"
  16. #include <netlogon.h> // private\inc
  17. //
  18. // Contants from sdk\inc\ntsam.h -- copied here because ntsam.h redefines things
  19. //
  20. //
  21. // User account control flags...
  22. //
  23. #define USER_ACCOUNT_DISABLED (0x00000001)
  24. #define USER_HOME_DIRECTORY_REQUIRED (0x00000002)
  25. #define USER_PASSWORD_NOT_REQUIRED (0x00000004)
  26. #define USER_TEMP_DUPLICATE_ACCOUNT (0x00000008)
  27. #define USER_NORMAL_ACCOUNT (0x00000010)
  28. #define USER_MNS_LOGON_ACCOUNT (0x00000020)
  29. #define USER_INTERDOMAIN_TRUST_ACCOUNT (0x00000040)
  30. #define USER_WORKSTATION_TRUST_ACCOUNT (0x00000080)
  31. #define USER_SERVER_TRUST_ACCOUNT (0x00000100)
  32. #define USER_DONT_EXPIRE_PASSWORD (0x00000200)
  33. #define USER_ACCOUNT_AUTO_LOCKED (0x00000400)
  34. #define USER_ENCRYPTED_TEXT_PASSWORD_ALLOWED (0x00000800)
  35. #define USER_SMARTCARD_REQUIRED (0x00001000)
  36. #define USER_MACHINE_ACCOUNT_MASK \
  37. ( USER_INTERDOMAIN_TRUST_ACCOUNT |\
  38. USER_WORKSTATION_TRUST_ACCOUNT |\
  39. USER_SERVER_TRUST_ACCOUNT)
  40. #define USER_ACCOUNT_TYPE_MASK \
  41. ( USER_TEMP_DUPLICATE_ACCOUNT |\
  42. USER_NORMAL_ACCOUNT |\
  43. USER_MACHINE_ACCOUNT_MASK )
  44. //
  45. // Defines
  46. //
  47. #define LM20_TOKENBYTE 0xFF // net\inc\logonp.h
  48. #define LMNT_TOKENBYTE 0xFF
  49. #define MAX_INBOUND_MESSAGE 400
  50. #define PING_RETRY_MAX 3 // number of attempts made against domain
  51. #define NETRES_INITIAL_SIZE 16384
  52. //
  53. // Types
  54. //
  55. typedef enum {
  56. ACCOUNT_FOUND,
  57. ACCOUNT_NOT_FOUND,
  58. DOMAIN_NOT_FOUND
  59. } SCAN_STATE;
  60. //
  61. // Local prototypes
  62. //
  63. BOOL
  64. pEnumNetResourceWorker (
  65. IN OUT PNETRESOURCE_ENUM EnumPtr
  66. );
  67. //
  68. // Implementation
  69. //
  70. VOID
  71. pGenerateLogonMailslotNameA (
  72. OUT PSTR SlotName,
  73. IN PCSTR DomainName
  74. )
  75. /*++
  76. Routine Description:
  77. pGenerateLogonMailslotNameA creates the mailslot name needed to query
  78. an NT domain server. It uses an undocumented syntax to open a mailslot
  79. to DomainName with the 16th character 1Ch.
  80. Arguments:
  81. SlotName - Receives the mailslot name. Should be a MAX_PATH buffer.
  82. DomainName - Specifies the name of the domain to query.
  83. Return Value:
  84. none
  85. --*/
  86. {
  87. StringCopyA (SlotName, "\\\\");
  88. StringCatA (SlotName, DomainName);
  89. StringCatA (SlotName, "*");
  90. StringCatA (SlotName, NETLOGON_NT_MAILSLOT_A);
  91. }
  92. PSTR
  93. pAppendStringA (
  94. OUT PSTR Buffer,
  95. IN PCSTR Source
  96. )
  97. /*++
  98. Routine Description:
  99. pAppendStringA appends the specified string in Source to the specified
  100. Buffer. The entire string, including the nul, is copied. The return
  101. value points to the character after the nul in Buffer.
  102. Arguments:
  103. Buffer - Receives the copy of Source, up to and including the nul.
  104. Source - Specifies the nul-terminated string to copy.
  105. Return Value:
  106. A pointer to the next character after the newly copied string in Buffer.
  107. The caller will use this pointer for additional append operations.
  108. --*/
  109. {
  110. while (*Source) {
  111. *Buffer++ = *Source++;
  112. }
  113. *Buffer++ = 0;
  114. return Buffer;
  115. }
  116. PWSTR
  117. pAppendStringW (
  118. OUT PWSTR Buffer,
  119. IN PCWSTR Source
  120. )
  121. /*++
  122. Routine Description:
  123. pAppendStringW appends the specified string in Source to the specified
  124. Buffer. The entire string, including the nul, is copied. The return
  125. value points to the character after the nul in Buffer.
  126. Arguments:
  127. Buffer - Receives the copy of Source, up to and including the nul.
  128. Source - Specifies the nul-terminated string to copy.
  129. Return Value:
  130. A pointer to the next character after the newly copied string in Buffer.
  131. The caller will use this pointer for additional append operations.
  132. --*/
  133. {
  134. while (*Source) {
  135. *Buffer++ = *Source++;
  136. }
  137. *Buffer++ = 0;
  138. return Buffer;
  139. }
  140. PBYTE
  141. pAppendBytes (
  142. OUT PBYTE Buffer,
  143. IN PBYTE Source,
  144. IN UINT Len
  145. )
  146. /*++
  147. Routine Description:
  148. pAppendBytes appends the specified block of data in Source to the specified
  149. Buffer. Len specifies the size of Source. The return value points to
  150. the byte after the copied block of data in Buffer.
  151. Arguments:
  152. Buffer - Receives the copy of Source
  153. Source - Specifies the block of data to copy
  154. Len - Specifies the number of bytes in Source
  155. Return Value:
  156. A pointer to the next byte after the newly copied blcok of data in Buffer.
  157. The caller will use this pointer for additional append operations.
  158. --*/
  159. {
  160. while (Len > 0) {
  161. *Buffer++ = *Source++;
  162. Len--;
  163. }
  164. return Buffer;
  165. }
  166. INT
  167. pBuildDomainPingMessageA (
  168. OUT PBYTE Buffer, // must be sizeof (NETLOGON_SAM_LOGON_REQUEST) + sizeof (DWORD)
  169. IN PCSTR LookUpName,
  170. IN PCSTR ReplySlotName
  171. )
  172. /*++
  173. Routine Description:
  174. pBuildDomainPingMessageA generates a SAM logon SMB that can be sent to
  175. the NT domain server's NTLOGON mailslot. If the server receives this
  176. message, it will reply with either LOGON_SAM_USER_UNKNOWN, LOGON_SAM_LOGON_RESPONSE
  177. or LOGON_SAM_LOGON_PAUSED.
  178. Arguments:
  179. Buffer - Receives the SMB message
  180. LookUpName - Specifies the name of the computer account that may be on
  181. the domain. (The domain is specified by the mailslot name.)
  182. ReplySlotName - Specifies the name of an open mailslot that will receive the
  183. server's response, if any.
  184. Return Value:
  185. The number of bytes used in Buffer, or zero if an error occurred, such as
  186. out of memory.
  187. --*/
  188. {
  189. CHAR ComputerName[MAX_COMPUTER_NAMEA];
  190. DWORD Size;
  191. PNETLOGON_SAM_LOGON_REQUEST SamLogonRequest;
  192. PSTR p;
  193. DWORD ControlBits;
  194. DWORD DomainSidSize;
  195. DWORD NtVersion;
  196. BYTE NtTokenByte;
  197. BYTE LmTokenByte;
  198. PCWSTR UnicodeComputerName;
  199. PCWSTR UnicodeLookUpName;
  200. //
  201. // Get computer name
  202. //
  203. Size = sizeof (ComputerName) / sizeof (ComputerName[0]);
  204. if (!GetComputerNameA (ComputerName, &Size)) {
  205. LOG ((LOG_ERROR, "Can't get computer name."));
  206. return FALSE;
  207. }
  208. //
  209. // Create unicode strings
  210. //
  211. UnicodeComputerName = CreateUnicode (ComputerName);
  212. if (!UnicodeComputerName) {
  213. return 0;
  214. }
  215. UnicodeLookUpName = CreateUnicode (LookUpName);
  216. if (!UnicodeLookUpName) {
  217. DestroyUnicode (UnicodeComputerName);
  218. return 0;
  219. }
  220. //
  221. // Init pointers
  222. //
  223. SamLogonRequest = (PNETLOGON_SAM_LOGON_REQUEST) Buffer;
  224. p = (PSTR) (SamLogonRequest->UnicodeComputerName);
  225. //
  226. // Initialize request packet
  227. //
  228. SamLogonRequest->Opcode = LOGON_SAM_LOGON_REQUEST;
  229. SamLogonRequest->RequestCount = 0;
  230. //
  231. // Append the rest of the params together
  232. //
  233. p = (PSTR) pAppendStringW ((PWSTR) p, UnicodeComputerName);
  234. p = (PSTR) pAppendStringW ((PWSTR) p, UnicodeLookUpName);
  235. p = pAppendStringA (p, ReplySlotName);
  236. ControlBits = USER_MACHINE_ACCOUNT_MASK;
  237. p = (PSTR) pAppendBytes ((PBYTE) p, (PBYTE) (&ControlBits), sizeof (DWORD));
  238. DomainSidSize = 0;
  239. p = (PSTR) pAppendBytes ((PBYTE) p, (PBYTE) (&DomainSidSize), sizeof (DWORD));
  240. NtVersion = NETLOGON_NT_VERSION_1;
  241. p = (PSTR) pAppendBytes ((PBYTE) p, (PBYTE) (&NtVersion), sizeof (DWORD));
  242. NtTokenByte = LMNT_TOKENBYTE;
  243. LmTokenByte = LM20_TOKENBYTE;
  244. p = (PSTR) pAppendBytes ((PBYTE) p, &NtTokenByte, sizeof (BYTE));
  245. p = (PSTR) pAppendBytes ((PBYTE) p, &NtTokenByte, sizeof (BYTE));
  246. p = (PSTR) pAppendBytes ((PBYTE) p, &LmTokenByte, sizeof (BYTE));
  247. p = (PSTR) pAppendBytes ((PBYTE) p, &LmTokenByte, sizeof (BYTE));
  248. DestroyUnicode (UnicodeComputerName);
  249. DestroyUnicode (UnicodeLookUpName);
  250. return p - Buffer;
  251. }
  252. LONG
  253. DoesComputerAccountExistOnDomain (
  254. IN PCTSTR DomainName,
  255. IN PCTSTR LookUpName,
  256. IN BOOL WaitCursorEnable
  257. )
  258. /*++
  259. Routine Description:
  260. DoesComputerAccountExistOnDomain queries a domain for the existence of
  261. a computer account. It does the following:
  262. 1. Open inbound mailslot to receive server's reply
  263. 2. Open outbound mailslot to domain server
  264. 3. Perpare a message to query the domain server
  265. 4. Send the message on the outbound mailslot
  266. 5. Wait 5 seconds for a reply; stop when a response is obtained.
  267. 6. Repeat 3, 4 and 5 three times if no repsonce
  268. Arguments:
  269. DomainName - Specifies the domain to query
  270. LookUpName - Specifies the name of the computer account that may be on
  271. the domain.
  272. Return Value:
  273. 1 if the account was found
  274. 0 if the account does not exist
  275. -1 if the domain did not respond
  276. --*/
  277. {
  278. BYTE Buffer[MAX_INBOUND_MESSAGE];
  279. CHAR InboundSlotSubName[MAX_MBCHAR_PATH];
  280. CHAR InboundSlotName[MAX_MBCHAR_PATH];
  281. CHAR OutboundSlotName[MAX_MBCHAR_PATH];
  282. PCSTR AnsiDomainName;
  283. PCSTR AnsiLookUpName;
  284. PCSTR AnsiLookUpNameWithDollar = NULL;
  285. HANDLE InboundSlot, OutboundSlot;
  286. INT OutData, InData;
  287. INT Size;
  288. INT Retry;
  289. BYTE OpCode;
  290. SCAN_STATE State = DOMAIN_NOT_FOUND;
  291. BOOL b;
  292. INT WriteRetries;
  293. static UINT Sequencer = 0;
  294. #ifdef PRERELEASE
  295. //
  296. // Stress mode: do not search the net
  297. //
  298. if (g_Stress) {
  299. DEBUGMSG ((DBG_WARNING, "Domain lookup skipped because g_Stress is TRUE"));
  300. return TRUE;
  301. }
  302. #endif
  303. //
  304. // Create an inbound mailslot
  305. //
  306. wsprintf (InboundSlotSubName, "\\MAILSLOT\\WIN9XUPG\\NETLOGON\\%u", Sequencer);
  307. InterlockedIncrement (&Sequencer);
  308. StringCopyA (InboundSlotName, "\\\\.");
  309. StringCatA (InboundSlotName, InboundSlotSubName);
  310. InboundSlot = CreateMailslotA (
  311. InboundSlotName,
  312. MAX_INBOUND_MESSAGE,
  313. 1000,
  314. NULL
  315. );
  316. if (InboundSlot == INVALID_HANDLE_VALUE) {
  317. LOG ((LOG_ERROR, "DoesComputerAccountExistOnDomain: Can't open %hs", InboundSlotName));
  318. return -1;
  319. }
  320. __try {
  321. if (WaitCursorEnable) {
  322. TurnOnWaitCursor();
  323. }
  324. //
  325. // Generate ANSI versions of domain and lookup names
  326. //
  327. AnsiDomainName = CreateDbcs (DomainName);
  328. AnsiLookUpName = CreateDbcs (LookUpName);
  329. __try {
  330. if (!AnsiDomainName || !AnsiLookUpName) {
  331. LOG ((LOG_ERROR, "Can't convert DomainName or LookUpName to ANSI"));
  332. __leave;
  333. }
  334. AnsiLookUpNameWithDollar = JoinTextA (AnsiLookUpName, "$");
  335. if (!AnsiLookUpNameWithDollar) {
  336. __leave;
  337. }
  338. //
  339. // Create outbound mailslot
  340. //
  341. pGenerateLogonMailslotNameA (OutboundSlotName, AnsiDomainName);
  342. OutboundSlot = CreateFileA (
  343. OutboundSlotName,
  344. GENERIC_WRITE,
  345. FILE_SHARE_READ,
  346. NULL,
  347. OPEN_EXISTING,
  348. FILE_ATTRIBUTE_NORMAL,
  349. NULL
  350. );
  351. if (OutboundSlot == INVALID_HANDLE_VALUE) {
  352. LOG ((LOG_ERROR, "Can't open %s", OutboundSlotName));
  353. __leave;
  354. }
  355. for (Retry = 0, State = DOMAIN_NOT_FOUND;
  356. State == DOMAIN_NOT_FOUND && Retry < PING_RETRY_MAX;
  357. Retry++
  358. ) {
  359. //
  360. // Generate message
  361. //
  362. Size = pBuildDomainPingMessageA (Buffer, AnsiLookUpNameWithDollar, InboundSlotSubName);
  363. if (Size > 0) {
  364. //
  365. // Send the message and wait for a response
  366. //
  367. WriteRetries = 20;
  368. do {
  369. b = WriteFile (OutboundSlot, Buffer, Size, (PDWORD) &OutData, NULL);
  370. if (!b || OutData != Size) {
  371. if (WriteRetries && GetLastError() == ERROR_NETWORK_BUSY) {
  372. b = TRUE;
  373. OutData = Size;
  374. WriteRetries--;
  375. Sleep (50);
  376. DEBUGMSG ((DBG_WARNING, "DoesComputerAccountExistOnDomain: Network busy! Retrying..."));
  377. } else {
  378. LOG ((LOG_ERROR, "Machine account query failed: can't write to network mailslot."));
  379. __leave;
  380. }
  381. }
  382. } while (!b || OutData != Size);
  383. //
  384. // Sit on mailslot for 5 seconds until data is available.
  385. // If no data comes back, assume failure.
  386. // If an unrecognized response comes back, wait for another response.
  387. //
  388. do {
  389. if (!WaitCursorEnable) {
  390. //
  391. // Only wait 1 second in search mode
  392. //
  393. Size = CheckForWaitingData (InboundSlot, sizeof (BYTE), 1000);
  394. } else {
  395. Size = CheckForWaitingData (InboundSlot, sizeof (BYTE), 5000);
  396. }
  397. if (Size > 0) {
  398. //
  399. // Response available!
  400. //
  401. if (!ReadFile (InboundSlot, Buffer, Size, (PDWORD) &InData, NULL)) {
  402. LOG ((LOG_ERROR, "Failed while reading from network mail slot."));
  403. __leave;
  404. }
  405. OpCode = *((PBYTE) Buffer);
  406. if (OpCode == LOGON_SAM_USER_UNKNOWN || OpCode == LOGON_SAM_USER_UNKNOWN_EX) {
  407. State = ACCOUNT_NOT_FOUND;
  408. } else if (OpCode == LOGON_SAM_LOGON_RESPONSE || OpCode == LOGON_SAM_LOGON_RESPONSE_EX) {
  409. State = ACCOUNT_FOUND;
  410. }
  411. }
  412. } while (State != ACCOUNT_FOUND && Size > 0);
  413. } else {
  414. DEBUGMSG ((DBG_WHOOPS, "Can't build domain ping message"));
  415. __leave;
  416. }
  417. }
  418. }
  419. __finally {
  420. FreeText (AnsiLookUpNameWithDollar);
  421. DestroyDbcs (AnsiDomainName); // this routine checks for NULL
  422. DestroyDbcs (AnsiLookUpName);
  423. }
  424. }
  425. __finally {
  426. CloseHandle (InboundSlot);
  427. if (WaitCursorEnable) {
  428. TurnOffWaitCursor();
  429. }
  430. }
  431. if (State == ACCOUNT_FOUND) {
  432. return 1;
  433. }
  434. if (State == ACCOUNT_NOT_FOUND) {
  435. return 0;
  436. }
  437. return -1;
  438. }
  439. BOOL
  440. EnumFirstNetResource (
  441. OUT PNETRESOURCE_ENUM EnumPtr,
  442. IN DWORD WNetScope, OPTIONAL
  443. IN DWORD WNetType, OPTIONAL
  444. IN DWORD WNetUsage OPTIONAL
  445. )
  446. /*++
  447. Routine Description:
  448. EnumFirstNetResource begins an enumeration of the network resources. It
  449. uses pEnumNetResourceWorker to do the enumeration.
  450. Arguments:
  451. EnumPtr - Receives the first enumerated network resource
  452. WNetScope - Specifies the RESOURCE_* flag to limit the enumeration. If zero,
  453. the default scope is RESOURCE_GLOBALNET.
  454. WNetType - Specifies the RESOURCETYPE_* flag(s) to limit the enumerattion.
  455. If zero, the default type is RESOURCETYPE_ANY.
  456. WNetUsage - Specifies the RESOURCEUSAGE_* flag(s) to limit the enumeration.
  457. If zero, the default usage is all resources.
  458. Return Value:
  459. TRUE if a network resource was enumerated, or FALSE if none were found.
  460. If return value is FALSE, GetLastError will return an error code, or
  461. ERROR_SUCCESS if all items were successfully enumerated.
  462. --*/
  463. {
  464. ZeroMemory (EnumPtr, sizeof (NETRESOURCE_ENUM));
  465. EnumPtr->State = NETRES_INIT;
  466. EnumPtr->EnumScope = WNetScope ? WNetScope : RESOURCE_GLOBALNET;
  467. EnumPtr->EnumType = WNetType ? WNetType : RESOURCETYPE_ANY;
  468. EnumPtr->EnumUsage = WNetUsage ? WNetUsage : 0; // 0 is "any"
  469. return pEnumNetResourceWorker (EnumPtr);
  470. }
  471. BOOL
  472. EnumNextNetResource (
  473. IN OUT PNETRESOURCE_ENUM EnumPtr
  474. )
  475. /*++
  476. Routine Description:
  477. EnumNextNetResource continues an enumeration of the network resources. It
  478. uses pEnumNetResourceWorker to do the enumeration.
  479. Arguments:
  480. EnumPtr - Specifies the previously enumerated item, receives the first
  481. enumerated network resource
  482. Return Value:
  483. TRUE if a network resource was enumerated, or FALSE if none were found.
  484. If return value is FALSE, GetLastError will return an error code, or
  485. ERROR_SUCCESS if all items were successfully enumerated.
  486. --*/
  487. {
  488. return pEnumNetResourceWorker (EnumPtr);
  489. }
  490. BOOL
  491. pEnumNetResourceWorker (
  492. IN OUT PNETRESOURCE_ENUM EnumPtr
  493. )
  494. /*++
  495. Routine Description:
  496. pEnumNetResourceWorker implements a state machine to enumerate network
  497. resources. The WNet APIs are used to do the enumeration. Each call
  498. to the WNetEnumResources function returns up to 64 items, but
  499. pEnumNetResourceWorker returns only one at a time. For this reason,
  500. a stack of handles and buffers are maintained by the state machine,
  501. simplifying the work for the caller.
  502. Arguments:
  503. EnumPtr - Specifies the current enumeration state, receives the next
  504. enumerated network resource
  505. Return Value:
  506. TRUE if a network resource was enumerated, or FALSE if none were found.
  507. If return value is FALSE, GetLastError will return an error code, or
  508. ERROR_SUCCESS if all items were successfully enumerated.
  509. --*/
  510. {
  511. LPNETRESOURCE CurrentResBase;
  512. LPNETRESOURCE CurrentRes;
  513. LPNETRESOURCE ParentRes;
  514. HANDLE CurrentHandle;
  515. UINT Entries;
  516. UINT Pos;
  517. DWORD rc;
  518. UINT Size;
  519. UINT u;
  520. for (;;) {
  521. u = EnumPtr->StackPos;
  522. Entries = EnumPtr->Entries[u];
  523. Pos = EnumPtr->Pos[u];
  524. CurrentResBase = (LPNETRESOURCE) EnumPtr->ResStack[u];
  525. CurrentRes = &CurrentResBase[Pos];
  526. CurrentHandle = EnumPtr->HandleStack[u];
  527. if (EnumPtr->StackPos) {
  528. ParentRes = (LPNETRESOURCE) EnumPtr->ResStack[EnumPtr->StackPos - 1];
  529. } else {
  530. ParentRes = NULL;
  531. }
  532. switch (EnumPtr->State) {
  533. case NETRES_INIT:
  534. EnumPtr->State = NETRES_OPEN_ENUM;
  535. break;
  536. case NETRES_OPEN_ENUM:
  537. EnumPtr->ResStack[EnumPtr->StackPos] = (PBYTE) MemAlloc (
  538. g_hHeap,
  539. 0,
  540. NETRES_INITIAL_SIZE
  541. );
  542. rc = WNetOpenEnum (
  543. EnumPtr->EnumScope,
  544. EnumPtr->EnumType,
  545. EnumPtr->EnumUsage,
  546. ParentRes,
  547. &CurrentHandle
  548. );
  549. if (rc != NO_ERROR) {
  550. AbortNetResourceEnum (EnumPtr);
  551. SetLastError (rc);
  552. LOG ((LOG_ERROR, "Failed to open network resource enumeration. (%u)", rc));
  553. return FALSE;
  554. }
  555. EnumPtr->HandleStack[EnumPtr->StackPos] = CurrentHandle;
  556. EnumPtr->State = NETRES_ENUM_BLOCK;
  557. break;
  558. case NETRES_ENUM_BLOCK:
  559. Entries = 64;
  560. Size = NETRES_INITIAL_SIZE;
  561. rc = WNetEnumResource (
  562. CurrentHandle,
  563. &Entries,
  564. (PBYTE) CurrentResBase,
  565. &Size
  566. );
  567. if (rc == ERROR_NO_MORE_ITEMS) {
  568. EnumPtr->State = NETRES_CLOSE_ENUM;
  569. break;
  570. }
  571. if (rc != NO_ERROR) {
  572. AbortNetResourceEnum (EnumPtr);
  573. SetLastError (rc);
  574. LOG ((LOG_ERROR, "Failure while enumerating network resources. (%u)", rc));
  575. return FALSE;
  576. }
  577. EnumPtr->Entries[EnumPtr->StackPos] = Entries;
  578. EnumPtr->Pos[EnumPtr->StackPos] = 0;
  579. EnumPtr->State = NETRES_RETURN_ITEM;
  580. break;
  581. case NETRES_RETURN_ITEM:
  582. EnumPtr->Connected = (CurrentRes->dwScope & RESOURCE_CONNECTED) != 0;
  583. EnumPtr->GlobalNet = (CurrentRes->dwScope & RESOURCE_GLOBALNET) != 0;
  584. EnumPtr->Persistent = (CurrentRes->dwScope & RESOURCE_REMEMBERED) != 0;
  585. EnumPtr->DiskResource = (CurrentRes->dwType & RESOURCETYPE_DISK) != 0;
  586. EnumPtr->PrintResource = (CurrentRes->dwType & RESOURCETYPE_PRINT) != 0;
  587. EnumPtr->TypeUnknown = (CurrentRes->dwType & RESOURCETYPE_ANY) != 0;
  588. EnumPtr->Domain = (CurrentRes->dwDisplayType & RESOURCEDISPLAYTYPE_DOMAIN) != 0;
  589. EnumPtr->Generic = (CurrentRes->dwDisplayType & RESOURCEDISPLAYTYPE_GENERIC) != 0;
  590. EnumPtr->Server = (CurrentRes->dwDisplayType & RESOURCEDISPLAYTYPE_SERVER) != 0;
  591. EnumPtr->Share = (CurrentRes->dwDisplayType & RESOURCEDISPLAYTYPE_SHARE) != 0;
  592. EnumPtr->Connectable = (CurrentRes->dwUsage & RESOURCEUSAGE_CONNECTABLE) != 0;
  593. EnumPtr->Container = (CurrentRes->dwUsage & RESOURCEUSAGE_CONTAINER) != 0;
  594. EnumPtr->RemoteName = CurrentRes->lpRemoteName ? CurrentRes->lpRemoteName : S_EMPTY;
  595. EnumPtr->LocalName = CurrentRes->lpLocalName ? CurrentRes->lpLocalName : S_EMPTY;
  596. EnumPtr->Comment = CurrentRes->lpComment;
  597. EnumPtr->Provider = CurrentRes->lpProvider;
  598. if (EnumPtr->Container) {
  599. //
  600. // Enum container resource
  601. //
  602. if (EnumPtr->StackPos + 1 < MAX_NETENUM_DEPTH) {
  603. EnumPtr->StackPos += 1;
  604. EnumPtr->State = NETRES_OPEN_ENUM;
  605. }
  606. }
  607. if (EnumPtr->State == NETRES_RETURN_ITEM) {
  608. EnumPtr->State = NETRES_ENUM_BLOCK_NEXT;
  609. }
  610. return TRUE;
  611. case NETRES_ENUM_BLOCK_NEXT:
  612. u = EnumPtr->StackPos;
  613. EnumPtr->Pos[u] += 1;
  614. if (EnumPtr->Pos[u] >= EnumPtr->Entries[u]) {
  615. EnumPtr->State = NETRES_ENUM_BLOCK;
  616. } else {
  617. EnumPtr->State = NETRES_RETURN_ITEM;
  618. }
  619. break;
  620. case NETRES_CLOSE_ENUM:
  621. WNetCloseEnum (CurrentHandle);
  622. MemFree (g_hHeap, 0, EnumPtr->ResStack[EnumPtr->StackPos]);
  623. if (!EnumPtr->StackPos) {
  624. EnumPtr->State = NETRES_DONE;
  625. break;
  626. }
  627. EnumPtr->StackPos -= 1;
  628. EnumPtr->State = NETRES_ENUM_BLOCK_NEXT;
  629. break;
  630. case NETRES_DONE:
  631. SetLastError (ERROR_SUCCESS);
  632. return FALSE;
  633. }
  634. }
  635. }
  636. VOID
  637. AbortNetResourceEnum (
  638. IN OUT PNETRESOURCE_ENUM EnumPtr
  639. )
  640. /*++
  641. Routine Description:
  642. AbortNetResourceEnum cleans up all allocated memory and open handles,
  643. and then sets the enumeration state to NETRES_DONE to stop any
  644. subsequent enumeration.
  645. If enumeration has already completed or was previously aborted, this
  646. routine simply returns without doing anything.
  647. Arguments:
  648. EnumPtr - Specifies the enumeration to stop, receives an enumeration
  649. structure that will not enumerate any more items unless it
  650. is given back to EnumFirstNetResource.
  651. Return Value:
  652. none
  653. --*/
  654. {
  655. UINT u;
  656. if (EnumPtr->State == NETRES_DONE) {
  657. return;
  658. }
  659. for (u = 0 ; u <= EnumPtr->StackPos ; u++) {
  660. WNetCloseEnum (EnumPtr->HandleStack[u]);
  661. MemFree (g_hHeap, 0, EnumPtr->ResStack[u]);
  662. }
  663. EnumPtr->State = NETRES_DONE;
  664. }