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.

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