920 lines
24 KiB
C
920 lines
24 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1998 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
compacct.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Implements DoesComputerAccountExistOnDomain, which determines if a computer
|
||
|
account exists given an NT domain and computer name. This is used to
|
||
|
warn the user if they are going to be joining an NT domain but do not have
|
||
|
a computer account ready.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Jim Schmidt (jimschm) 02-Jan-1998
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
jimschm 23-Sep-1998 Added 20 retries for datagram write
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "pch.h"
|
||
|
#include <netlogon.h> // private\inc
|
||
|
|
||
|
//
|
||
|
// Contants from sdk\inc\ntsam.h -- copied here because ntsam.h redefines things
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// User account control flags...
|
||
|
//
|
||
|
|
||
|
#define USER_ACCOUNT_DISABLED (0x00000001)
|
||
|
#define USER_HOME_DIRECTORY_REQUIRED (0x00000002)
|
||
|
#define USER_PASSWORD_NOT_REQUIRED (0x00000004)
|
||
|
#define USER_TEMP_DUPLICATE_ACCOUNT (0x00000008)
|
||
|
#define USER_NORMAL_ACCOUNT (0x00000010)
|
||
|
#define USER_MNS_LOGON_ACCOUNT (0x00000020)
|
||
|
#define USER_INTERDOMAIN_TRUST_ACCOUNT (0x00000040)
|
||
|
#define USER_WORKSTATION_TRUST_ACCOUNT (0x00000080)
|
||
|
#define USER_SERVER_TRUST_ACCOUNT (0x00000100)
|
||
|
#define USER_DONT_EXPIRE_PASSWORD (0x00000200)
|
||
|
#define USER_ACCOUNT_AUTO_LOCKED (0x00000400)
|
||
|
#define USER_ENCRYPTED_TEXT_PASSWORD_ALLOWED (0x00000800)
|
||
|
#define USER_SMARTCARD_REQUIRED (0x00001000)
|
||
|
|
||
|
|
||
|
#define USER_MACHINE_ACCOUNT_MASK \
|
||
|
( USER_INTERDOMAIN_TRUST_ACCOUNT |\
|
||
|
USER_WORKSTATION_TRUST_ACCOUNT |\
|
||
|
USER_SERVER_TRUST_ACCOUNT)
|
||
|
|
||
|
#define USER_ACCOUNT_TYPE_MASK \
|
||
|
( USER_TEMP_DUPLICATE_ACCOUNT |\
|
||
|
USER_NORMAL_ACCOUNT |\
|
||
|
USER_MACHINE_ACCOUNT_MASK )
|
||
|
|
||
|
|
||
|
//
|
||
|
// Defines
|
||
|
//
|
||
|
|
||
|
#define LM20_TOKENBYTE 0xFF // net\inc\logonp.h
|
||
|
#define LMNT_TOKENBYTE 0xFF
|
||
|
|
||
|
#define MAX_INBOUND_MESSAGE 400
|
||
|
#define PING_RETRY_MAX 3 // number of attempts made against domain
|
||
|
|
||
|
#define NETRES_INITIAL_SIZE 16384
|
||
|
|
||
|
|
||
|
//
|
||
|
// Types
|
||
|
//
|
||
|
|
||
|
typedef enum {
|
||
|
ACCOUNT_FOUND,
|
||
|
ACCOUNT_NOT_FOUND,
|
||
|
DOMAIN_NOT_FOUND
|
||
|
} SCAN_STATE;
|
||
|
|
||
|
//
|
||
|
// Local prototypes
|
||
|
//
|
||
|
|
||
|
BOOL
|
||
|
pEnumNetResourceWorker (
|
||
|
IN OUT PNETRESOURCE_ENUM EnumPtr
|
||
|
);
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Implementation
|
||
|
//
|
||
|
|
||
|
VOID
|
||
|
pGenerateLogonMailslotNameA (
|
||
|
OUT PSTR SlotName,
|
||
|
IN PCSTR DomainName
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
pGenerateLogonMailslotNameA creates the mailslot name needed to query
|
||
|
an NT domain server. It uses an undocumented syntax to open a mailslot
|
||
|
to DomainName with the 16th character 1Ch.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
SlotName - Receives the mailslot name. Should be a MAX_PATH buffer.
|
||
|
|
||
|
DomainName - Specifies the name of the domain to query.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
none
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
StringCopyA (SlotName, "\\\\");
|
||
|
StringCatA (SlotName, DomainName);
|
||
|
StringCatA (SlotName, "*");
|
||
|
StringCatA (SlotName, NETLOGON_NT_MAILSLOT_A);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
PSTR
|
||
|
pAppendStringA (
|
||
|
OUT PSTR Buffer,
|
||
|
IN PCSTR Source
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
pAppendStringA appends the specified string in Source to the specified
|
||
|
Buffer. The entire string, including the nul, is copied. The return
|
||
|
value points to the character after the nul in Buffer.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Buffer - Receives the copy of Source, up to and including the nul.
|
||
|
|
||
|
Source - Specifies the nul-terminated string to copy.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
A pointer to the next character after the newly copied string in Buffer.
|
||
|
The caller will use this pointer for additional append operations.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
while (*Source) {
|
||
|
*Buffer++ = *Source++;
|
||
|
}
|
||
|
|
||
|
*Buffer++ = 0;
|
||
|
|
||
|
return Buffer;
|
||
|
}
|
||
|
|
||
|
|
||
|
PWSTR
|
||
|
pAppendStringW (
|
||
|
OUT PWSTR Buffer,
|
||
|
IN PCWSTR Source
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
pAppendStringW appends the specified string in Source to the specified
|
||
|
Buffer. The entire string, including the nul, is copied. The return
|
||
|
value points to the character after the nul in Buffer.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Buffer - Receives the copy of Source, up to and including the nul.
|
||
|
|
||
|
Source - Specifies the nul-terminated string to copy.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
A pointer to the next character after the newly copied string in Buffer.
|
||
|
The caller will use this pointer for additional append operations.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
while (*Source) {
|
||
|
*Buffer++ = *Source++;
|
||
|
}
|
||
|
|
||
|
*Buffer++ = 0;
|
||
|
|
||
|
return Buffer;
|
||
|
}
|
||
|
|
||
|
|
||
|
PBYTE
|
||
|
pAppendBytes (
|
||
|
OUT PBYTE Buffer,
|
||
|
IN PBYTE Source,
|
||
|
IN UINT Len
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
pAppendBytes appends the specified block of data in Source to the specified
|
||
|
Buffer. Len specifies the size of Source. The return value points to
|
||
|
the byte after the copied block of data in Buffer.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Buffer - Receives the copy of Source
|
||
|
|
||
|
Source - Specifies the block of data to copy
|
||
|
|
||
|
Len - Specifies the number of bytes in Source
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
A pointer to the next byte after the newly copied blcok of data in Buffer.
|
||
|
The caller will use this pointer for additional append operations.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
while (Len > 0) {
|
||
|
*Buffer++ = *Source++;
|
||
|
Len--;
|
||
|
}
|
||
|
|
||
|
return Buffer;
|
||
|
}
|
||
|
|
||
|
|
||
|
INT
|
||
|
pBuildDomainPingMessageA (
|
||
|
OUT PBYTE Buffer, // must be sizeof (NETLOGON_SAM_LOGON_REQUEST) + sizeof (DWORD)
|
||
|
IN PCSTR LookUpName,
|
||
|
IN PCSTR ReplySlotName
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
pBuildDomainPingMessageA generates a SAM logon SMB that can be sent to
|
||
|
the NT domain server's NTLOGON mailslot. If the server receives this
|
||
|
message, it will reply with either LOGON_SAM_USER_UNKNOWN, LOGON_SAM_LOGON_RESPONSE
|
||
|
or LOGON_SAM_LOGON_PAUSED.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Buffer - Receives the SMB message
|
||
|
|
||
|
LookUpName - Specifies the name of the computer account that may be on
|
||
|
the domain. (The domain is specified by the mailslot name.)
|
||
|
|
||
|
ReplySlotName - Specifies the name of an open mailslot that will receive the
|
||
|
server's response, if any.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
The number of bytes used in Buffer, or zero if an error occurred, such as
|
||
|
out of memory.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
CHAR ComputerName[MAX_COMPUTER_NAMEA];
|
||
|
DWORD Size;
|
||
|
PNETLOGON_SAM_LOGON_REQUEST SamLogonRequest;
|
||
|
PSTR p;
|
||
|
DWORD ControlBits;
|
||
|
DWORD DomainSidSize;
|
||
|
DWORD NtVersion;
|
||
|
BYTE NtTokenByte;
|
||
|
BYTE LmTokenByte;
|
||
|
PCWSTR UnicodeComputerName;
|
||
|
PCWSTR UnicodeLookUpName;
|
||
|
|
||
|
//
|
||
|
// Get computer name
|
||
|
//
|
||
|
|
||
|
Size = sizeof (ComputerName) / sizeof (ComputerName[0]);
|
||
|
if (!GetComputerNameA (ComputerName, &Size)) {
|
||
|
LOG ((LOG_ERROR, "Can't get computer name."));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create unicode strings
|
||
|
//
|
||
|
|
||
|
UnicodeComputerName = CreateUnicode (ComputerName);
|
||
|
if (!UnicodeComputerName) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
UnicodeLookUpName = CreateUnicode (LookUpName);
|
||
|
if (!UnicodeLookUpName) {
|
||
|
DestroyUnicode (UnicodeComputerName);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Init pointers
|
||
|
//
|
||
|
|
||
|
SamLogonRequest = (PNETLOGON_SAM_LOGON_REQUEST) Buffer;
|
||
|
p = (PSTR) (SamLogonRequest->UnicodeComputerName);
|
||
|
|
||
|
//
|
||
|
// Initialize request packet
|
||
|
//
|
||
|
|
||
|
SamLogonRequest->Opcode = LOGON_SAM_LOGON_REQUEST;
|
||
|
SamLogonRequest->RequestCount = 0;
|
||
|
|
||
|
//
|
||
|
// Append the rest of the params together
|
||
|
//
|
||
|
|
||
|
p = (PSTR) pAppendStringW ((PWSTR) p, UnicodeComputerName);
|
||
|
p = (PSTR) pAppendStringW ((PWSTR) p, UnicodeLookUpName);
|
||
|
p = pAppendStringA (p, ReplySlotName);
|
||
|
|
||
|
ControlBits = USER_MACHINE_ACCOUNT_MASK;
|
||
|
p = (PSTR) pAppendBytes ((PBYTE) p, (PBYTE) (&ControlBits), sizeof (DWORD));
|
||
|
|
||
|
DomainSidSize = 0;
|
||
|
p = (PSTR) pAppendBytes ((PBYTE) p, (PBYTE) (&DomainSidSize), sizeof (DWORD));
|
||
|
|
||
|
NtVersion = NETLOGON_NT_VERSION_1;
|
||
|
p = (PSTR) pAppendBytes ((PBYTE) p, (PBYTE) (&NtVersion), sizeof (DWORD));
|
||
|
|
||
|
NtTokenByte = LMNT_TOKENBYTE;
|
||
|
LmTokenByte = LM20_TOKENBYTE;
|
||
|
|
||
|
p = (PSTR) pAppendBytes ((PBYTE) p, &NtTokenByte, sizeof (BYTE));
|
||
|
p = (PSTR) pAppendBytes ((PBYTE) p, &NtTokenByte, sizeof (BYTE));
|
||
|
p = (PSTR) pAppendBytes ((PBYTE) p, &LmTokenByte, sizeof (BYTE));
|
||
|
p = (PSTR) pAppendBytes ((PBYTE) p, &LmTokenByte, sizeof (BYTE));
|
||
|
|
||
|
DestroyUnicode (UnicodeComputerName);
|
||
|
DestroyUnicode (UnicodeLookUpName);
|
||
|
|
||
|
return p - Buffer;
|
||
|
}
|
||
|
|
||
|
|
||
|
LONG
|
||
|
DoesComputerAccountExistOnDomain (
|
||
|
IN PCTSTR DomainName,
|
||
|
IN PCTSTR LookUpName,
|
||
|
IN BOOL WaitCursorEnable
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
DoesComputerAccountExistOnDomain queries a domain for the existence of
|
||
|
a computer account. It does the following:
|
||
|
|
||
|
1. Open inbound mailslot to receive server's reply
|
||
|
2. Open outbound mailslot to domain server
|
||
|
3. Perpare a message to query the domain server
|
||
|
4. Send the message on the outbound mailslot
|
||
|
5. Wait 5 seconds for a reply; stop when a response is obtained.
|
||
|
6. Repeat 3, 4 and 5 three times if no repsonce
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DomainName - Specifies the domain to query
|
||
|
|
||
|
LookUpName - Specifies the name of the computer account that may be on
|
||
|
the domain.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
1 if the account was found
|
||
|
0 if the account does not exist
|
||
|
-1 if the domain did not respond
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
BYTE Buffer[MAX_INBOUND_MESSAGE];
|
||
|
CHAR InboundSlotSubName[MAX_MBCHAR_PATH];
|
||
|
CHAR InboundSlotName[MAX_MBCHAR_PATH];
|
||
|
CHAR OutboundSlotName[MAX_MBCHAR_PATH];
|
||
|
PCSTR AnsiDomainName;
|
||
|
PCSTR AnsiLookUpName;
|
||
|
PCSTR AnsiLookUpNameWithDollar = NULL;
|
||
|
HANDLE InboundSlot, OutboundSlot;
|
||
|
INT OutData, InData;
|
||
|
INT Size;
|
||
|
INT Retry;
|
||
|
BYTE OpCode;
|
||
|
SCAN_STATE State = DOMAIN_NOT_FOUND;
|
||
|
BOOL b;
|
||
|
INT WriteRetries;
|
||
|
static UINT Sequencer = 0;
|
||
|
|
||
|
#ifdef PRERELEASE
|
||
|
//
|
||
|
// Stress mode: do not search the net
|
||
|
//
|
||
|
|
||
|
if (g_Stress) {
|
||
|
DEBUGMSG ((DBG_WARNING, "Domain lookup skipped because g_Stress is TRUE"));
|
||
|
return TRUE;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Create an inbound mailslot
|
||
|
//
|
||
|
|
||
|
wsprintf (InboundSlotSubName, "\\MAILSLOT\\WIN9XUPG\\NETLOGON\\%u", Sequencer);
|
||
|
InterlockedIncrement (&Sequencer);
|
||
|
|
||
|
StringCopyA (InboundSlotName, "\\\\.");
|
||
|
StringCatA (InboundSlotName, InboundSlotSubName);
|
||
|
|
||
|
InboundSlot = CreateMailslotA (
|
||
|
InboundSlotName,
|
||
|
MAX_INBOUND_MESSAGE,
|
||
|
1000,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
if (InboundSlot == INVALID_HANDLE_VALUE) {
|
||
|
LOG ((LOG_ERROR, "DoesComputerAccountExistOnDomain: Can't open %hs", InboundSlotName));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
__try {
|
||
|
if (WaitCursorEnable) {
|
||
|
TurnOnWaitCursor();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Generate ANSI versions of domain and lookup names
|
||
|
//
|
||
|
|
||
|
AnsiDomainName = CreateDbcs (DomainName);
|
||
|
AnsiLookUpName = CreateDbcs (LookUpName);
|
||
|
|
||
|
__try {
|
||
|
if (!AnsiDomainName || !AnsiLookUpName) {
|
||
|
LOG ((LOG_ERROR, "Can't convert DomainName or LookUpName to ANSI"));
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
AnsiLookUpNameWithDollar = JoinTextA (AnsiLookUpName, "$");
|
||
|
if (!AnsiLookUpNameWithDollar) {
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create outbound mailslot
|
||
|
//
|
||
|
|
||
|
pGenerateLogonMailslotNameA (OutboundSlotName, AnsiDomainName);
|
||
|
|
||
|
OutboundSlot = CreateFileA (
|
||
|
OutboundSlotName,
|
||
|
GENERIC_WRITE,
|
||
|
FILE_SHARE_READ,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
if (OutboundSlot == INVALID_HANDLE_VALUE) {
|
||
|
LOG ((LOG_ERROR, "Can't open %s", OutboundSlotName));
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
for (Retry = 0, State = DOMAIN_NOT_FOUND;
|
||
|
State == DOMAIN_NOT_FOUND && Retry < PING_RETRY_MAX;
|
||
|
Retry++
|
||
|
) {
|
||
|
|
||
|
//
|
||
|
// Generate message
|
||
|
//
|
||
|
|
||
|
Size = pBuildDomainPingMessageA (Buffer, AnsiLookUpNameWithDollar, InboundSlotSubName);
|
||
|
|
||
|
if (Size > 0) {
|
||
|
//
|
||
|
// Send the message and wait for a response
|
||
|
//
|
||
|
|
||
|
WriteRetries = 20;
|
||
|
do {
|
||
|
b = WriteFile (OutboundSlot, Buffer, Size, (PDWORD) &OutData, NULL);
|
||
|
|
||
|
if (!b || OutData != Size) {
|
||
|
if (WriteRetries && GetLastError() == ERROR_NETWORK_BUSY) {
|
||
|
b = TRUE;
|
||
|
OutData = Size;
|
||
|
WriteRetries--;
|
||
|
Sleep (50);
|
||
|
|
||
|
DEBUGMSG ((DBG_WARNING, "DoesComputerAccountExistOnDomain: Network busy! Retrying..."));
|
||
|
|
||
|
} else {
|
||
|
LOG ((LOG_ERROR, "Machine account query failed: can't write to network mailslot."));
|
||
|
__leave;
|
||
|
}
|
||
|
}
|
||
|
} while (!b || OutData != Size);
|
||
|
|
||
|
//
|
||
|
// Sit on mailslot for 5 seconds until data is available.
|
||
|
// If no data comes back, assume failure.
|
||
|
// If an unrecognized response comes back, wait for another response.
|
||
|
//
|
||
|
|
||
|
do {
|
||
|
if (!WaitCursorEnable) {
|
||
|
//
|
||
|
// Only wait 1 second in search mode
|
||
|
//
|
||
|
|
||
|
Size = CheckForWaitingData (InboundSlot, sizeof (BYTE), 1000);
|
||
|
} else {
|
||
|
Size = CheckForWaitingData (InboundSlot, sizeof (BYTE), 5000);
|
||
|
}
|
||
|
|
||
|
if (Size > 0) {
|
||
|
//
|
||
|
// Response available!
|
||
|
//
|
||
|
|
||
|
if (!ReadFile (InboundSlot, Buffer, Size, (PDWORD) &InData, NULL)) {
|
||
|
LOG ((LOG_ERROR, "Failed while reading from network mail slot."));
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
OpCode = *((PBYTE) Buffer);
|
||
|
|
||
|
if (OpCode == LOGON_SAM_USER_UNKNOWN || OpCode == LOGON_SAM_USER_UNKNOWN_EX) {
|
||
|
State = ACCOUNT_NOT_FOUND;
|
||
|
} else if (OpCode == LOGON_SAM_LOGON_RESPONSE || OpCode == LOGON_SAM_LOGON_RESPONSE_EX) {
|
||
|
State = ACCOUNT_FOUND;
|
||
|
}
|
||
|
}
|
||
|
} while (State != ACCOUNT_FOUND && Size > 0);
|
||
|
|
||
|
} else {
|
||
|
DEBUGMSG ((DBG_WHOOPS, "Can't build domain ping message"));
|
||
|
__leave;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
__finally {
|
||
|
FreeText (AnsiLookUpNameWithDollar);
|
||
|
DestroyDbcs (AnsiDomainName); // this routine checks for NULL
|
||
|
DestroyDbcs (AnsiLookUpName);
|
||
|
}
|
||
|
}
|
||
|
__finally {
|
||
|
CloseHandle (InboundSlot);
|
||
|
if (WaitCursorEnable) {
|
||
|
TurnOffWaitCursor();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (State == ACCOUNT_FOUND) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (State == ACCOUNT_NOT_FOUND) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
EnumFirstNetResource (
|
||
|
OUT PNETRESOURCE_ENUM EnumPtr,
|
||
|
IN DWORD WNetScope, OPTIONAL
|
||
|
IN DWORD WNetType, OPTIONAL
|
||
|
IN DWORD WNetUsage OPTIONAL
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
EnumFirstNetResource begins an enumeration of the network resources. It
|
||
|
uses pEnumNetResourceWorker to do the enumeration.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
EnumPtr - Receives the first enumerated network resource
|
||
|
|
||
|
WNetScope - Specifies the RESOURCE_* flag to limit the enumeration. If zero,
|
||
|
the default scope is RESOURCE_GLOBALNET.
|
||
|
|
||
|
WNetType - Specifies the RESOURCETYPE_* flag(s) to limit the enumerattion.
|
||
|
If zero, the default type is RESOURCETYPE_ANY.
|
||
|
|
||
|
WNetUsage - Specifies the RESOURCEUSAGE_* flag(s) to limit the enumeration.
|
||
|
If zero, the default usage is all resources.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if a network resource was enumerated, or FALSE if none were found.
|
||
|
If return value is FALSE, GetLastError will return an error code, or
|
||
|
ERROR_SUCCESS if all items were successfully enumerated.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
ZeroMemory (EnumPtr, sizeof (NETRESOURCE_ENUM));
|
||
|
EnumPtr->State = NETRES_INIT;
|
||
|
EnumPtr->EnumScope = WNetScope ? WNetScope : RESOURCE_GLOBALNET;
|
||
|
EnumPtr->EnumType = WNetType ? WNetType : RESOURCETYPE_ANY;
|
||
|
EnumPtr->EnumUsage = WNetUsage ? WNetUsage : 0; // 0 is "any"
|
||
|
|
||
|
return pEnumNetResourceWorker (EnumPtr);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
EnumNextNetResource (
|
||
|
IN OUT PNETRESOURCE_ENUM EnumPtr
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
EnumNextNetResource continues an enumeration of the network resources. It
|
||
|
uses pEnumNetResourceWorker to do the enumeration.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
EnumPtr - Specifies the previously enumerated item, receives the first
|
||
|
enumerated network resource
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if a network resource was enumerated, or FALSE if none were found.
|
||
|
If return value is FALSE, GetLastError will return an error code, or
|
||
|
ERROR_SUCCESS if all items were successfully enumerated.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
return pEnumNetResourceWorker (EnumPtr);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
pEnumNetResourceWorker (
|
||
|
IN OUT PNETRESOURCE_ENUM EnumPtr
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
pEnumNetResourceWorker implements a state machine to enumerate network
|
||
|
resources. The WNet APIs are used to do the enumeration. Each call
|
||
|
to the WNetEnumResources function returns up to 64 items, but
|
||
|
pEnumNetResourceWorker returns only one at a time. For this reason,
|
||
|
a stack of handles and buffers are maintained by the state machine,
|
||
|
simplifying the work for the caller.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
EnumPtr - Specifies the current enumeration state, receives the next
|
||
|
enumerated network resource
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if a network resource was enumerated, or FALSE if none were found.
|
||
|
If return value is FALSE, GetLastError will return an error code, or
|
||
|
ERROR_SUCCESS if all items were successfully enumerated.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
LPNETRESOURCE CurrentResBase;
|
||
|
LPNETRESOURCE CurrentRes;
|
||
|
LPNETRESOURCE ParentRes;
|
||
|
HANDLE CurrentHandle;
|
||
|
UINT Entries;
|
||
|
UINT Pos;
|
||
|
DWORD rc;
|
||
|
UINT Size;
|
||
|
UINT u;
|
||
|
|
||
|
for (;;) {
|
||
|
u = EnumPtr->StackPos;
|
||
|
|
||
|
Entries = EnumPtr->Entries[u];
|
||
|
Pos = EnumPtr->Pos[u];
|
||
|
CurrentResBase = (LPNETRESOURCE) EnumPtr->ResStack[u];
|
||
|
CurrentRes = &CurrentResBase[Pos];
|
||
|
CurrentHandle = EnumPtr->HandleStack[u];
|
||
|
|
||
|
if (EnumPtr->StackPos) {
|
||
|
ParentRes = (LPNETRESOURCE) EnumPtr->ResStack[EnumPtr->StackPos - 1];
|
||
|
} else {
|
||
|
ParentRes = NULL;
|
||
|
}
|
||
|
|
||
|
switch (EnumPtr->State) {
|
||
|
|
||
|
case NETRES_INIT:
|
||
|
EnumPtr->State = NETRES_OPEN_ENUM;
|
||
|
break;
|
||
|
|
||
|
case NETRES_OPEN_ENUM:
|
||
|
EnumPtr->ResStack[EnumPtr->StackPos] = (PBYTE) MemAlloc (
|
||
|
g_hHeap,
|
||
|
0,
|
||
|
NETRES_INITIAL_SIZE
|
||
|
);
|
||
|
|
||
|
rc = WNetOpenEnum (
|
||
|
EnumPtr->EnumScope,
|
||
|
EnumPtr->EnumType,
|
||
|
EnumPtr->EnumUsage,
|
||
|
ParentRes,
|
||
|
&CurrentHandle
|
||
|
);
|
||
|
|
||
|
if (rc != NO_ERROR) {
|
||
|
AbortNetResourceEnum (EnumPtr);
|
||
|
SetLastError (rc);
|
||
|
LOG ((LOG_ERROR, "Failed to open network resource enumeration. (%u)", rc));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
EnumPtr->HandleStack[EnumPtr->StackPos] = CurrentHandle;
|
||
|
EnumPtr->State = NETRES_ENUM_BLOCK;
|
||
|
break;
|
||
|
|
||
|
case NETRES_ENUM_BLOCK:
|
||
|
Entries = 64;
|
||
|
Size = NETRES_INITIAL_SIZE;
|
||
|
|
||
|
rc = WNetEnumResource (
|
||
|
CurrentHandle,
|
||
|
&Entries,
|
||
|
(PBYTE) CurrentResBase,
|
||
|
&Size
|
||
|
);
|
||
|
|
||
|
if (rc == ERROR_NO_MORE_ITEMS) {
|
||
|
EnumPtr->State = NETRES_CLOSE_ENUM;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (rc != NO_ERROR) {
|
||
|
AbortNetResourceEnum (EnumPtr);
|
||
|
SetLastError (rc);
|
||
|
LOG ((LOG_ERROR, "Failure while enumerating network resources. (%u)", rc));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
EnumPtr->Entries[EnumPtr->StackPos] = Entries;
|
||
|
EnumPtr->Pos[EnumPtr->StackPos] = 0;
|
||
|
EnumPtr->State = NETRES_RETURN_ITEM;
|
||
|
break;
|
||
|
|
||
|
case NETRES_RETURN_ITEM:
|
||
|
EnumPtr->Connected = (CurrentRes->dwScope & RESOURCE_CONNECTED) != 0;
|
||
|
EnumPtr->GlobalNet = (CurrentRes->dwScope & RESOURCE_GLOBALNET) != 0;
|
||
|
EnumPtr->Persistent = (CurrentRes->dwScope & RESOURCE_REMEMBERED) != 0;
|
||
|
|
||
|
EnumPtr->DiskResource = (CurrentRes->dwType & RESOURCETYPE_DISK) != 0;
|
||
|
EnumPtr->PrintResource = (CurrentRes->dwType & RESOURCETYPE_PRINT) != 0;
|
||
|
EnumPtr->TypeUnknown = (CurrentRes->dwType & RESOURCETYPE_ANY) != 0;
|
||
|
|
||
|
EnumPtr->Domain = (CurrentRes->dwDisplayType & RESOURCEDISPLAYTYPE_DOMAIN) != 0;
|
||
|
EnumPtr->Generic = (CurrentRes->dwDisplayType & RESOURCEDISPLAYTYPE_GENERIC) != 0;
|
||
|
EnumPtr->Server = (CurrentRes->dwDisplayType & RESOURCEDISPLAYTYPE_SERVER) != 0;
|
||
|
EnumPtr->Share = (CurrentRes->dwDisplayType & RESOURCEDISPLAYTYPE_SHARE) != 0;
|
||
|
|
||
|
EnumPtr->Connectable = (CurrentRes->dwUsage & RESOURCEUSAGE_CONNECTABLE) != 0;
|
||
|
EnumPtr->Container = (CurrentRes->dwUsage & RESOURCEUSAGE_CONTAINER) != 0;
|
||
|
|
||
|
EnumPtr->RemoteName = CurrentRes->lpRemoteName ? CurrentRes->lpRemoteName : S_EMPTY;
|
||
|
EnumPtr->LocalName = CurrentRes->lpLocalName ? CurrentRes->lpLocalName : S_EMPTY;
|
||
|
EnumPtr->Comment = CurrentRes->lpComment;
|
||
|
EnumPtr->Provider = CurrentRes->lpProvider;
|
||
|
|
||
|
if (EnumPtr->Container) {
|
||
|
//
|
||
|
// Enum container resource
|
||
|
//
|
||
|
|
||
|
if (EnumPtr->StackPos + 1 < MAX_NETENUM_DEPTH) {
|
||
|
EnumPtr->StackPos += 1;
|
||
|
EnumPtr->State = NETRES_OPEN_ENUM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (EnumPtr->State == NETRES_RETURN_ITEM) {
|
||
|
EnumPtr->State = NETRES_ENUM_BLOCK_NEXT;
|
||
|
}
|
||
|
return TRUE;
|
||
|
|
||
|
case NETRES_ENUM_BLOCK_NEXT:
|
||
|
u = EnumPtr->StackPos;
|
||
|
EnumPtr->Pos[u] += 1;
|
||
|
if (EnumPtr->Pos[u] >= EnumPtr->Entries[u]) {
|
||
|
EnumPtr->State = NETRES_ENUM_BLOCK;
|
||
|
} else {
|
||
|
EnumPtr->State = NETRES_RETURN_ITEM;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case NETRES_CLOSE_ENUM:
|
||
|
WNetCloseEnum (CurrentHandle);
|
||
|
MemFree (g_hHeap, 0, EnumPtr->ResStack[EnumPtr->StackPos]);
|
||
|
|
||
|
if (!EnumPtr->StackPos) {
|
||
|
EnumPtr->State = NETRES_DONE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
EnumPtr->StackPos -= 1;
|
||
|
EnumPtr->State = NETRES_ENUM_BLOCK_NEXT;
|
||
|
break;
|
||
|
|
||
|
case NETRES_DONE:
|
||
|
SetLastError (ERROR_SUCCESS);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
AbortNetResourceEnum (
|
||
|
IN OUT PNETRESOURCE_ENUM EnumPtr
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
AbortNetResourceEnum cleans up all allocated memory and open handles,
|
||
|
and then sets the enumeration state to NETRES_DONE to stop any
|
||
|
subsequent enumeration.
|
||
|
|
||
|
If enumeration has already completed or was previously aborted, this
|
||
|
routine simply returns without doing anything.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
EnumPtr - Specifies the enumeration to stop, receives an enumeration
|
||
|
structure that will not enumerate any more items unless it
|
||
|
is given back to EnumFirstNetResource.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
none
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
UINT u;
|
||
|
|
||
|
if (EnumPtr->State == NETRES_DONE) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (u = 0 ; u <= EnumPtr->StackPos ; u++) {
|
||
|
WNetCloseEnum (EnumPtr->HandleStack[u]);
|
||
|
MemFree (g_hHeap, 0, EnumPtr->ResStack[u]);
|
||
|
}
|
||
|
|
||
|
EnumPtr->State = NETRES_DONE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|