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.
1576 lines
42 KiB
1576 lines
42 KiB
/*++
|
|
|
|
Copyright (c) 1991-1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
utils.c
|
|
|
|
Abstract:
|
|
|
|
Utility routines for the browser service.
|
|
|
|
Author:
|
|
|
|
Larry Osterman (LarryO) 23-Mar-1992
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#undef IF_DEBUG // avoid wsclient.h vs. debuglib.h conflicts.
|
|
#include <nt.h> // DbgPrint prototype
|
|
#include <ntrtl.h> // DbgPrint
|
|
#include <nturtl.h> // Needed by winbase.h
|
|
|
|
#include <windef.h> // DWORD
|
|
#include <winbase.h> // LocalFree
|
|
#include <winreg.h>
|
|
|
|
#include <rpc.h> // DataTypes and runtime APIs
|
|
#include <rpcutil.h> // GENERIC_ENUM_STRUCT
|
|
|
|
#include <lmcons.h> // NET_API_STATUS
|
|
#include <lmerr.h> // NetError codes
|
|
#include <lmremutl.h> // SUPPORTS_RPC
|
|
|
|
#include <netlib.h> // NetpNtStatusToApiStatus
|
|
#include <netlibnt.h> // NetpNtStatusToApiStatus
|
|
#include <netdebug.h>
|
|
|
|
#include <bowser.h> // generated by the MIDL complier
|
|
#include <brnames.h> // Service and interface names
|
|
|
|
#include <winsvc.h>
|
|
|
|
#include <debuglib.h> // IF_DEBUG() (needed by netrpc.h).
|
|
#include <lmserver.h>
|
|
#include <align.h>
|
|
#include <tstr.h>
|
|
|
|
#include <ntddbrow.h>
|
|
#include <brcommon.h> // Routines common between client & server
|
|
|
|
#include <nb30.h>
|
|
#include <hostannc.h>
|
|
|
|
#include <winnls.h>
|
|
|
|
|
|
#ifdef ENABLE_PSEUDO_BROWSER
|
|
#include <config.h> // for LPNET_CONFIG_HANDLE & friends
|
|
#include <confname.h> // for SECT_NT_BROWSER
|
|
#endif
|
|
|
|
// begin_setup
|
|
|
|
|
|
//
|
|
// Buffer allocation size for enumeration output buffer.
|
|
//
|
|
#define INITIAL_ALLOCATION_SIZE 48*1024 // First attempt size (48K)
|
|
#define FUDGE_FACTOR_SIZE 1024 // Second try TotalBytesNeeded
|
|
// plus this amount
|
|
|
|
//
|
|
// prototypes
|
|
//
|
|
|
|
#ifdef ENABLE_PSEUDO_BROWSER
|
|
DWORD
|
|
IsBrowserEnabled(
|
|
IN OPTIONAL LPTSTR Section,
|
|
IN LPTSTR Key,
|
|
IN BOOL fDefault
|
|
);
|
|
|
|
DWORD
|
|
GetBrowserValue(
|
|
IN OPTIONAL LPTSTR Section,
|
|
IN LPTSTR Key,
|
|
OUT PDWORD pdwValue
|
|
);
|
|
#endif
|
|
|
|
|
|
//
|
|
// Implementation
|
|
//
|
|
|
|
NET_API_STATUS
|
|
BrDgReceiverIoControlEx(
|
|
IN HANDLE FileHandle,
|
|
IN ULONG DgReceiverControlCode,
|
|
IN PLMDR_REQUEST_PACKET Drp,
|
|
IN ULONG DrpSize,
|
|
IN PVOID SecondBuffer OPTIONAL,
|
|
IN ULONG SecondBufferLength,
|
|
OUT PULONG Information OPTIONAL,
|
|
IN BOOLEAN WaitForCompletion
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
FileHandle - Supplies a handle to the file or device on which the service
|
|
is being performed.
|
|
|
|
DgReceiverControlCode - Supplies the NtDeviceIoControlFile function code
|
|
given to the datagram receiver.
|
|
|
|
Drp - Supplies the datagram receiver request packet.
|
|
|
|
DrpSize - Supplies the length of the datagram receiver request packet.
|
|
|
|
SecondBuffer - Supplies the second buffer in call to NtDeviceIoControlFile.
|
|
|
|
SecondBufferLength - Supplies the length of the second buffer.
|
|
|
|
Information - Returns the information field of the I/O status block.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntstatus;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PLMDR_REQUEST_PACKET RealDrp;
|
|
HANDLE CompletionEvent;
|
|
LPBYTE Where;
|
|
|
|
if (FileHandle == NULL) {
|
|
return ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Allocate a copy of the request packet where we can put the transport and
|
|
// emulated domain name in the packet itself.
|
|
//
|
|
RealDrp = (PLMDR_REQUEST_PACKET) MIDL_user_allocate(DrpSize+
|
|
Drp->TransportName.Length+sizeof(WCHAR)+
|
|
Drp->EmulatedDomainName.Length+sizeof(WCHAR) );
|
|
|
|
if (RealDrp == NULL) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Copy the request packet into the local copy.
|
|
//
|
|
RtlCopyMemory(RealDrp, Drp, DrpSize);
|
|
|
|
Where = (LPBYTE)RealDrp+DrpSize;
|
|
if (Drp->TransportName.Length != 0) {
|
|
RealDrp->TransportName.Buffer = (LPWSTR)Where;
|
|
RealDrp->TransportName.MaximumLength = Drp->TransportName.Length+sizeof(WCHAR);
|
|
RtlCopyUnicodeString(&RealDrp->TransportName, &Drp->TransportName);
|
|
Where += RealDrp->TransportName.MaximumLength;
|
|
}
|
|
|
|
if (Drp->EmulatedDomainName.Length != 0) {
|
|
RealDrp->EmulatedDomainName.Buffer = (LPWSTR)Where;
|
|
RealDrp->EmulatedDomainName.MaximumLength = Drp->EmulatedDomainName.Length+sizeof(WCHAR);
|
|
RtlCopyUnicodeString(&RealDrp->EmulatedDomainName, &Drp->EmulatedDomainName);
|
|
Where += RealDrp->EmulatedDomainName.MaximumLength;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Create a completion event
|
|
//
|
|
CompletionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if (CompletionEvent == NULL) {
|
|
|
|
MIDL_user_free(RealDrp);
|
|
|
|
return(GetLastError());
|
|
}
|
|
|
|
//
|
|
// Send the request to the Datagram Receiver DD.
|
|
//
|
|
|
|
ntstatus = NtDeviceIoControlFile(
|
|
FileHandle,
|
|
CompletionEvent,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
DgReceiverControlCode,
|
|
RealDrp,
|
|
(ULONG)(Where-(LPBYTE)RealDrp),
|
|
SecondBuffer,
|
|
SecondBufferLength
|
|
);
|
|
|
|
if (NT_SUCCESS(ntstatus)) {
|
|
|
|
//
|
|
// If we need to wait for completion (synchronous) and
|
|
// If pending was returned, then wait until the request completes.
|
|
//
|
|
|
|
if ( WaitForCompletion && (ntstatus == STATUS_PENDING) ) {
|
|
|
|
do {
|
|
ntstatus = WaitForSingleObjectEx(CompletionEvent, 0xffffffff, TRUE);
|
|
} while ( ntstatus == WAIT_IO_COMPLETION );
|
|
}
|
|
|
|
|
|
if (NT_SUCCESS(ntstatus)) {
|
|
ntstatus = IoStatusBlock.Status;
|
|
}
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(Information)) {
|
|
*Information = (ULONG)IoStatusBlock.Information;
|
|
}
|
|
|
|
MIDL_user_free(RealDrp);
|
|
|
|
CloseHandle(CompletionEvent);
|
|
|
|
return NetpNtStatusToApiStatus(ntstatus);
|
|
}
|
|
|
|
NET_API_STATUS
|
|
BrDgReceiverIoControl(
|
|
IN HANDLE FileHandle,
|
|
IN ULONG DgReceiverControlCode,
|
|
IN PLMDR_REQUEST_PACKET Drp,
|
|
IN ULONG DrpSize,
|
|
IN PVOID SecondBuffer OPTIONAL,
|
|
IN ULONG SecondBufferLength,
|
|
OUT PULONG Information OPTIONAL
|
|
)
|
|
{
|
|
return BrDgReceiverIoControlEx(
|
|
FileHandle,
|
|
DgReceiverControlCode,
|
|
Drp,
|
|
DrpSize,
|
|
SecondBuffer,
|
|
SecondBufferLength,
|
|
Information,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
NET_API_STATUS
|
|
DeviceControlGetInfo(
|
|
IN HANDLE FileHandle,
|
|
IN ULONG DeviceControlCode,
|
|
IN PVOID RequestPacket,
|
|
IN ULONG RequestPacketLength,
|
|
OUT LPVOID *OutputBuffer,
|
|
IN ULONG PreferedMaximumLength,
|
|
IN ULONG BufferHintSize,
|
|
OUT PULONG Information OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allocates the buffer and fill it with the information
|
|
that is retrieved from the datagram receiver.
|
|
|
|
Arguments:
|
|
|
|
DeviceDriverType - Supplies the value which indicates whether to call
|
|
the datagram receiver.
|
|
|
|
FileHandle - Supplies a handle to the file or device of which to get
|
|
information about.
|
|
|
|
DeviceControlCode - Supplies the NtFsControlFile or NtIoDeviceControlFile
|
|
function control code.
|
|
|
|
RequestPacket - Supplies a pointer to the device request packet.
|
|
|
|
RrequestPacketLength - Supplies the length of the device request packet.
|
|
|
|
OutputBuffer - Returns a pointer to the buffer allocated by this routine
|
|
which contains the use information requested. This pointer is set to
|
|
NULL if return code is not NERR_Success.
|
|
|
|
PreferedMaximumLength - Supplies the number of bytes of information to
|
|
return in the buffer. If this value is MAXULONG, we will try to
|
|
return all available information if there is enough memory resource.
|
|
|
|
BufferHintSize - Supplies the hint size of the output buffer so that the
|
|
memory allocated for the initial buffer will most likely be large
|
|
enough to hold all requested data.
|
|
|
|
Information - Returns the information code from the NtFsControlFile or
|
|
NtIoDeviceControlFile call.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS status;
|
|
NTSTATUS ntstatus;
|
|
DWORD OutputBufferLength;
|
|
DWORD TotalBytesNeeded = 1;
|
|
ULONG OriginalResumeKey;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) RequestPacket;
|
|
HANDLE CompletionEvent;
|
|
|
|
OriginalResumeKey = Drrp->Parameters.EnumerateNames.ResumeHandle;
|
|
|
|
//
|
|
// If PreferedMaximumLength is MAXULONG, then we are supposed to get all
|
|
// the information, regardless of size. Allocate the output buffer of a
|
|
// reasonable size and try to use it. If this fails, the Redirector FSD
|
|
// will say how much we need to allocate.
|
|
//
|
|
if (PreferedMaximumLength == MAXULONG) {
|
|
OutputBufferLength = (BufferHintSize) ?
|
|
BufferHintSize :
|
|
INITIAL_ALLOCATION_SIZE;
|
|
}
|
|
else {
|
|
OutputBufferLength = PreferedMaximumLength;
|
|
}
|
|
|
|
OutputBufferLength = ROUND_UP_COUNT(OutputBufferLength, ALIGN_WCHAR);
|
|
|
|
if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
|
|
|
|
CompletionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if (CompletionEvent == NULL) {
|
|
MIDL_user_free(*OutputBuffer);
|
|
*OutputBuffer = NULL;
|
|
return(GetLastError());
|
|
}
|
|
|
|
Drrp->Parameters.EnumerateServers.EntriesRead = 0;
|
|
|
|
//
|
|
// Make the request of the Datagram Receiver
|
|
//
|
|
|
|
ntstatus = NtDeviceIoControlFile(
|
|
FileHandle,
|
|
CompletionEvent,
|
|
NULL, // APC routine
|
|
NULL, // APC context
|
|
&IoStatusBlock,
|
|
DeviceControlCode,
|
|
Drrp,
|
|
RequestPacketLength,
|
|
*OutputBuffer,
|
|
OutputBufferLength
|
|
);
|
|
|
|
if (NT_SUCCESS(ntstatus)) {
|
|
|
|
//
|
|
// If pending was returned, then wait until the request completes.
|
|
//
|
|
|
|
if (ntstatus == STATUS_PENDING) {
|
|
do {
|
|
ntstatus = WaitForSingleObjectEx(CompletionEvent, 0xffffffff, TRUE);
|
|
} while ( ntstatus == WAIT_IO_COMPLETION );
|
|
}
|
|
|
|
if (NT_SUCCESS(ntstatus)) {
|
|
ntstatus = IoStatusBlock.Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Map NT status to Win error
|
|
//
|
|
status = NetpNtStatusToApiStatus(ntstatus);
|
|
|
|
if (status == ERROR_MORE_DATA) {
|
|
|
|
NetpAssert(
|
|
FIELD_OFFSET(
|
|
LMDR_REQUEST_PACKET,
|
|
Parameters.EnumerateNames.TotalBytesNeeded
|
|
) ==
|
|
FIELD_OFFSET(
|
|
LMDR_REQUEST_PACKET,
|
|
Parameters.EnumerateServers.TotalBytesNeeded
|
|
)
|
|
);
|
|
|
|
NetpAssert(
|
|
FIELD_OFFSET(
|
|
LMDR_REQUEST_PACKET,
|
|
Parameters.GetBrowserServerList.TotalBytesNeeded
|
|
) ==
|
|
FIELD_OFFSET(
|
|
LMDR_REQUEST_PACKET,
|
|
Parameters.EnumerateServers.TotalBytesNeeded
|
|
)
|
|
);
|
|
|
|
TotalBytesNeeded = Drrp->Parameters.EnumerateNames.TotalBytesNeeded;
|
|
}
|
|
|
|
if ((TotalBytesNeeded > OutputBufferLength) &&
|
|
(PreferedMaximumLength == MAXULONG)) {
|
|
PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) RequestPacket;
|
|
|
|
//
|
|
// Initial output buffer allocated was too small and we need to return
|
|
// all data. First free the output buffer before allocating the
|
|
// required size plus a fudge factor just in case the amount of data
|
|
// grew.
|
|
//
|
|
|
|
MIDL_user_free(*OutputBuffer);
|
|
|
|
OutputBufferLength =
|
|
ROUND_UP_COUNT((TotalBytesNeeded + FUDGE_FACTOR_SIZE),
|
|
ALIGN_WCHAR);
|
|
|
|
if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
|
|
|
|
|
|
NetpAssert(
|
|
FIELD_OFFSET(
|
|
LMDR_REQUEST_PACKET,
|
|
Parameters.EnumerateNames.ResumeHandle
|
|
) ==
|
|
FIELD_OFFSET(
|
|
LMDR_REQUEST_PACKET,
|
|
Parameters.EnumerateServers.ResumeHandle
|
|
)
|
|
);
|
|
|
|
NetpAssert(
|
|
FIELD_OFFSET(
|
|
LMDR_REQUEST_PACKET,
|
|
Parameters.EnumerateNames.ResumeHandle
|
|
) ==
|
|
FIELD_OFFSET(
|
|
LMDR_REQUEST_PACKET,
|
|
Parameters.GetBrowserServerList.ResumeHandle
|
|
)
|
|
);
|
|
|
|
Drrp->Parameters.EnumerateNames.ResumeHandle = OriginalResumeKey;
|
|
Drrp->Parameters.EnumerateServers.EntriesRead = 0;
|
|
|
|
//
|
|
// Make the request of the Datagram Receiver
|
|
//
|
|
|
|
ntstatus = NtDeviceIoControlFile(
|
|
FileHandle,
|
|
CompletionEvent,
|
|
NULL, // APC routine
|
|
NULL, // APC context
|
|
&IoStatusBlock,
|
|
DeviceControlCode,
|
|
Drrp,
|
|
RequestPacketLength,
|
|
*OutputBuffer,
|
|
OutputBufferLength
|
|
);
|
|
|
|
if (NT_SUCCESS(ntstatus)) {
|
|
|
|
//
|
|
// If pending was returned, then wait until the request completes.
|
|
//
|
|
|
|
if (ntstatus == STATUS_PENDING) {
|
|
do {
|
|
ntstatus = WaitForSingleObjectEx(CompletionEvent, 0xffffffff, TRUE);
|
|
} while ( ntstatus == WAIT_IO_COMPLETION );
|
|
}
|
|
|
|
if (NT_SUCCESS(ntstatus)) {
|
|
ntstatus = IoStatusBlock.Status;
|
|
}
|
|
}
|
|
|
|
status = NetpNtStatusToApiStatus(ntstatus);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If not successful in getting any data, or if the caller asked for
|
|
// all available data with PreferedMaximumLength == MAXULONG and
|
|
// our buffer overflowed, free the output buffer and set its pointer
|
|
// to NULL.
|
|
//
|
|
if ((status != NERR_Success && status != ERROR_MORE_DATA) ||
|
|
(TotalBytesNeeded == 0) ||
|
|
(PreferedMaximumLength == MAXULONG && status == ERROR_MORE_DATA) ||
|
|
(Drrp->Parameters.EnumerateServers.EntriesRead == 0)) {
|
|
|
|
MIDL_user_free(*OutputBuffer);
|
|
*OutputBuffer = NULL;
|
|
|
|
//
|
|
// PreferedMaximumLength == MAXULONG and buffer overflowed means
|
|
// we do not have enough memory to satisfy the request.
|
|
//
|
|
if (status == ERROR_MORE_DATA) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
CloseHandle(CompletionEvent);
|
|
|
|
return status;
|
|
|
|
UNREFERENCED_PARAMETER(Information);
|
|
}
|
|
|
|
// end_setup
|
|
|
|
NET_API_STATUS
|
|
GetBrowserServerList(
|
|
IN PUNICODE_STRING TransportName,
|
|
IN LPCWSTR Domain,
|
|
OUT LPWSTR *BrowserList[],
|
|
OUT PULONG BrowserListLength,
|
|
IN BOOLEAN ForceRescan
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function will return a list of browser servers.
|
|
|
|
Arguments:
|
|
|
|
IN PUNICODE_STRING TransportName - Transport to return list.
|
|
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
NET_API_STATUS Status;
|
|
HANDLE BrowserHandle;
|
|
PLMDR_REQUEST_PACKET RequestPacket = NULL;
|
|
|
|
// DbgPrint("Getting browser server list for transport %wZ\n", TransportName);
|
|
|
|
Status = OpenBrowser(&BrowserHandle);
|
|
|
|
if (Status != NERR_Success) {
|
|
return Status;
|
|
}
|
|
|
|
RequestPacket = MIDL_user_allocate(sizeof(LMDR_REQUEST_PACKET)+(DNLEN*sizeof(WCHAR))+TransportName->MaximumLength);
|
|
|
|
if (RequestPacket == NULL) {
|
|
NtClose(BrowserHandle);
|
|
return(GetLastError());
|
|
}
|
|
|
|
RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
|
|
|
|
RequestPacket->Level = 0;
|
|
|
|
RequestPacket->Parameters.GetBrowserServerList.ForceRescan = ForceRescan;
|
|
|
|
if (Domain != NULL) {
|
|
STRCPY(RequestPacket->Parameters.GetBrowserServerList.DomainName, Domain);
|
|
|
|
RequestPacket->Parameters.GetBrowserServerList.DomainNameLength = (USHORT)STRLEN(Domain) * sizeof(TCHAR);
|
|
} else {
|
|
RequestPacket->Parameters.GetBrowserServerList.DomainNameLength = 0;
|
|
RequestPacket->Parameters.GetBrowserServerList.DomainName[0] = L'\0';
|
|
|
|
}
|
|
|
|
RequestPacket->TransportName.Buffer = (PWSTR)((PCHAR)RequestPacket+sizeof(LMDR_REQUEST_PACKET)+DNLEN*sizeof(WCHAR));
|
|
RequestPacket->TransportName.MaximumLength = TransportName->MaximumLength;
|
|
|
|
RtlCopyUnicodeString(&RequestPacket->TransportName, TransportName);
|
|
RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, NULL );
|
|
|
|
RequestPacket->Parameters.GetBrowserServerList.ResumeHandle = 0;
|
|
|
|
Status = DeviceControlGetInfo(
|
|
BrowserHandle,
|
|
IOCTL_LMDR_GET_BROWSER_SERVER_LIST,
|
|
RequestPacket,
|
|
sizeof(LMDR_REQUEST_PACKET)+
|
|
(DNLEN*sizeof(WCHAR))+TransportName->MaximumLength,
|
|
(PVOID *)BrowserList,
|
|
0xffffffff,
|
|
4096,
|
|
NULL);
|
|
|
|
if (Status == NERR_Success) {
|
|
if ( NULL == *BrowserList ) {
|
|
Status = ERROR_NO_BROWSER_SERVERS_FOUND;
|
|
}
|
|
else {
|
|
*BrowserListLength = RequestPacket->Parameters.GetBrowserServerList.EntriesRead;
|
|
}
|
|
}
|
|
|
|
NtClose(BrowserHandle);
|
|
MIDL_user_free(RequestPacket);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
OpenBrowserEx(
|
|
OUT PHANDLE BrowserHandle,
|
|
IN BOOLEAN Synchronous
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function opens a handle to the bowser device driver.
|
|
|
|
Arguments:
|
|
|
|
OUT PHANDLE BrowserHandle - Returns the handle to the browser.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS - NERR_Success or reason for failure.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntstatus;
|
|
|
|
UNICODE_STRING DeviceName;
|
|
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
ULONG openOptions;
|
|
|
|
|
|
//
|
|
// Open the redirector device.
|
|
//
|
|
RtlInitUnicodeString(&DeviceName, DD_BROWSER_DEVICE_NAME_U);
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&DeviceName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (Synchronous) {
|
|
openOptions = FILE_SYNCHRONOUS_IO_NONALERT;
|
|
}
|
|
else {
|
|
openOptions = 0;
|
|
}
|
|
ntstatus = NtOpenFile(
|
|
BrowserHandle,
|
|
SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
openOptions
|
|
);
|
|
|
|
if (NT_SUCCESS(ntstatus)) {
|
|
ntstatus = IoStatusBlock.Status;
|
|
}
|
|
|
|
return NetpNtStatusToApiStatus(ntstatus);
|
|
|
|
}
|
|
|
|
NET_API_STATUS
|
|
OpenBrowser(
|
|
OUT PHANDLE BrowserHandle
|
|
)
|
|
{
|
|
return OpenBrowserEx(BrowserHandle, TRUE);
|
|
}
|
|
|
|
NET_API_STATUS
|
|
CheckForService(
|
|
IN LPTSTR ServiceName,
|
|
OUT LPSERVICE_STATUS ServiceStatus OPTIONAL
|
|
)
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
SC_HANDLE ServiceControllerHandle;
|
|
SC_HANDLE ServiceHandle;
|
|
SERVICE_STATUS Status;
|
|
|
|
if (!ARGUMENT_PRESENT(ServiceStatus)) {
|
|
ServiceStatus = &Status;
|
|
}
|
|
|
|
ServiceControllerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
|
|
|
|
if (ServiceControllerHandle == NULL) {
|
|
|
|
return GetLastError();
|
|
}
|
|
|
|
ServiceHandle = OpenService(ServiceControllerHandle, ServiceName, SERVICE_QUERY_STATUS);
|
|
|
|
if (ServiceHandle == NULL) {
|
|
|
|
NetStatus = GetLastError();
|
|
CloseServiceHandle(ServiceControllerHandle);
|
|
return NetStatus;
|
|
}
|
|
|
|
|
|
if (!QueryServiceStatus(ServiceHandle, ServiceStatus)) {
|
|
NetStatus = GetLastError();
|
|
CloseServiceHandle(ServiceHandle);
|
|
CloseServiceHandle(ServiceControllerHandle);
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
if ((ServiceStatus->dwCurrentState != SERVICE_RUNNING) &&
|
|
(ServiceStatus->dwCurrentState != SERVICE_START_PENDING)) {
|
|
CloseServiceHandle(ServiceHandle);
|
|
CloseServiceHandle(ServiceControllerHandle);
|
|
return(NERR_ServiceNotInstalled);
|
|
}
|
|
|
|
CloseServiceHandle(ServiceHandle);
|
|
|
|
CloseServiceHandle(ServiceControllerHandle);
|
|
|
|
return NERR_Success;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
BrGetLanaNumFromNetworkName(
|
|
IN PWCHAR TransportName,
|
|
OUT CCHAR *LanaNum
|
|
)
|
|
/*++
|
|
|
|
NOTE: THIS CODE WILL NOT WORK IN THE FUTURE!!!!!!!!!!!!!!
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HKEY Key;
|
|
LPWSTR BindInformation = NULL;
|
|
LPWSTR DevicePointer;
|
|
ULONG BindInfoSize = 0;
|
|
struct {
|
|
CHAR Enumerated;
|
|
CHAR LanaNum;
|
|
} *LanaMap = NULL;
|
|
|
|
ULONG LanaMapSize = 0;
|
|
NET_API_STATUS Status = ERROR_SUCCESS;
|
|
DWORD Type;
|
|
DWORD LanaIndex;
|
|
|
|
LanaIndex = 0;
|
|
|
|
if (Status = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services\\Netbios\\Linkage",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&Key))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Get buffers length
|
|
//
|
|
|
|
Status = RegQueryValueEx(
|
|
Key,
|
|
L"Bind",
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&BindInfoSize
|
|
);
|
|
if ( Status != ERROR_SUCCESS ||
|
|
BindInfoSize == 0 )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RegQueryValueEx(
|
|
Key,
|
|
L"LanaMap",
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&LanaMapSize
|
|
);
|
|
|
|
if ( Status != ERROR_SUCCESS ||
|
|
LanaMapSize == 0 )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate buffers for data.
|
|
//
|
|
|
|
BindInformation = MIDL_user_allocate(BindInfoSize);
|
|
if ( !BindInformation )
|
|
{
|
|
Status = ERROR_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
LanaMap = MIDL_user_allocate(LanaMapSize);
|
|
if ( !LanaMap )
|
|
{
|
|
Status = ERROR_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Load values from registry
|
|
//
|
|
|
|
if (Status = RegQueryValueEx(
|
|
Key,
|
|
L"Bind",
|
|
0,
|
|
&Type,
|
|
(LPBYTE)BindInformation,
|
|
&BindInfoSize))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (Status = RegQueryValueEx(
|
|
Key,
|
|
L"LanaMap",
|
|
0,
|
|
&Type,
|
|
(LPBYTE)LanaMap,
|
|
&LanaMapSize))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Calculate buffer size
|
|
//
|
|
|
|
|
|
DevicePointer = BindInformation;
|
|
|
|
while (*DevicePointer != UNICODE_NULL) {
|
|
if (!_wcsicmp(TransportName, DevicePointer)) {
|
|
// found transport
|
|
if (LanaMap[LanaIndex].Enumerated != 0) {
|
|
*LanaNum = LanaMap[LanaIndex].LanaNum;
|
|
Status = NERR_Success;
|
|
|
|
} else {
|
|
Status = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
LanaIndex += 1;
|
|
|
|
DevicePointer += wcslen(DevicePointer)+1;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( BindInformation )
|
|
{
|
|
MIDL_user_free( BindInformation );
|
|
}
|
|
if ( LanaMap )
|
|
{
|
|
MIDL_user_free( LanaMap );
|
|
}
|
|
RegCloseKey(Key);
|
|
return( Status );
|
|
}
|
|
|
|
// 1234567890123456
|
|
#define SPACES " "
|
|
|
|
#define ClearNcb( PNCB ) { \
|
|
RtlZeroMemory( PNCB , sizeof (NCB) ); \
|
|
RtlCopyMemory( (PNCB)->ncb_name, SPACES, sizeof(SPACES)-1 );\
|
|
RtlCopyMemory( (PNCB)->ncb_callname, SPACES, sizeof(SPACES)-1 );\
|
|
}
|
|
|
|
NET_API_STATUS
|
|
GetNetBiosMasterName(
|
|
IN LPWSTR NetworkName,
|
|
IN LPWSTR PrimaryDomain,
|
|
OUT LPWSTR MasterName,
|
|
IN PSVCS_NET_BIOS_RESET SvcsNetBiosReset OPTIONAL
|
|
)
|
|
{
|
|
CCHAR LanaNum;
|
|
NCB AStatNcb;
|
|
#define MAX_NETBIOS_NAMES 256
|
|
struct {
|
|
ADAPTER_STATUS AdapterInfo;
|
|
NAME_BUFFER Names[MAX_NETBIOS_NAMES];
|
|
} AdapterStatus;
|
|
WORD i;
|
|
CHAR remoteName[CNLEN+1];
|
|
NET_API_STATUS Status;
|
|
OEM_STRING OemString;
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
Status = BrGetLanaNumFromNetworkName(NetworkName, &LanaNum);
|
|
|
|
if (Status != NERR_Success) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// If the SvcsNetBiosReset argument is present, then this routine is
|
|
// being called from the service. In this case it needs to synchronize
|
|
// its NetBios Reset with the workstation and the messenger.
|
|
//
|
|
if (ARGUMENT_PRESENT(SvcsNetBiosReset)) {
|
|
SvcsNetBiosReset(LanaNum);
|
|
}
|
|
else {
|
|
ClearNcb(&AStatNcb)
|
|
|
|
AStatNcb.ncb_command = NCBRESET;
|
|
AStatNcb.ncb_lsn = 0; // Request resources
|
|
AStatNcb.ncb_lana_num = LanaNum;
|
|
AStatNcb.ncb_callname[0] = 0; // 16 sessions
|
|
AStatNcb.ncb_callname[1] = 0; // 16 commands
|
|
AStatNcb.ncb_callname[2] = 0; // 8 names
|
|
AStatNcb.ncb_callname[3] = 0; // Don't want the reserved address
|
|
Netbios( &AStatNcb );
|
|
}
|
|
ClearNcb( &AStatNcb );
|
|
|
|
//
|
|
// Uppercase the remote name.
|
|
//
|
|
|
|
RtlInitUnicodeString(&UnicodeString, PrimaryDomain);
|
|
|
|
OemString.Buffer=remoteName;
|
|
|
|
OemString.MaximumLength=sizeof(remoteName);
|
|
|
|
Status = RtlUpcaseUnicodeStringToOemString(&OemString,
|
|
&UnicodeString,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return RtlNtStatusToDosError(Status);
|
|
}
|
|
|
|
AStatNcb.ncb_command = NCBASTAT;
|
|
|
|
RtlCopyMemory( AStatNcb.ncb_callname, remoteName, strlen(remoteName));
|
|
|
|
AStatNcb.ncb_callname[15] = MASTER_BROWSER_SIGNATURE;
|
|
|
|
AStatNcb.ncb_lana_num = LanaNum;
|
|
AStatNcb.ncb_length = sizeof( AdapterStatus );
|
|
AStatNcb.ncb_buffer = (CHAR *)&AdapterStatus;
|
|
Netbios( &AStatNcb );
|
|
|
|
if ( AStatNcb.ncb_retcode == NRC_GOODRET ||
|
|
AStatNcb.ncb_retcode == NRC_INCOMP ) {
|
|
for ( i=0 ; i < min(AdapterStatus.AdapterInfo.name_count, MAX_NETBIOS_NAMES) ; i++ ) {
|
|
if (AdapterStatus.Names[i].name[NCBNAMSZ-1] == SERVER_SIGNATURE) {
|
|
DWORD j;
|
|
|
|
//
|
|
// Ignore malformed netbios names.
|
|
//
|
|
// Some transports have strange netbios names. For instance,
|
|
// netbt registers a netbios name where the first 12 bytes
|
|
// are 0 and the last 4 bytes are the IP address.
|
|
//
|
|
for ( j = 0 ; j < CNLEN ; j++ ) {
|
|
if (AdapterStatus.Names[i].name[j] == '\0') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( j != CNLEN ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Convert to unicode
|
|
//
|
|
|
|
if (MultiByteToWideChar(CP_OEMCP,
|
|
0,
|
|
AdapterStatus.Names[i].name,
|
|
CNLEN,
|
|
MasterName,
|
|
CNLEN) == 0) {
|
|
return(GetLastError());
|
|
}
|
|
|
|
for (j = CNLEN - 1; j ; j -= 1) {
|
|
if (MasterName[j] != L' ') {
|
|
MasterName[j+1] = UNICODE_NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NERR_Success;
|
|
}
|
|
}
|
|
return AStatNcb.ncb_retcode;
|
|
} else {
|
|
return AStatNcb.ncb_retcode;
|
|
}
|
|
}
|
|
|
|
NET_API_STATUS
|
|
SendDatagramEx(
|
|
IN HANDLE DgReceiverHandle,
|
|
IN PUNICODE_STRING Network,
|
|
IN PUNICODE_STRING EmulatedDomainName,
|
|
IN PWSTR ResponseName,
|
|
IN DGRECEIVER_NAME_TYPE NameType,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferLength,
|
|
IN BOOLEAN Synchronous
|
|
)
|
|
{
|
|
NET_API_STATUS Status;
|
|
ULONG IOCTLCode;
|
|
|
|
UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+(LM20_CNLEN+1)*sizeof(WCHAR)];
|
|
PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
|
|
|
|
|
|
RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
|
|
|
|
RequestPacket->TransportName = *Network;
|
|
RequestPacket->EmulatedDomainName = *EmulatedDomainName;
|
|
|
|
RequestPacket->Type = Datagram;
|
|
|
|
RequestPacket->Parameters.SendDatagram.DestinationNameType = NameType;
|
|
|
|
RequestPacket->Parameters.SendDatagram.MailslotNameLength = 0;
|
|
|
|
//
|
|
// The domain announcement name is special, so we don't have to specify
|
|
// a destination name for it.
|
|
//
|
|
|
|
RequestPacket->Parameters.SendDatagram.NameLength = wcslen(ResponseName)*sizeof(WCHAR);
|
|
|
|
wcscpy(RequestPacket->Parameters.SendDatagram.Name, ResponseName);
|
|
|
|
//
|
|
// This is a simple IoControl - It just sends the datagram.
|
|
//
|
|
|
|
if ( Synchronous ) {
|
|
IOCTLCode = IOCTL_LMDR_WRITE_MAILSLOT;
|
|
}
|
|
else {
|
|
IOCTLCode = IOCTL_LMDR_WRITE_MAILSLOT_ASYNC;
|
|
}
|
|
Status = BrDgReceiverIoControlEx(DgReceiverHandle,
|
|
IOCTLCode,
|
|
RequestPacket,
|
|
FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.SendDatagram.Name)+
|
|
RequestPacket->Parameters.SendDatagram.NameLength,
|
|
Buffer,
|
|
BufferLength,
|
|
NULL,
|
|
Synchronous
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NET_API_STATUS
|
|
SendDatagram(
|
|
IN HANDLE DgReceiverHandle,
|
|
IN PUNICODE_STRING Network,
|
|
IN PUNICODE_STRING EmulatedDomainName,
|
|
IN PWSTR ResponseName,
|
|
IN DGRECEIVER_NAME_TYPE NameType,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferLength
|
|
)
|
|
{
|
|
return SendDatagramEx(
|
|
DgReceiverHandle,
|
|
Network,
|
|
EmulatedDomainName,
|
|
ResponseName,
|
|
NameType,
|
|
Buffer,
|
|
BufferLength,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
#ifdef ENABLE_PSEUDO_BROWSER
|
|
DWORD
|
|
GetBrowserValue(
|
|
IN OPTIONAL LPTSTR Section,
|
|
IN LPTSTR Key,
|
|
OUT PDWORD pdwValue
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Query Registry for browser DWORD value.
|
|
|
|
Arguments:
|
|
|
|
Section: registry section to query
|
|
Key: Key to query value.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS: Got value
|
|
Win32 error: failed to get value
|
|
--*/
|
|
{
|
|
DWORD status = NERR_Success;
|
|
LPNET_CONFIG_HANDLE BrowserSection;
|
|
const DWORD dwDefault = 0;
|
|
|
|
NetpAssert(pdwValue);
|
|
|
|
if ( Section ) {
|
|
//
|
|
// Open specified section
|
|
// (usually used for policy propagation & such)
|
|
//
|
|
if (NetpOpenConfigDataWithPath(
|
|
&BrowserSection,
|
|
NULL,
|
|
Section,
|
|
TRUE) != NERR_Success) {
|
|
return ERROR_CANTREAD;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Open default browser section
|
|
//
|
|
if (NetpOpenConfigData(
|
|
&BrowserSection,
|
|
NULL,
|
|
SECT_NT_BROWSER,
|
|
TRUE) != NERR_Success) {
|
|
return ERROR_CANTREAD;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get config value
|
|
//
|
|
if ( NetpGetConfigDword(
|
|
BrowserSection,
|
|
Key,
|
|
dwDefault,
|
|
pdwValue ) ) {
|
|
// free handle & return failure (default's assigned already)
|
|
NetpCloseConfigData(BrowserSection);
|
|
return (ERROR_CANTREAD);
|
|
}
|
|
|
|
// free handle
|
|
NetpCloseConfigData(BrowserSection);
|
|
|
|
//
|
|
// Hack: Net api missing key fixup
|
|
//
|
|
if ( Section ){
|
|
//
|
|
// If explicit path was specified we distinguish missing key
|
|
// from when the key is set. However Net api return
|
|
// success (assuming default) if the key is missing.
|
|
// Thus, upon success we query below for the key's existance.
|
|
// Justification: We need to know when a policy key was
|
|
// specified or not. However for default net section we can
|
|
// accept default value. That is, we assume proper call order:
|
|
// 1. query policy --> if specified accept, if missing -->
|
|
// 2. query net default location --> if missing or error accept
|
|
// default.
|
|
// So why don't we just use our own? Cause net api has it standards
|
|
// such as [yes|no] equiv to [DWORD(1)|DWORD(0)], default locations
|
|
// and more.
|
|
//
|
|
HKEY SectionKey = NULL;
|
|
DWORD cbData;
|
|
LPWSTR pwszSection;
|
|
const LPWSTR wszParameters = L"\\Parameters";
|
|
|
|
|
|
// alloc & copy fixed up section name
|
|
cbData = (wcslen(Section) + wcslen(wszParameters) + 1) * sizeof(WCHAR);
|
|
pwszSection = MIDL_user_allocate(cbData);
|
|
if (!pwszSection) {
|
|
return (ERROR_CANTREAD);
|
|
}
|
|
wcscpy(pwszSection, Section);
|
|
wcscat(pwszSection, wszParameters);
|
|
|
|
|
|
status = RegOpenKeyEx (
|
|
HKEY_LOCAL_MACHINE,
|
|
pwszSection,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ,
|
|
&SectionKey );
|
|
if (status) {
|
|
//
|
|
// Can't even open the key.
|
|
//
|
|
MIDL_user_free( pwszSection );
|
|
return (ERROR_CANTREAD);
|
|
}
|
|
|
|
// free mem & query value.
|
|
MIDL_user_free( pwszSection );
|
|
cbData = 0;
|
|
status = RegQueryValueEx(
|
|
SectionKey,
|
|
Key,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&cbData );
|
|
|
|
if ( status != ERROR_SUCCESS ||
|
|
cbData == 0) {
|
|
//
|
|
// Key's not there or failed to get it
|
|
//
|
|
RegCloseKey(SectionKey);
|
|
return (ERROR_CANTREAD);
|
|
}
|
|
|
|
RegCloseKey(SectionKey);
|
|
}
|
|
|
|
// Got value
|
|
return (ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
DWORD
|
|
IsBrowserEnabled(
|
|
IN OPTIONAL LPTSTR Section,
|
|
IN LPTSTR Key,
|
|
IN BOOL fDefault
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Query Registry for browser boolean state.
|
|
|
|
Arguments:
|
|
|
|
Section: registry section to query
|
|
Key: Key to query value.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS: Browser marked enabled.
|
|
ERROR_SERVICE_DISABLED: Browser marked disabled.
|
|
ERROR_CANTREAD: No regkey found or can't open.
|
|
--*/
|
|
{
|
|
DWORD status = NERR_Success;
|
|
LPNET_CONFIG_HANDLE BrowserSection;
|
|
BOOL fEnabled = fDefault;
|
|
|
|
|
|
if ( Section ) {
|
|
//
|
|
// Open specified section
|
|
// (usually used for policy propagation & such)
|
|
//
|
|
if (NetpOpenConfigDataWithPath(
|
|
&BrowserSection,
|
|
NULL,
|
|
Section,
|
|
TRUE) != NERR_Success) {
|
|
return ERROR_CANTREAD;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Open default browser section
|
|
//
|
|
if (NetpOpenConfigData(
|
|
&BrowserSection,
|
|
NULL,
|
|
SECT_NT_BROWSER,
|
|
TRUE) != NERR_Success) {
|
|
return ERROR_CANTREAD;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get config value
|
|
//
|
|
if ( NetpGetConfigBool(
|
|
BrowserSection,
|
|
Key,
|
|
fDefault,
|
|
&fEnabled
|
|
) ) {
|
|
// free handle & return failure (default's assigned already)
|
|
NetpCloseConfigData(BrowserSection);
|
|
return (ERROR_CANTREAD);
|
|
}
|
|
|
|
// free handle
|
|
NetpCloseConfigData(BrowserSection);
|
|
|
|
//
|
|
// Hack: Net api missing key fixup
|
|
//
|
|
if ( Section ){
|
|
//
|
|
// If explicit path was specified we distinguish missing key
|
|
// from when the key is set. However Net api return
|
|
// success (assuming default) if the key is missing.
|
|
// Thus, upon success we query below for the key's existance.
|
|
// Justification: We need to know when a policy key was
|
|
// specified or not. However for default net section we can
|
|
// accept default value. That is, we assume proper call order:
|
|
// 1. query policy --> if specified accept, if missing -->
|
|
// 2. query net default location --> if missing or error accept
|
|
// default.
|
|
// So why don't we just use our own? Cause net api has it standards
|
|
// such as [yes|no] equiv to [DWORD(1)|DWORD(0)], default locations
|
|
// and more.
|
|
//
|
|
HKEY SectionKey = NULL;
|
|
DWORD cbData;
|
|
LPWSTR pwszSection;
|
|
const LPWSTR wszParameters = L"\\Parameters";
|
|
|
|
|
|
// alloc & copy fixed up section name
|
|
cbData = (wcslen(Section) + wcslen(wszParameters) + 1) * sizeof(WCHAR);
|
|
pwszSection = MIDL_user_allocate(cbData);
|
|
if (!pwszSection) {
|
|
return (ERROR_CANTREAD);
|
|
}
|
|
wcscpy(pwszSection, Section);
|
|
wcscat(pwszSection, wszParameters);
|
|
|
|
|
|
status = RegOpenKeyEx (
|
|
HKEY_LOCAL_MACHINE,
|
|
pwszSection,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ,
|
|
&SectionKey );
|
|
if (status) {
|
|
//
|
|
// Can't even open the key.
|
|
//
|
|
MIDL_user_free( pwszSection );
|
|
return (ERROR_CANTREAD);
|
|
}
|
|
|
|
// free mem & query value.
|
|
MIDL_user_free( pwszSection );
|
|
cbData = 0;
|
|
status = RegQueryValueEx(
|
|
SectionKey,
|
|
Key,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&cbData );
|
|
|
|
if ( status != ERROR_SUCCESS ||
|
|
cbData == 0) {
|
|
//
|
|
// Key's not there or failed to get it
|
|
//
|
|
RegCloseKey(SectionKey);
|
|
return (ERROR_CANTREAD);
|
|
}
|
|
|
|
RegCloseKey(SectionKey);
|
|
}
|
|
|
|
// got value, return state.
|
|
return ( fEnabled ? ERROR_SUCCESS :
|
|
ERROR_SERVICE_DISABLED);
|
|
}
|
|
|
|
BOOL
|
|
IsEnumServerEnabled(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Query the Registry to find if NetServerEnum functionality has
|
|
been disabled by an admin.
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
FALSE: service is marked disabled.
|
|
TRUE: default or service is marked enabled
|
|
|
|
Remarks:
|
|
None.
|
|
--*/
|
|
{
|
|
|
|
DWORD status;
|
|
const BOOL fDefault = TRUE;
|
|
|
|
//
|
|
// Query default section
|
|
//
|
|
status = IsBrowserEnabled(
|
|
BROWSER_POLICY_REGPATH_W,
|
|
BROWSER_SEND_SERVER_ENUM_REGKEY_W,
|
|
fDefault);
|
|
if ( status == ERROR_SUCCESS ) {
|
|
// policy marked enabled.
|
|
return TRUE;
|
|
}
|
|
else if ( status == ERROR_SERVICE_DISABLED ) {
|
|
// policy marked disabled
|
|
return FALSE;
|
|
}
|
|
// else failed to read, thus try default (local) service location.
|
|
|
|
//
|
|
// Query default section
|
|
//
|
|
status = IsBrowserEnabled(
|
|
NULL,
|
|
BROWSER_SEND_SERVER_ENUM_REGKEY_W,
|
|
fDefault);
|
|
if ( status == ERROR_SERVICE_DISABLED ) {
|
|
// marked disabled
|
|
return FALSE;
|
|
}
|
|
// else either ERROR_SUCCESS (i.e. enabled)
|
|
// or error (default to enabled)
|
|
|
|
NetpAssert(fDefault);
|
|
// default is enabled.
|
|
return fDefault;
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
GetBrowserPseudoServerLevel(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Query the Registry to find if this machine should act
|
|
as a Pseudo Server (in case it's a master browser)
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
FALSE: default or server is not a Pseudo Server.
|
|
TRUE: service is marked as Pseudo Server.
|
|
|
|
Remarks:
|
|
None.
|
|
--*/
|
|
{
|
|
|
|
DWORD status;
|
|
const DWORD dwDefault = BROWSER_NON_PSEUDO;
|
|
DWORD dwLevel = dwDefault;
|
|
|
|
//
|
|
// Query policy & then browser sections
|
|
//
|
|
if ( ERROR_SUCCESS == GetBrowserValue(
|
|
BROWSER_POLICY_REGPATH_W,
|
|
BROWSER_PSEUDO_SERVER_REGKEY_W,
|
|
&dwLevel) ||
|
|
ERROR_SUCCESS == GetBrowserValue(
|
|
NULL,
|
|
BROWSER_PSEUDO_SERVER_REGKEY_W,
|
|
&dwLevel) ) {
|
|
|
|
// policy level exits
|
|
if ( dwLevel != BROWSER_NON_PSEUDO &&
|
|
dwLevel != BROWSER_SEMI_PSEUDO_NO_DMB &&
|
|
dwLevel != BROWSER_PSEUDO ) {
|
|
NetpAssert(!"Regkey browser pseudo level set to invalid value");
|
|
return dwDefault;
|
|
}
|
|
return dwLevel;
|
|
}
|
|
// else failed to read all sections, use default.
|
|
|
|
return dwDefault;
|
|
}
|
|
#endif
|