|
|
//+----------------------------------------------------------------------------
//
// Copyright (C) 1997, Microsoft Corporation
//
// File: dfslpc.c
//
// Contents: Code implement lpc server for dfs.sys
//
// Classes:
//
// Functions:
//
// History: 18 Dec 97 Jharper created (from XACT stuff)
//
//-----------------------------------------------------------------------------
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <dfsfsctl.h>
#include <windows.h>
#include <stdlib.h>
#include <lm.h>
#include <winsock2.h>
#include <dsgetdc.h>
#include <dfssrv.h>
#include <dsrole.h>
#include "dfsipc.h"
extern WCHAR DomainName[MAX_PATH]; extern DSROLE_MACHINE_ROLE DfsMachineRole;
//
// Event logging and debugging globals
//
extern ULONG DfsSvcVerbose; extern ULONG DfsEventLog;
//
// Number of worker threads.
//
LONG DfsThreads = 0;
//
// Event signalled when the last worker thread terminates.
//
HANDLE DfsAllThreadsTerminatedEvent = NULL;
//
// Boolean indicating whether server is active or terminating.
//
BOOL DfsTerminating = FALSE;
//
// Handle for the LPC port used for communication between the dfs driver
// and server
//
HANDLE DfsConnectionPortHandle = NULL; HANDLE DfsCommunicationPortHandle = NULL;
//
// Handle for communicating with the dfs driver
//
HANDLE serverHandle = NULL;
//
// This is the number of threads blocked waiting for an LPC request.
// When it drops to zero, all threads are active and another thread is
// created.
//
LONG DfsWaitingApiThreads = 0;
VOID DfsProcessApisWrapper ( DWORD ThreadNum );
VOID DfsProcessApis ( DWORD ThreadNum );
VOID DfsLoadIpCache( PDFS_IPADDRESS pIpAddress, LPWSTR SiteName );
NTSTATUS AddrToSite( PDFS_IPADDRESS IpAddress, LPWSTR **pppSiteName );
//
// Flags used in DsGetDcName()
//
DWORD dwFlags[] = { DS_DIRECTORY_SERVICE_REQUIRED | DS_IP_REQUIRED,
DS_DIRECTORY_SERVICE_REQUIRED | DS_IP_REQUIRED | DS_FORCE_REDISCOVERY };
DWORD DfsStartDfssrv ( VOID) { NTSTATUS status; DWORD error; DWORD i; HANDLE threadHandle; DWORD threadId; HANDLE eventHandle; UNICODE_STRING unicodeName; IO_STATUS_BLOCK ioStatusBlock; OBJECT_ATTRIBUTES objectAttributes; PORT_MESSAGE connectionRequest; BOOL waitForEvent; PCHAR bp; ULONG size; PUNICODE_STRING pustr; UNICODE_STRING DfsDriverName;
#if DBG
if (DfsSvcVerbose) DbgPrint("DfsStartDfssrv()\n", 0); #endif
// Sleep(60*1000);
// lpc_debug_trace("DfsStartDfssrv done sleeping...\n", 0);
//
// Set up variables so that we'll know how to shut down in case of
// an error.
//
serverHandle = NULL; eventHandle = NULL; DfsAllThreadsTerminatedEvent = NULL; waitForEvent = FALSE;
//
// Create a event that will be set by the last thread to exit.
//
status = NtCreateEvent( &DfsAllThreadsTerminatedEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE );
if ( !NT_SUCCESS(status) ) { DfsAllThreadsTerminatedEvent = NULL; goto exit; }
#if DBG
if (DfsSvcVerbose) DbgPrint("created event\n", 0); #endif
//
// Open the server device. Note that we need this handle because
// the handle used by the main server service is synchronous. We
// need to to do the DFS_CONNECT FSCTL asynchronously.
//
RtlInitUnicodeString( &unicodeName, DFS_SERVER_NAME );
InitializeObjectAttributes( &objectAttributes, &unicodeName, OBJ_CASE_INSENSITIVE, NULL, NULL );
status = NtOpenFile( &serverHandle, FILE_READ_DATA | FILE_WRITE_DATA, // DesiredAccess
&objectAttributes, &ioStatusBlock, 0L, // ShareAccess
0L // OpenOptions
);
if ( NT_SUCCESS(status) ) { status = ioStatusBlock.Status; } if ( !NT_SUCCESS(status) ) { goto exit; }
#if DBG
if (DfsSvcVerbose) DbgPrint("opened device\n", 0); #endif
//
// Create the LPC port.
//
// Right now this only tries a single port name. If, for some
// bizarre reason, somebody already has a port by this name,
// then this will fail. It might make sense to try different
// names if this fails.
//
RtlInitUnicodeString( &unicodeName, DFS_PORT_NAME_W );
InitializeObjectAttributes( &objectAttributes, &unicodeName, 0, NULL, NULL );
status = NtCreatePort( &DfsConnectionPortHandle, &objectAttributes, 0, DFS_PORT_MAX_MESSAGE_LENGTH, DFS_PORT_MAX_MESSAGE_LENGTH * 32 );
#if DBG
if (DfsSvcVerbose) DbgPrint("NtCreatePort status=0x%x\n", status); #endif
if ( ! NT_SUCCESS(status) ) { DfsConnectionPortHandle = NULL; goto exit; }
//
// Set up an event so that we'll know when IO completes, then send
// the FSCTL to the server indicating that it should now connect to
// us. We'll set up the port while the IO is outstanding, then wait
// on the event when the port setup is complete.
//
status = NtCreateEvent( &eventHandle, EVENT_ALL_ACCESS, NULL, // ObjectAttributes
NotificationEvent, FALSE );
#if DBG
if (DfsSvcVerbose) DbgPrint("NtCreateEvent status=0x%x\n", status); #endif
if ( !NT_SUCCESS(status) ) { goto exit; }
size = sizeof(UNICODE_STRING) + unicodeName.Length + sizeof(WCHAR);
pustr = (UNICODE_STRING *)(bp = malloc(size));
if (pustr == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto exit; }
RtlZeroMemory(bp, size);
pustr->Length = unicodeName.Length; pustr->MaximumLength = unicodeName.Length + sizeof(WCHAR); pustr->Buffer = (WCHAR *)(bp + sizeof(UNICODE_STRING)); RtlCopyMemory(pustr->Buffer, unicodeName.Buffer, unicodeName.Length); pustr->Buffer = (WCHAR *)sizeof(UNICODE_STRING);
#if DBG
if (DfsSvcVerbose) DbgPrint("Calling NtFsControlFile(FSCTL_SRV_DFSSRV_CONNECT)\n", status); #endif
status = NtFsControlFile( serverHandle, eventHandle, NULL, // ApcRoutine
NULL, // ApcContext
&ioStatusBlock, FSCTL_SRV_DFSSRV_CONNECT, bp, size, NULL, // OutputBuffer
0L // OutputBufferLength
);
#if DBG
if (DfsSvcVerbose) DbgPrint("NtFsControlFile status=0x%x\n", status); #endif
free(pustr);
if ( !NT_SUCCESS(status) ) { goto exit; }
waitForEvent = TRUE;
//
// Start listening for the server's connection to the port. Note
// that it is OK if the server happens to call NtConnectPort
// first--it will simply block until this call to NtListenPort
// occurs.
//
connectionRequest.u1.s1.TotalLength = sizeof(connectionRequest); connectionRequest.u1.s1.DataLength = (CSHORT)0; status = NtListenPort( DfsConnectionPortHandle, &connectionRequest );
#if DBG
if (DfsSvcVerbose) DbgPrint("NtListenPort status=0x%x\n", status); #endif
if ( !NT_SUCCESS(status) ) { goto exit; }
//
// The server has initiated the connection. Accept the connection.
//
status = NtAcceptConnectPort( &DfsCommunicationPortHandle, NULL, // PortContext
&connectionRequest, TRUE, // AcceptConnection
NULL, // ServerView
NULL // ClientView
);
#if DBG
if (DfsSvcVerbose) DbgPrint("NtAcceptConnectPort status=0x%x\n", status); #endif
if ( !NT_SUCCESS(status) ) { DfsCommunicationPortHandle = NULL; goto exit; }
//
// Complete the connection to the port, thereby releasing the server
// thread waiting in NtConnectPort.
//
status = NtCompleteConnectPort( DfsCommunicationPortHandle );
#if DBG
if (DfsSvcVerbose) DbgPrint("NtCompleteConnectPort status=0x%x\n", status); #endif
if ( !NT_SUCCESS(status) ) { goto exit; }
status = STATUS_SUCCESS;
exit:
//
// Wait for the IO to complete, then close the event handle.
//
if ( waitForEvent ) {
NTSTATUS waitStatus;
waitStatus = NtWaitForSingleObject( eventHandle, FALSE, NULL );
#if DBG
if (DfsSvcVerbose) DbgPrint("NtWaitForSingleObject status=0x%x\n", waitStatus); #endif
if ( !NT_SUCCESS(waitStatus) ) {
//
// If another error has already occurred, don't report this
// one.
//
if ( NT_SUCCESS(status) ) { status = waitStatus; } }
//
// Check the status in the IO status block. If it is bad, then
// there was some problem on the server side of the port setup.
//
if ( !NT_SUCCESS(ioStatusBlock.Status) ) { //
// If another error has already occurred, don't report this
// one.
//
if ( NT_SUCCESS(status) ) { status = ioStatusBlock.Status; }
}
}
if (eventHandle != NULL) {
CloseHandle( eventHandle );
}
//
// If the above failed, return to caller now.
//
if ( !NT_SUCCESS(status) ) { #if DBG
if (DfsSvcVerbose) DbgPrint("DfsStartDfsSrv exit 0x%x\n", status); #endif
return RtlNtStatusToDosError( status ); }
//
// Start one API processing thread. It will spawn others if needed
//
DfsThreads = 1;
DfsProcessApisWrapper(DfsThreads);
//
// Initialization succeeded.
//
return NO_ERROR;
}
VOID DfsProcessApisWrapper ( DWORD ThreadNum) { DFSSRV_REQUEST_MESSAGE requestMessage;
//
// Increase the priority of this thread to just above foreground
//
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL );
//
// Do the APIs
//
DfsProcessApis( ThreadNum );
//
// Decrement the count of threads. If the count goes to
// zero, set the All Threads Terminated event.
//
if (InterlockedDecrement(&DfsThreads ) == 0 ) {
SetEvent( DfsAllThreadsTerminatedEvent );
} else if (DfsTerminating ) {
//
// There are still threads left, and we are trying to terminate. Queue
// another message to the queue so the next thread will get it and
// notice that we're trying to quit.
//
RtlZeroMemory( &requestMessage, sizeof( requestMessage )); requestMessage.PortMessage.u1.s1.DataLength = (USHORT)( sizeof(requestMessage) - sizeof(PORT_MESSAGE) ); requestMessage.PortMessage.u1.s1.TotalLength = sizeof(requestMessage); requestMessage.MessageType = DFSSRV_MESSAGE_WAKEUP;
NtRequestPort( DfsConnectionPortHandle, (PPORT_MESSAGE)&requestMessage ); }
ExitThread( NO_ERROR );
}
/*
* This routine is called to stop the transaction processor once the * server driver has terminated. */ VOID DfsStopDfssrv ( VOID) { NTSTATUS status; static DFSSRV_REQUEST_MESSAGE requestMessage; LONG i;
//
// Stop all the worker threads, and release resources
//
if ( DfsConnectionPortHandle != NULL ) {
//
// Indicate that server is terminating.
//
DfsTerminating = TRUE;
//
// Queue a message to kill off the worker thereads
//
RtlZeroMemory( &requestMessage, sizeof( requestMessage )); requestMessage.PortMessage.u1.s1.DataLength = (USHORT)( sizeof(requestMessage) - sizeof(PORT_MESSAGE) ); requestMessage.PortMessage.u1.s1.TotalLength = sizeof(requestMessage); requestMessage.MessageType = DFSSRV_MESSAGE_WAKEUP; status = NtRequestPort( DfsConnectionPortHandle, (PPORT_MESSAGE)&requestMessage );
//
// The above will cause all worker threads to wake up then die.
//
if ( DfsThreads != 0 ) { BOOL ok; ok = WaitForSingleObject( DfsAllThreadsTerminatedEvent, (DWORD)-1 ); }
CloseHandle( DfsConnectionPortHandle ); }
if( DfsCommunicationPortHandle != NULL ) { CloseHandle( DfsCommunicationPortHandle ); DfsCommunicationPortHandle = NULL; }
//
// Close the termination event.
//
if ( DfsAllThreadsTerminatedEvent != NULL ) { CloseHandle( DfsAllThreadsTerminatedEvent ); DfsAllThreadsTerminatedEvent = NULL; }
//
// Close handle to driver
//
if ( serverHandle != NULL ) { CloseHandle( serverHandle ); }
return;
}
NTSTATUS ReplyFailedStatus; #define MAX_RETRY_REPLY 10
VOID DfsProcessApis ( DWORD ThreadNum) { NTSTATUS status; DFSSRV_REQUEST_MESSAGE request; DFSSRV_REPLY_MESSAGE reply; BOOL sendReply = FALSE; WORD apiNumber; LPWSTR *SiteNames; ULONG RetryReplyCount = 0;
RtlZeroMemory(&request, sizeof(DFSSRV_REQUEST_MESSAGE));
//
// Loop dispatching API requests.
//
while ( DfsTerminating == FALSE ) {
//
// We're waiting to handle another API...
//
InterlockedIncrement( &DfsWaitingApiThreads );
//
// Send the reply to the last message and wait for the next
// message. The first time through the loop, there will be
// no last message -- reply will be NULL.
//
status = NtReplyWaitReceivePort( DfsCommunicationPortHandle, NULL, // PortContext
sendReply ? (PPORT_MESSAGE)&reply : NULL, (PPORT_MESSAGE)&request );
if ( status == STATUS_INVALID_PORT_HANDLE || status == STATUS_PORT_DISCONNECTED || status == STATUS_INVALID_HANDLE || DfsTerminating || request.PortMessage.u2.s2.Type == LPC_PORT_CLOSED ) {
//
// The port is no longer valid, or DFSSRV is terminating.
//
InterlockedDecrement( &DfsWaitingApiThreads ); return;
} else if (request.PortMessage.u2.s2.Type == LPC_CONNECTION_REQUEST) {
sendReply = FALSE; continue;
} else if ( !NT_SUCCESS(status) ) { //
// Attempt to handle error returns from reply wait receive.
//
if ((status == STATUS_UNSUCCESSFUL) || (status == STATUS_USER_APC)) { sendReply = FALSE; } else if ((status == STATUS_INVALID_PARAMETER) || (status == STATUS_PORT_MESSAGE_TOO_LONG) || (status == STATUS_REPLY_MESSAGE_MISMATCH)) { sendReply = FALSE;
} else if (RetryReplyCount++ > MAX_RETRY_REPLY) { sendReply = FALSE; ReplyFailedStatus = status; }
continue; }
RetryReplyCount = 0; sendReply = TRUE;
//
// Set up the response message to be sent on the next call to
// NtReplyWaitReceivePort.
//
reply.PortMessage.u1.s1.DataLength = sizeof(reply) - sizeof(PORT_MESSAGE); reply.PortMessage.u1.s1.TotalLength = sizeof(reply); reply.PortMessage.u2.ZeroInit = 0; reply.PortMessage.ClientId = request.PortMessage.ClientId; reply.PortMessage.MessageId = request.PortMessage.MessageId;
if( InterlockedDecrement( &DfsWaitingApiThreads ) == 0 ) {
HANDLE threadHandle; DWORD threadId;
//
// Are there other threads ready to handle new requests? If not, then
// we should spawn a new thread.
//
InterlockedIncrement( &DfsThreads );
threadHandle = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)DfsProcessApisWrapper, (LPVOID)ULongToPtr( DfsThreads ), 0, &threadId );
if ( threadHandle != 0 ) {
CloseHandle( threadHandle );
} else {
InterlockedDecrement( &DfsThreads ); } }
switch ( request.MessageType ) {
case DFSSRV_MESSAGE_GET_SITE_NAME: { ULONG IpAddress = *((ULONG *) &request.Message.GetSiteName.IpAddress.IpData);
#if DBG
if (DfsSvcVerbose) DbgPrint("Asking for SiteName for %d.%d.%d.%d\n", IpAddress & 0xff, (IpAddress >> 8) & 0xff, (IpAddress >> 16) & 0xff, (IpAddress >> 24) & 0xff); #endif
SiteNames = NULL;
#define _Dfs_LocalAddress 0x0100007f
if ((request.Message.GetSiteName.IpAddress.IpLen == 4) && (IpAddress == _Dfs_LocalAddress)) { LPWSTR SiteName;
status = DsGetSiteName( NULL, &SiteName ); if (status == NO_ERROR) { DfsLoadIpCache(&request.Message.GetSiteName.IpAddress, SiteName); NetApiBufferFree(SiteName); } } else { status = AddrToSite( &request.Message.GetSiteName.IpAddress, &SiteNames);
if (status == NO_ERROR) {
if (SiteNames != NULL && SiteNames[0] != NULL) {
DfsLoadIpCache(&request.Message.GetSiteName.IpAddress, SiteNames[0]);
}
if (SiteNames != NULL) {
NetApiBufferFree(SiteNames); } } }
reply.Message.Result.Status = status;
break; }
case DFSSRV_MESSAGE_GET_DOMAIN_REFERRAL: { LPWSTR FtName = request.Message.GetFtDfs.FtName;
#if DBG
if (DfsSvcVerbose) DbgPrint("Asking for DOMAIN REFERRAL for %ws\n", FtName); #endif
status = DfsDomainReferral( DomainName, FtName);
reply.Message.Result.Status = status;
break; }
case DFSSRV_MESSAGE_GET_SPC_ENTRY: { LPWSTR SpcName = request.Message.GetSpcName.SpcName; ULONG Flags = request.Message.GetSpcName.Flags;
#if DBG
if (DfsSvcVerbose) DbgPrint("Asking for SPC EXPANSION for %ws\n", SpcName); #endif
status = DfsGetTrustedDomainDCs( SpcName, Flags);
reply.Message.Result.Status = status;
break; }
default: /* NOTHING */;
}
}
}
VOID DfsLoadIpCache( PDFS_IPADDRESS pIpAddress, LPWSTR SiteName) { NTSTATUS NtStatus; IO_STATUS_BLOCK IoStatusBlock; PDFS_CREATE_IP_INFO_ARG pInfoArg; ULONG size;
size = sizeof(DFS_CREATE_IP_INFO_ARG) + wcslen(SiteName) * sizeof(WCHAR); pInfoArg = malloc(size);
if (pInfoArg != NULL) {
RtlZeroMemory(pInfoArg, size);
pInfoArg->SiteName.Buffer = (LPWSTR)&pInfoArg[1]; pInfoArg->SiteName.Length = wcslen(SiteName) * sizeof(WCHAR); pInfoArg->SiteName.MaximumLength = pInfoArg->SiteName.Length; RtlCopyMemory(pInfoArg->SiteName.Buffer,SiteName,pInfoArg->SiteName.Length);
pInfoArg->SiteName.Buffer = (LPWSTR)sizeof(DFS_CREATE_IP_INFO_ARG);
pInfoArg->IpAddress = *pIpAddress;
NtStatus = NtFsControlFile( serverHandle, NULL, // Event,
NULL, // ApcRoutine,
NULL, // ApcContext,
&IoStatusBlock, FSCTL_DFS_CREATE_IP_INFO, pInfoArg, size, NULL, 0 );
free(pInfoArg);
}
}
NTSTATUS AddrToSite( PDFS_IPADDRESS IpAddress, LPWSTR **pppSiteName) { DWORD dwErr; PSOCKET_ADDRESS pSockAddr; SOCKET_ADDRESS SockAddr; PSOCKADDR_IN pSockAddrIn; SOCKADDR_IN SockAddrIn; WCHAR **SiteNames; DWORD cRetry; PDOMAIN_CONTROLLER_INFO pDCInfo;
pSockAddr = &SockAddr; pSockAddr->iSockaddrLength = sizeof(SOCKADDR_IN); pSockAddr->lpSockaddr = (LPSOCKADDR)&SockAddrIn; pSockAddrIn = &SockAddrIn; pSockAddrIn->sin_family = IpAddress->IpFamily; pSockAddrIn->sin_port = 0; RtlCopyMemory( &pSockAddrIn->sin_addr, IpAddress->IpData, (IpAddress->IpLen & 0xff));
if (DfsMachineRole == DsRole_RoleBackupDomainController || DfsMachineRole == DsRole_RolePrimaryDomainController) {
dwErr = DsAddressToSiteNames( NULL, 1, pSockAddr, pppSiteName);
goto exit;
}
//
// Call DsGetDcName() with ever-increasing urgency, until either
// we get a good DC or we just give up.
//
for (cRetry = 0; cRetry < (sizeof(dwFlags) / sizeof(dwFlags[1])); cRetry++) {
dwErr = DsGetDcName( NULL, // Computer to remote to
NULL, // Domain - use local domain
NULL, // Domain Guid
NULL, // Site Guid
dwFlags[cRetry], // Flags
&pDCInfo);
if (dwErr == ERROR_SUCCESS) {
dwErr = DsAddressToSiteNames( pDCInfo->DomainControllerAddress, 1, pSockAddr, pppSiteName);
NetApiBufferFree( pDCInfo );
if (dwErr == ERROR_SUCCESS) {
goto exit;
}
}
}
exit:
return dwErr;
}
|