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.
 
 
 
 
 
 

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