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.
4161 lines
108 KiB
4161 lines
108 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
nb.c
|
|
|
|
Abstract:
|
|
|
|
This module contains code which defines the NetBIOS driver's
|
|
device object.
|
|
|
|
Author:
|
|
|
|
Colin Watson (ColinW) 13-Mar-1991
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "nb.h"
|
|
//#include <zwapi.h>
|
|
//#include <ntos.h>
|
|
|
|
typedef ADAPTER_STATUS UNALIGNED *PUADAPTER_STATUS;
|
|
typedef NAME_BUFFER UNALIGNED *PUNAME_BUFFER;
|
|
typedef SESSION_HEADER UNALIGNED *PUSESSION_HEADER;
|
|
typedef SESSION_BUFFER UNALIGNED *PUSESSION_BUFFER;
|
|
|
|
#if DBG
|
|
ULONG NbDebug = 0;
|
|
#endif
|
|
|
|
#if PAGED_DBG
|
|
ULONG ThisCodeCantBePaged;
|
|
#endif
|
|
|
|
PEPROCESS NbFspProcess = NULL;
|
|
|
|
//
|
|
// for PNP the list of devices is not static and hence cannot be read
|
|
// from the registry. A global list of active devices is maintained.
|
|
// This list is updated by the bind and unbind handlers. In addition
|
|
// to the device list the MaxLana and the LanaEnum also need to be
|
|
// updated to reflect the presence/absence of devices.
|
|
//
|
|
|
|
ULONG g_ulMaxLana;
|
|
|
|
LANA_ENUM g_leLanaEnum;
|
|
|
|
PUNICODE_STRING g_pusActiveDeviceList;
|
|
|
|
HANDLE g_hBindHandle;
|
|
|
|
UNICODE_STRING g_usRegistryPath;
|
|
|
|
|
|
//
|
|
// every load of the netapi32.dll results in an open call (IRP_MJ_CREATE)
|
|
// call to the netbios.sys driver. Each open creates an FCB that contains
|
|
// a list of devices, MaxLana and a LanaEnum. Each FCB needs to be updated
|
|
// to reflect the changes to the active device list.
|
|
//
|
|
// In addition the LanaInfo structure corresponding to a Lana that has
|
|
// been unbound needs to be cleaned up.
|
|
//
|
|
|
|
LIST_ENTRY g_leFCBList;
|
|
|
|
ERESOURCE g_erGlobalLock;
|
|
|
|
|
|
//
|
|
// Each application that uses the NETBIOS api (via the netapi32.dll),
|
|
// opens a handle to \\Device\Netbios. This file handle is not closed
|
|
// until netapi32.dll is unloaded.
|
|
//
|
|
// In order to be able to unload netbios.sys these handles have to be
|
|
// closed. To force these handles to be closed, the NETAPI32.DLL now
|
|
// posts an IOCTL (IOCTL_NB_REGISTER) to listen for shutdown
|
|
// notifications. The IRPs corresponding to these IOCTLs are pended,
|
|
//
|
|
// When the driver is being stopped (unloaded), the pended IRPs are
|
|
// completed indicating to netapi32 that it needs to close the open
|
|
// handles on \\Device\netbios.
|
|
//
|
|
// Once all the handles have been closed NETBIOS.SYS can be unloaded.
|
|
//
|
|
|
|
ERESOURCE g_erStopLock; // protects g_ulNumOpens and
|
|
// g_dwnetbiosState
|
|
|
|
DWORD g_dwNetbiosState;
|
|
|
|
ULONG g_ulNumOpens;
|
|
|
|
|
|
LIST_ENTRY g_leWaitList;
|
|
|
|
KEVENT g_keAllHandlesClosed;
|
|
|
|
|
|
|
|
NTSTATUS
|
|
NbAstat(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN ULONG Buffer2Length
|
|
);
|
|
|
|
VOID
|
|
CopyAddresses(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN ULONG Buffer2Length
|
|
);
|
|
|
|
NTSTATUS
|
|
NbFindName(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN ULONG Buffer2Length
|
|
);
|
|
|
|
NTSTATUS
|
|
NbSstat(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN ULONG Buffer2Length
|
|
);
|
|
|
|
VOID
|
|
CopySessionStatus(
|
|
IN PDNCB pdncb,
|
|
IN PCB pcb,
|
|
IN PUSESSION_HEADER pSessionHeader,
|
|
IN PUSESSION_BUFFER* ppSessionBuffer,
|
|
IN PULONG pLengthRemaining
|
|
);
|
|
|
|
NTSTATUS
|
|
NbEnum(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN ULONG Buffer2Length
|
|
);
|
|
|
|
NTSTATUS
|
|
NbReset(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
);
|
|
|
|
NTSTATUS
|
|
NbAction(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
);
|
|
|
|
NTSTATUS
|
|
NbCancel(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
);
|
|
|
|
VOID
|
|
CancelRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
|
|
//
|
|
// Pnp Stop related functions
|
|
//
|
|
|
|
NTSTATUS
|
|
NbRegisterWait(
|
|
IN PIRP pIrp
|
|
);
|
|
|
|
VOID
|
|
CancelIrp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
NbStop(
|
|
);
|
|
|
|
|
|
#if AUTO_RESET
|
|
|
|
NTSTATUS
|
|
NbRegisterReset(
|
|
IN PIRP pIrp,
|
|
IN PIO_STACK_LOCATION pIrpSp
|
|
);
|
|
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#pragma alloc_text(PAGE, NbDispatch)
|
|
#pragma alloc_text(PAGE, NbDeviceControl)
|
|
#pragma alloc_text(PAGE, NbOpen)
|
|
#pragma alloc_text(PAGE, NbClose)
|
|
#pragma alloc_text(PAGE, NbAstat)
|
|
#pragma alloc_text(PAGE, NbEnum)
|
|
#pragma alloc_text(PAGE, NbReset)
|
|
#pragma alloc_text(PAGE, NbFindName)
|
|
#pragma alloc_text(PAGE, AllocateAndCopyUnicodeString)
|
|
|
|
#endif
|
|
|
|
NTSTATUS
|
|
NbCompletionEvent(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does not complete the Irp. It is used to signal to a
|
|
synchronous part of the Netbios driver that it can proceed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - unused.
|
|
|
|
Irp - Supplies Irp that the transport has finished processing.
|
|
|
|
Context - Supplies the event associated with the Irp.
|
|
|
|
Return Value:
|
|
|
|
The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
|
|
processing Irp stack locations at this point.
|
|
|
|
--*/
|
|
{
|
|
IF_NBDBG (NB_DEBUG_COMPLETE) {
|
|
NbPrint( ("NbCompletion event: %lx, Irp: %lx, DeviceObject: %lx\n",
|
|
Context,
|
|
Irp,
|
|
DeviceObject));
|
|
}
|
|
|
|
KeSetEvent((PKEVENT )Context, 0, FALSE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
UNREFERENCED_PARAMETER( Irp );
|
|
}
|
|
|
|
NTSTATUS
|
|
FindNameCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes the TDI Irp used to issue a Find Name to netbt.
|
|
It's main job is to clear the MdlAddress field in the IRP since it was
|
|
borrowed from the original user mode IRP.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - unused.
|
|
|
|
Irp - Supplies Irp that the transport has finished processing.
|
|
|
|
Context - User supplied context arg (not used)
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
IF_NBDBG (NB_DEBUG_COMPLETE) {
|
|
NbPrint( ("FindNameCompletion: Irp: %lx, DeviceObject: %lx\n",
|
|
Irp,
|
|
DeviceObject));
|
|
}
|
|
|
|
Irp->MdlAddress = NULL;
|
|
return STATUS_SUCCESS;
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
UNREFERENCED_PARAMETER( Context );
|
|
}
|
|
|
|
NTSTATUS
|
|
NbCompletionPDNCB(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes the Irp by setting the length and status bytes
|
|
in the NCB supplied in context.
|
|
|
|
Send requests have additional processing to remove the send request from
|
|
the connection block send list.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - unused.
|
|
|
|
Irp - Supplies Irp that the transport has finished processing.
|
|
|
|
Context - Supplies the NCB associated with the Irp.
|
|
|
|
Return Value:
|
|
|
|
The final status from the operation (success or an exception).
|
|
|
|
--*/
|
|
{
|
|
PDNCB pdncb = (PDNCB) Context;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
|
|
IF_NBDBG (NB_DEBUG_COMPLETE) {
|
|
NbPrint(("NbCompletionPDNCB pdncb: %lx, Command: %lx, Lana: %lx,"
|
|
"Status: %lx, Length: %lx\n",
|
|
Context,
|
|
pdncb-> ncb_command,
|
|
pdncb-> ncb_lana_num,
|
|
Irp->IoStatus.Status,
|
|
Irp->IoStatus.Information));
|
|
}
|
|
|
|
// Tell application how many bytes were transferred
|
|
pdncb->ncb_length = (unsigned short)Irp->IoStatus.Information;
|
|
|
|
if ( NT_SUCCESS(Irp->IoStatus.Status) ) {
|
|
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
|
|
} else {
|
|
|
|
if (((pdncb->ncb_command & ~ASYNCH) == NCBRECV ) ||
|
|
((pdncb->ncb_command & ~ASYNCH) == NCBRECVANY )) {
|
|
|
|
if ( Irp->IoStatus.Status == STATUS_BUFFER_OVERFLOW ) {
|
|
|
|
PIRP LocalIrp = NULL;
|
|
KIRQL OldIrql; // Used when SpinLock held.
|
|
PPCB ppcb;
|
|
PDEVICE_OBJECT LocalDeviceObject;
|
|
|
|
LOCK_SPINLOCK( pdncb->pfcb, OldIrql );
|
|
|
|
//
|
|
// The transport will not indicate again so we must put
|
|
// another receive down if we can.
|
|
// If an Irp cannot be built then BuildReceiveIrp will
|
|
// set ReceiveIndicated.
|
|
//
|
|
|
|
ppcb = FindCb( pdncb->pfcb, pdncb, FALSE );
|
|
|
|
if ( ppcb != NULL ) {
|
|
|
|
LocalDeviceObject = (*ppcb)->DeviceObject;
|
|
|
|
LocalIrp = BuildReceiveIrp( *ppcb );
|
|
|
|
|
|
}
|
|
|
|
UNLOCK_SPINLOCK( pdncb->pfcb, OldIrql );
|
|
|
|
if ( LocalIrp != NULL ) {
|
|
IoCallDriver (LocalDeviceObject, LocalIrp);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NCB_COMPLETE( pdncb, NbMakeNbError( Irp->IoStatus.Status ) );
|
|
|
|
}
|
|
|
|
//
|
|
// Tell IopCompleteRequest how much to copy back when the request
|
|
// completes.
|
|
//
|
|
|
|
Irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt );
|
|
|
|
//
|
|
// Remove the Send request from the send queue. We have to scan
|
|
// the queue because they may be completed out of order if a send
|
|
// is rejected because of resource limitations.
|
|
//
|
|
|
|
if (((pdncb->ncb_command & ~ASYNCH) == NCBSEND ) ||
|
|
((pdncb->ncb_command & ~ASYNCH) == NCBCHAINSEND ) ||
|
|
((pdncb->ncb_command & ~ASYNCH) == NCBSENDNA ) ||
|
|
((pdncb->ncb_command & ~ASYNCH) == NCBCHAINSENDNA )) {
|
|
PLIST_ENTRY SendEntry;
|
|
PPCB ppcb;
|
|
KIRQL OldIrql; // Used when SpinLock held.
|
|
|
|
LOCK_SPINLOCK( pdncb->pfcb, OldIrql );
|
|
|
|
ppcb = FindCb( pdncb->pfcb, pdncb, FALSE );
|
|
|
|
//
|
|
// If the connection block still exists remove the send. If the connection
|
|
// has gone then we no longer need to worry about maintaining the list.
|
|
//
|
|
|
|
if ( ppcb != NULL ) {
|
|
#if DBG
|
|
BOOLEAN Found = FALSE;
|
|
#endif
|
|
PCB pcb = *ppcb;
|
|
|
|
for (SendEntry = pcb->SendList.Flink ;
|
|
SendEntry != &pcb->SendList ;
|
|
SendEntry = SendEntry->Flink) {
|
|
|
|
PDNCB pSend = CONTAINING_RECORD( SendEntry, DNCB, ncb_next);
|
|
|
|
if ( pSend == pdncb ) {
|
|
|
|
#if DBG
|
|
Found = TRUE;
|
|
#endif
|
|
|
|
RemoveEntryList( &pdncb->ncb_next );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
ASSERT( Found == TRUE);
|
|
|
|
//
|
|
// If the session is being hung up then we may wish to cleanup the connection
|
|
// as well. STATUS_HANGUP_REQUIRED will cause the dll to manufacture
|
|
// another hangup. The manufactured hangup will complete along with
|
|
// pcb->pdncbHangup. This method is used to ensure that when a
|
|
// hangup is delayed by an outstanding send and the send finally
|
|
// completes, that the user hangup completes after all operations
|
|
// on the connection.
|
|
//
|
|
|
|
if (( IsListEmpty( &pcb->SendList) ) &&
|
|
( pcb->pdncbHangup != NULL )) {
|
|
|
|
IF_NBDBG (NB_DEBUG_COMPLETE) {
|
|
NbPrint( ("NbCompletionPDNCB Hangup session: %lx\n", ppcb ));
|
|
}
|
|
|
|
Status = STATUS_HANGUP_REQUIRED;
|
|
}
|
|
}
|
|
|
|
UNLOCK_SPINLOCK( pdncb->pfcb, OldIrql );
|
|
}
|
|
|
|
//
|
|
// Must return a non-error status otherwise the IO system will not copy
|
|
// back the NCB into the users buffer.
|
|
//
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
|
|
NbCheckAndCompleteIrp32(Irp);
|
|
|
|
return Status;
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
}
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs initialization of the NetBIOS driver.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by the system.
|
|
|
|
RegistryPath - The name of the Netbios node in the registry.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the initialization operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
NTSTATUS status;
|
|
UNICODE_STRING UnicodeString;
|
|
//STRING AnsiNameString;
|
|
|
|
|
|
//
|
|
// bind handler info.
|
|
//
|
|
|
|
TDI_CLIENT_INTERFACE_INFO tcii;
|
|
PWSTR wsClientName = NETBIOS;
|
|
UNICODE_STRING usClientName;
|
|
UCHAR ucInd = 0;
|
|
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
|
|
#ifdef MEMPRINT
|
|
MemPrintInitialize ();
|
|
#endif
|
|
|
|
//
|
|
// Create the device object for NETBIOS. For now, we simply create
|
|
// \Device\Netbios using a unicode string.
|
|
//
|
|
|
|
NbFspProcess = PsGetCurrentProcess();
|
|
|
|
RtlInitUnicodeString( &UnicodeString, NB_DEVICE_NAME);
|
|
|
|
status = NbCreateDeviceContext (DriverObject,
|
|
&UnicodeString,
|
|
&DeviceContext,
|
|
RegistryPath);
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
NbPrint( ("NbInitialize: Netbios failed to initialize\n"));
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// PnP additions - V Raman
|
|
//
|
|
|
|
//
|
|
// save registry path.
|
|
//
|
|
|
|
g_usRegistryPath.Buffer = (PWSTR) ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof( WCHAR ) * (RegistryPath-> Length + 1),
|
|
'fSBN'
|
|
);
|
|
|
|
if ( g_usRegistryPath.Buffer == NULL )
|
|
{
|
|
NbPrint( (
|
|
"DriverEntry : Netbios failed to allocate memory for registry path\n"
|
|
) );
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
|
|
g_usRegistryPath.MaximumLength =
|
|
sizeof( WCHAR ) * (RegistryPath-> Length + 1);
|
|
|
|
RtlCopyUnicodeString( &g_usRegistryPath, RegistryPath );
|
|
|
|
|
|
//
|
|
// Save lana information.
|
|
//
|
|
|
|
status = GetMaxLana( &g_usRegistryPath, &g_ulMaxLana );
|
|
|
|
if ( !NT_SUCCESS( status ) )
|
|
{
|
|
ExFreePool( g_usRegistryPath.Buffer );
|
|
return status;
|
|
}
|
|
|
|
|
|
//
|
|
// On starup there are no devices and no Lanas enabled.
|
|
//
|
|
|
|
g_leLanaEnum.length = 0;
|
|
|
|
g_pusActiveDeviceList = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof( UNICODE_STRING ) * ( MAX_LANA + 1 ),
|
|
'fSBN'
|
|
);
|
|
|
|
if ( g_pusActiveDeviceList == NULL )
|
|
{
|
|
ExFreePool( g_usRegistryPath.Buffer );
|
|
|
|
NbPrint( (
|
|
"DriverEntry : Netbios failed to allocate memory for device list\n"
|
|
) );
|
|
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
for ( ucInd = 0; ucInd <= MAX_LANA; ucInd++ )
|
|
{
|
|
RtlInitUnicodeString( &g_pusActiveDeviceList[ ucInd ], NULL );
|
|
}
|
|
|
|
|
|
//
|
|
// There are no FCBs.
|
|
//
|
|
|
|
InitializeListHead( &g_leFCBList );
|
|
|
|
ExInitializeResourceLite( &g_erGlobalLock );
|
|
|
|
|
|
|
|
InitializeListHead( &g_leWaitList );
|
|
|
|
ExInitializeResourceLite( &g_erStopLock );
|
|
|
|
KeInitializeEvent( &g_keAllHandlesClosed, SynchronizationEvent, FALSE );
|
|
|
|
g_ulNumOpens = 0;
|
|
|
|
g_dwNetbiosState = NETBIOS_RUNNING;
|
|
|
|
|
|
DeviceContext->Initialized = TRUE;
|
|
|
|
|
|
//
|
|
// set up binding handlers
|
|
//
|
|
|
|
RtlZeroMemory( &tcii, sizeof( TDI_CLIENT_INTERFACE_INFO ) );
|
|
|
|
tcii.TdiVersion = TDI_CURRENT_VERSION;
|
|
|
|
RtlInitUnicodeString( &usClientName, wsClientName );
|
|
tcii.ClientName = &usClientName;
|
|
|
|
|
|
tcii.BindingHandler = NbBindHandler;
|
|
tcii.PnPPowerHandler = NbPowerHandler;
|
|
|
|
status = TdiRegisterPnPHandlers(
|
|
&tcii,
|
|
sizeof( TDI_CLIENT_INTERFACE_INFO ),
|
|
&g_hBindHandle
|
|
);
|
|
|
|
if ( status != STATUS_SUCCESS )
|
|
{
|
|
//
|
|
// failed to register bind/unbind handlers
|
|
//
|
|
|
|
NbPrint( (
|
|
"Netbios : DriverEntry : failed to register Bind handlers %0x\n", status
|
|
) );
|
|
|
|
g_hBindHandle = NULL;
|
|
|
|
|
|
ExDeleteResourceLite( &g_erStopLock );
|
|
|
|
|
|
ExDeleteResourceLite( &g_erGlobalLock );
|
|
|
|
ExFreePool( g_pusActiveDeviceList );
|
|
|
|
ExFreePool( g_usRegistryPath.Buffer );
|
|
|
|
DeviceContext->Initialized = FALSE;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
IF_NBDBG (NB_DEBUG_DISPATCH) {
|
|
NbPrint( ("NbInitialize: Netbios initialized.\n"));
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
VOID
|
|
NbDriverUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the unload routine for the NB device driver.
|
|
In response to an unload request this function deletes the
|
|
"\\device\netbios" created by DriverEntry.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to the driver object created by the system
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS nsStatus;
|
|
|
|
UCHAR ucIndex = 0;
|
|
|
|
|
|
nsStatus = TdiDeregisterPnPHandlers( g_hBindHandle );
|
|
|
|
if ( !NT_SUCCESS( nsStatus ) )
|
|
{
|
|
NbPrint( (
|
|
"Netbios : NbDriverUnload : Failed to de-register bind handler\n"
|
|
) );
|
|
}
|
|
|
|
|
|
//
|
|
// all opens to Netbios have been closed.
|
|
// All devices have been unbound
|
|
// remove all global resources
|
|
//
|
|
|
|
LOCK_GLOBAL();
|
|
|
|
for ( ucIndex = 0; ucIndex < g_ulMaxLana; ucIndex++ )
|
|
{
|
|
if ( g_pusActiveDeviceList[ ucIndex ].Buffer != NULL )
|
|
{
|
|
ExFreePool ( g_pusActiveDeviceList[ ucIndex ].Buffer );
|
|
}
|
|
}
|
|
|
|
ExDeleteResourceLite( &g_erStopLock );
|
|
|
|
ExFreePool( g_pusActiveDeviceList );
|
|
|
|
ExFreePool( g_usRegistryPath.Buffer );
|
|
|
|
UNLOCK_GLOBAL();
|
|
|
|
ExDeleteResourceLite( &g_erGlobalLock );
|
|
|
|
IoDeleteDevice( DriverObject-> DeviceObject );
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
NbDispatch(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the main dispatch routine for the NB device driver.
|
|
It accepts an I/O Request Packet, performs the request, and then
|
|
returns with the appropriate status.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for this driver.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Check to see if NB has been initialized; if not, don't allow any use.
|
|
//
|
|
|
|
DeviceContext = (PDEVICE_CONTEXT)DeviceObject;
|
|
if (!DeviceContext->Initialized) {
|
|
NbCompleteRequest( Irp, STATUS_SUCCESS);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Get a pointer to the current stack location in the IRP. This is where
|
|
// the function codes and parameters are stored.
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
//
|
|
// Case on the function that is being performed by the requestor. If the
|
|
// operation is a valid one for this device, then make it look like it was
|
|
// successfully completed, where possible.
|
|
//
|
|
|
|
switch (IrpSp->MajorFunction) {
|
|
|
|
//
|
|
// The Create function opens a handle that can be used with fsctl's
|
|
// to build all interesting operations.
|
|
//
|
|
|
|
case IRP_MJ_CREATE:
|
|
IF_NBDBG (NB_DEBUG_DISPATCH) {
|
|
NbPrint( ("NbDispatch: IRP_MJ_CREATE.\n"));
|
|
}
|
|
|
|
//
|
|
// check if netbios is in the process of stopping
|
|
//
|
|
|
|
LOCK_STOP();
|
|
|
|
if ( g_dwNetbiosState == NETBIOS_STOPPING )
|
|
{
|
|
//
|
|
// fail the CREATE operation and quit
|
|
//
|
|
|
|
Status = STATUS_NO_SUCH_DEVICE;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
UNLOCK_STOP();
|
|
}
|
|
|
|
else
|
|
{
|
|
//
|
|
// netbios is still running. Increment count of
|
|
// open handles
|
|
//
|
|
|
|
g_ulNumOpens++;
|
|
|
|
IF_NBDBG (NB_DEBUG_DISPATCH)
|
|
{
|
|
NbPrint( ( "[NETBIOS] : NbOpen OpenCount %d\n", g_ulNumOpens ) );
|
|
}
|
|
|
|
UNLOCK_STOP();
|
|
|
|
Status = NbOpen ( DeviceContext, IrpSp );
|
|
Irp->IoStatus.Information = FILE_OPENED;
|
|
|
|
//
|
|
// if NbOpen failed, decrement count and return error
|
|
//
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
LOCK_STOP();
|
|
|
|
g_ulNumOpens--;
|
|
|
|
IF_NBDBG (NB_DEBUG_DISPATCH)
|
|
{
|
|
NbPrint( ( "[NETBIOS] : NbOpen Open Error %lx, numopens : %d\n", Status, g_ulNumOpens ) );
|
|
}
|
|
|
|
//
|
|
// check if netbios is in the process of being stopped
|
|
//
|
|
|
|
if ( ( g_ulNumOpens == 0 ) &&
|
|
( g_dwNetbiosState == NETBIOS_STOPPING ) )
|
|
{
|
|
//
|
|
// signal the stopping thread
|
|
//
|
|
|
|
KeSetEvent( &g_keAllHandlesClosed, 0, FALSE );
|
|
|
|
IF_NBDBG (NB_DEBUG_DISPATCH)
|
|
{
|
|
NbPrint( ( "[NETBIOS] : NbOpen error %lx; ", Status ) );
|
|
NbPrint( ( "Set stop event\n" ) );
|
|
}
|
|
}
|
|
|
|
UNLOCK_STOP();
|
|
}
|
|
}
|
|
break;
|
|
|
|
//
|
|
// The Close function closes a transport , terminates
|
|
// all outstanding transport activity on the transport, and unbinds
|
|
// the from its transport address, if any. If this
|
|
// is the last transport endpoint bound to the address, then
|
|
// the address is removed by the provider.
|
|
//
|
|
|
|
case IRP_MJ_CLOSE:
|
|
IF_NBDBG (NB_DEBUG_DISPATCH) {
|
|
NbPrint( ("NbDispatch: IRP_MJ_CLOSE.\n"));
|
|
}
|
|
|
|
|
|
Status = NbClose( IrpSp);
|
|
|
|
if ( NT_SUCCESS( Status ) )
|
|
{
|
|
|
|
LOCK_STOP();
|
|
|
|
g_ulNumOpens--;
|
|
|
|
IF_NBDBG (NB_DEBUG_DISPATCH)
|
|
{
|
|
NbPrint( ( "[NETBIOS] : NbClose OpenCount %d\n", g_ulNumOpens ) );
|
|
}
|
|
|
|
if ( ( g_ulNumOpens == 0 ) &&
|
|
( g_dwNetbiosState == NETBIOS_STOPPING ) )
|
|
{
|
|
//
|
|
// netbios is shutting down and this is the
|
|
// last open file handle, signal the stopping
|
|
// thread
|
|
//
|
|
|
|
KeSetEvent( &g_keAllHandlesClosed, 0, FALSE );
|
|
|
|
IF_NBDBG (NB_DEBUG_DISPATCH)
|
|
{
|
|
NbPrint( ( "[NETBIOS] : NbClose, Set stop event\n" ) );
|
|
}
|
|
}
|
|
|
|
UNLOCK_STOP();
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// The DeviceControl function is the main path to the transport
|
|
// driver interface. Every TDI request is assigned a minor
|
|
// function code that is processed by this function.
|
|
//
|
|
|
|
case IRP_MJ_DEVICE_CONTROL:
|
|
IF_NBDBG (NB_DEBUG_DISPATCH) {
|
|
NbPrint( ("NbDispatch: IRP_MJ_DEVICE_CONTROL, Irp: %lx.\n", Irp ));
|
|
}
|
|
|
|
Status = NbDeviceControl (DeviceObject, Irp, IrpSp);
|
|
|
|
if ((Status != STATUS_PENDING) &&
|
|
(IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_NB_NCB)) {
|
|
|
|
//
|
|
// Bug # : 340042
|
|
//
|
|
// Set the IoStatus.Information field only for IOCTL_NB_NCB.
|
|
// For other IOCTLs it is either irrelevant or the IOCTL processing
|
|
// will set it itself
|
|
//
|
|
|
|
//
|
|
// Tell IopCompleteRequest how much to copy back when the
|
|
// request completes. We need to do this for cases where
|
|
// NbCompletionPDNCB is not used.
|
|
//
|
|
Irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt );
|
|
}
|
|
|
|
#if DBG
|
|
if ( (Status != STATUS_SUCCESS) &&
|
|
(Status != STATUS_PENDING ) &&
|
|
(Status != STATUS_HANGUP_REQUIRED )) {
|
|
|
|
IF_NBDBG (NB_DEBUG_DISPATCH) {
|
|
NbPrint( ("NbDispatch: Invalid status: %X.\n", Status ));
|
|
ASSERT( FALSE );
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
//
|
|
// Handle the two stage IRP for a file close operation. When the first
|
|
// stage hits, ignore it. We will do all the work on the close Irp.
|
|
//
|
|
|
|
case IRP_MJ_CLEANUP:
|
|
IF_NBDBG (NB_DEBUG_DISPATCH) {
|
|
NbPrint( ("NbDispatch: IRP_MJ_CLEANUP.\n"));
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
IF_NBDBG (NB_DEBUG_DISPATCH) {
|
|
NbPrint( ("NbDispatch: OTHER (DEFAULT).\n"));
|
|
}
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
} /* major function switch */
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
IF_NBDBG (NB_DEBUG_DISPATCH) {
|
|
NbPrint( ("NbDispatch: request PENDING from handler.\n"));
|
|
}
|
|
} else {
|
|
IF_NBDBG (NB_DEBUG_DISPATCH) {
|
|
NbPrint( ("NbDispatch: request COMPLETED by handler.\n"));
|
|
}
|
|
|
|
/*
|
|
* Thunk the NCB back to 32-bit compatible
|
|
* structure if the caller is a 32-bit app.
|
|
*/
|
|
NbCheckAndCompleteIrp32(Irp);
|
|
|
|
NbCompleteRequest( Irp, Status);
|
|
}
|
|
|
|
return Status;
|
|
} /* NbDispatch */
|
|
|
|
NTSTATUS
|
|
NbDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dispatches NetBios request types to different handlers based
|
|
on the minor IOCTL function code in the IRP's current stack location.
|
|
In addition to cracking the minor function code, this routine also
|
|
reaches into the IRP and passes the packetized parameters stored there
|
|
as parameters to the various request handlers so that they are
|
|
not IRP-dependent.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for this driver.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
IrpSp - Pointer to current IRP stack frame.
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PNCB pUsersNcb;
|
|
PDNCB pdncb = NULL;
|
|
PUCHAR Buffer2;
|
|
ULONG Buffer2Length;
|
|
ULONG RequestLength;
|
|
BOOLEAN Is32bitProcess;
|
|
|
|
PAGED_CODE();
|
|
|
|
IF_NBDBG (NB_DEBUG_DEVICE_CONTROL) {
|
|
NbPrint( ("NbDeviceControl: Entered...\n"));
|
|
}
|
|
|
|
switch ( IrpSp->Parameters.DeviceIoControl.IoControlCode )
|
|
{
|
|
case IOCTL_NB_NCB :
|
|
break;
|
|
|
|
case IOCTL_NB_REGISTER_STOP :
|
|
Status = NbRegisterWait( Irp );
|
|
return Status;
|
|
|
|
case IOCTL_NB_STOP :
|
|
Status = NbStop();
|
|
return Status;
|
|
|
|
#if AUTO_RESET
|
|
case IOCTL_NB_REGISTER_RESET :
|
|
Status = NbRegisterReset( Irp, IrpSp );
|
|
return Status;
|
|
#endif
|
|
|
|
default:
|
|
{
|
|
IF_NBDBG (NB_DEBUG_DEVICE_CONTROL)
|
|
{
|
|
NbPrint( ("NbDeviceControl: invalid request type.\n"));
|
|
}
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Caller provided 2 buffers. The first is the NCB.
|
|
// The second is an optional buffer for send or receive data.
|
|
// Since the Netbios driver only operates in the context of the
|
|
// calling application, these buffers are directly accessable.
|
|
// however they can be deleted by the user so try-except clauses are
|
|
// required.
|
|
//
|
|
|
|
pUsersNcb = (PNCB)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
RequestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
Buffer2 = Irp->UserBuffer;
|
|
Buffer2Length = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
#if defined(_WIN64)
|
|
Is32bitProcess = IoIs32bitProcess(Irp);
|
|
if (Is32bitProcess == TRUE) {
|
|
if (RequestLength != sizeof( NCB32 )) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
} else {
|
|
#endif
|
|
|
|
if ( RequestLength != sizeof( NCB ) ) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
#if defined(_WIN64)
|
|
}
|
|
#endif
|
|
|
|
try {
|
|
|
|
//
|
|
// Probe the input buffer
|
|
//
|
|
|
|
if (ExGetPreviousMode() != KernelMode) {
|
|
ProbeForWrite(pUsersNcb, RequestLength, 4);
|
|
}
|
|
|
|
//
|
|
// Create a copy of the NCB and convince the IO system to
|
|
// copy it back (and deallocate it) when the IRP completes.
|
|
//
|
|
|
|
Irp->AssociatedIrp.SystemBuffer =
|
|
ExAllocatePoolWithTag( NonPagedPool, sizeof( DNCB ), 'nSBN' );
|
|
|
|
if (Irp->AssociatedIrp.SystemBuffer == NULL) {
|
|
//
|
|
// Since we cannot allocate the drivers copy of the NCB, we
|
|
// must turn around and use the original Ncb to return the error.
|
|
//
|
|
|
|
#if defined(_WIN64)
|
|
if (Is32bitProcess) {
|
|
NCB32 *pUsersNcb32 = (PNCB32)
|
|
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
pUsersNcb32->ncb_retcode = NRC_NORES;
|
|
} else {
|
|
#endif
|
|
pUsersNcb->ncb_retcode = NRC_NORES;
|
|
|
|
#if defined(_WIN64)
|
|
}
|
|
#endif
|
|
|
|
Irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Tell the IO system where to copy the ncb back to during
|
|
// IoCompleteRequest.
|
|
//
|
|
|
|
Irp->Flags |= (ULONG) (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER |
|
|
IRP_INPUT_OPERATION );
|
|
|
|
Irp->UserBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
|
|
// In the driver we should now use our copy of the NCB
|
|
pdncb = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
#if defined(_WIN64)
|
|
if (Is32bitProcess == TRUE) {
|
|
RtlZeroMemory(pdncb, sizeof( DNCB ));
|
|
NbThunkNcb((PNCB32)pUsersNcb, pdncb);
|
|
} else {
|
|
#endif
|
|
RtlMoveMemory( pdncb,
|
|
pUsersNcb,
|
|
FIELD_OFFSET( DNCB, ncb_cmd_cplt )+1 );
|
|
|
|
#if defined(_WIN64)
|
|
}
|
|
#endif
|
|
//
|
|
// Save the users virtual address for the NCB just in case the
|
|
// virtual address is supplied in an NCBCANCEL. This is the same
|
|
// as Irp->UserBuffer.
|
|
//
|
|
|
|
pdncb->users_ncb = pUsersNcb;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
Status = GetExceptionCode();
|
|
IF_NBDBG (NB_DEBUG_DEVICE_CONTROL) {
|
|
NbPrint( ("NbDeviceControl: Exception1 %X.\n", Status));
|
|
}
|
|
|
|
if (pdncb != NULL) {
|
|
NCB_COMPLETE( pdncb, NbMakeNbError(Status) );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
if ( Buffer2Length ) {
|
|
|
|
// Mdl will be freed by IopCompleteRequest.
|
|
Irp->MdlAddress = IoAllocateMdl( Buffer2,
|
|
Buffer2Length,
|
|
FALSE,
|
|
FALSE,
|
|
Irp );
|
|
ASSERT( Irp->MdlAddress != NULL );
|
|
|
|
|
|
//
|
|
// Added by V Raman for bug fix : 127223
|
|
//
|
|
// Check if MDL allocate failed and return.
|
|
//
|
|
|
|
if ( Irp-> MdlAddress == NULL )
|
|
{
|
|
IF_NBDBG(NB_DEBUG_DEVICE_CONTROL)
|
|
NbPrint( ("[NETBIOS] NbDeviceControl: Failed to allocate MDL") );
|
|
|
|
NCB_COMPLETE( pdncb, NRC_NORES );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
try {
|
|
MmProbeAndLockPages( Irp->MdlAddress,
|
|
Irp->RequestorMode,
|
|
(LOCK_OPERATION) IoModifyAccess);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
IF_NBDBG (NB_DEBUG_DEVICE_CONTROL) {
|
|
NbPrint( ("NbDeviceControl: Exception2 %X.\n", Status));
|
|
NbPrint( ("NbDeviceControl: IoContolCode: %lx, Fcb: %lx,"
|
|
" ncb_command %lx, Buffer2Length: %lx\n",
|
|
IrpSp->Parameters.DeviceIoControl.IoControlCode,
|
|
IrpSp->FileObject->FsContext2,
|
|
pdncb->ncb_command,
|
|
Buffer2Length));
|
|
}
|
|
if ( Irp->MdlAddress != NULL ) {
|
|
IoFreeMdl(Irp->MdlAddress);
|
|
Irp->MdlAddress = NULL;
|
|
}
|
|
NCB_COMPLETE( pdncb, NbMakeNbError(Status) );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
ASSERT( Irp->MdlAddress == NULL );
|
|
}
|
|
|
|
IF_NBDBG (NB_DEBUG_DEVICE_CONTROL) {
|
|
NbPrint( ("NbDeviceControl: Fcb: %lx, Ncb: %lx"
|
|
" ncb_command %lx, ncb_lana_num: %lx\n",
|
|
IrpSp->FileObject->FsContext2,
|
|
pdncb,
|
|
pdncb->ncb_command,
|
|
pdncb->ncb_lana_num));
|
|
}
|
|
|
|
switch ( pdncb->ncb_command & ~ASYNCH ) {
|
|
|
|
case NCBCALL:
|
|
case NCALLNIU:
|
|
Status = NbCall( pdncb, Irp, IrpSp );
|
|
break;
|
|
|
|
case NCBCANCEL:
|
|
Status = NbCancel( pdncb, Irp, IrpSp );
|
|
break;
|
|
|
|
case NCBLISTEN:
|
|
Status = NbListen( pdncb, Irp, IrpSp );
|
|
break;
|
|
|
|
case NCBHANGUP:
|
|
Status = NbHangup( pdncb, Irp, IrpSp );
|
|
break;
|
|
|
|
case NCBASTAT:
|
|
Status = NbAstat( pdncb, Irp, IrpSp, Buffer2Length );
|
|
break;
|
|
|
|
case NCBFINDNAME:
|
|
Status = NbFindName( pdncb, Irp, IrpSp, Buffer2Length );
|
|
break;
|
|
|
|
case NCBSSTAT:
|
|
Status = NbSstat( pdncb, Irp, IrpSp, Buffer2Length );
|
|
break;
|
|
|
|
case NCBENUM:
|
|
NbEnum( pdncb, Irp, IrpSp, Buffer2Length );
|
|
break;
|
|
|
|
case NCBRECV:
|
|
Status = NbReceive( pdncb, Irp, IrpSp, Buffer2Length, FALSE, 0 );
|
|
break;
|
|
|
|
case NCBRECVANY:
|
|
Status = NbReceiveAny( pdncb, Irp, IrpSp, Buffer2Length );
|
|
break;
|
|
|
|
case NCBDGRECV:
|
|
case NCBDGRECVBC:
|
|
Status = NbReceiveDatagram( pdncb, Irp, IrpSp, Buffer2Length );
|
|
break;
|
|
|
|
case NCBSEND:
|
|
case NCBSENDNA:
|
|
case NCBCHAINSEND:
|
|
case NCBCHAINSENDNA:
|
|
Status = NbSend( pdncb, Irp, IrpSp, Buffer2Length );
|
|
break;
|
|
|
|
case NCBDGSEND:
|
|
case NCBDGSENDBC:
|
|
Status = NbSendDatagram( pdncb, Irp, IrpSp, Buffer2Length );
|
|
break;
|
|
|
|
case NCBADDNAME:
|
|
case NCBADDGRNAME:
|
|
case NCBQUICKADDNAME:
|
|
case NCBQUICKADDGRNAME:
|
|
NbAddName( pdncb, IrpSp );
|
|
break;
|
|
|
|
case NCBDELNAME:
|
|
NbDeleteName( pdncb, IrpSp );
|
|
break;
|
|
|
|
case NCBLANSTALERT:
|
|
Status = NbLanStatusAlert( pdncb, Irp, IrpSp );
|
|
break;
|
|
|
|
case NCBRESET:
|
|
Status = NbReset( pdncb, Irp, IrpSp );
|
|
break;
|
|
|
|
case NCBACTION:
|
|
Status = NbAction( pdncb, Irp, IrpSp);
|
|
break;
|
|
|
|
// The following are No-operations that return success for compatibility
|
|
case NCBUNLINK:
|
|
case NCBTRACE:
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
break;
|
|
|
|
default:
|
|
NCB_COMPLETE( pdncb, NRC_ILLCMD );
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
|
|
} /* NbDeviceControl */
|
|
|
|
NTSTATUS
|
|
NbOpen(
|
|
IN PDEVICE_CONTEXT DeviceContext,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - Includes the name of the netbios node in the registry.
|
|
|
|
IrpSp - Pointer to current IRP stack frame.
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return NewFcb( DeviceContext, IrpSp );
|
|
} /* NbOpen */
|
|
|
|
|
|
NTSTATUS
|
|
NbClose(
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to close an existing handle. This
|
|
involves running down all of the current and pending activity associated
|
|
with the handle, and dereferencing structures as appropriate.
|
|
|
|
Arguments:
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
IrpSp - Pointer to current IRP stack frame.
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFCB pfcb = IrpSp->FileObject->FsContext2;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (pfcb!=NULL) {
|
|
|
|
CleanupFcb( IrpSp, pfcb );
|
|
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
} /* NbClose */
|
|
|
|
NTSTATUS
|
|
NbAstat(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN ULONG Buffer2Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to return the adapter status. It queries the
|
|
transport for the main adapter status data such as number of FRMR frames
|
|
received and then uses CopyAddresses to fill in the status for the names
|
|
that THIS application has added.
|
|
|
|
Arguments:
|
|
|
|
pdncb - Pointer to the NCB.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
IrpSp - Pointer to current IRP stack frame.
|
|
|
|
Buffer2Length - User provided buffer length for data.
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
TDI_CONNECTION_INFORMATION RequestInformation;
|
|
TA_NETBIOS_ADDRESS ConnectBlock;
|
|
PTDI_ADDRESS_NETBIOS temp;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
BOOLEAN ChangedMode=FALSE;
|
|
PFCB pfcb = IrpSp->FileObject->FsContext2;
|
|
|
|
UNICODE_STRING usDeviceName;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlInitUnicodeString( &usDeviceName, NULL );
|
|
|
|
|
|
if ( Buffer2Length >= sizeof(ADAPTER_STATUS) ) {
|
|
KEVENT Event1;
|
|
NTSTATUS Status;
|
|
HANDLE TdiHandle;
|
|
PFILE_OBJECT TdiObject;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
|
|
RtlInitUnicodeString( &usDeviceName, NULL );
|
|
|
|
|
|
//
|
|
// for PNP
|
|
//
|
|
|
|
LOCK_RESOURCE( pfcb );
|
|
|
|
|
|
if ( ( pdncb->ncb_lana_num > pfcb->MaxLana ) ||
|
|
( pfcb->pDriverName[pdncb->ncb_lana_num].MaximumLength == 0 ) ||
|
|
( pfcb->pDriverName[pdncb->ncb_lana_num].Buffer == NULL ) ) {
|
|
NCB_COMPLETE( pdncb, NRC_BRIDGE );
|
|
UNLOCK_RESOURCE( pfcb );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (( pfcb == NULL ) ||
|
|
(pfcb->ppLana[pdncb->ncb_lana_num] == NULL ) ||
|
|
(pfcb->ppLana[pdncb->ncb_lana_num]->Status != NB_INITIALIZED)) {
|
|
NCB_COMPLETE( pdncb, NRC_ENVNOTDEF ); // need a reset
|
|
UNLOCK_RESOURCE( pfcb );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Status = AllocateAndCopyUnicodeString(
|
|
&usDeviceName, &pfcb->pDriverName[pdncb->ncb_lana_num]
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
NCB_COMPLETE( pdncb, NRC_NORESOURCES );
|
|
UNLOCK_RESOURCE( pfcb );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
UNLOCK_RESOURCE( pfcb );
|
|
|
|
|
|
// NULL returns a handle for doing control functions
|
|
Status = NbOpenAddress (
|
|
&TdiHandle, (PVOID*)&TdiObject, &usDeviceName,
|
|
pdncb->ncb_lana_num, NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
IF_NBDBG (NB_DEBUG_ASTAT) {
|
|
NbPrint(( "\n FAILED on open of Tdi: %X ******\n", Status ));
|
|
}
|
|
NCB_COMPLETE( pdncb, NRC_SYSTEM );
|
|
|
|
ExFreePool( usDeviceName.Buffer );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
KeInitializeEvent (
|
|
&Event1,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( TdiObject );
|
|
|
|
TdiBuildQueryInformation( Irp,
|
|
DeviceObject,
|
|
TdiObject,
|
|
NbCompletionEvent,
|
|
&Event1,
|
|
TDI_QUERY_ADAPTER_STATUS,
|
|
Irp->MdlAddress);
|
|
|
|
if ( pdncb->ncb_callname[0] != '*') {
|
|
//
|
|
// Remote Astat. The variables used to specify the remote adapter name
|
|
// are kept the same as those in connect.c to aid maintenance.
|
|
//
|
|
PIO_STACK_LOCATION NewIrpSp = IoGetNextIrpStackLocation (Irp);
|
|
|
|
ConnectBlock.TAAddressCount = 1;
|
|
ConnectBlock.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
|
|
ConnectBlock.Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS);
|
|
temp = (PTDI_ADDRESS_NETBIOS)ConnectBlock.Address[0].Address;
|
|
|
|
temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
|
|
RtlMoveMemory( temp->NetbiosName, pdncb->ncb_callname, NCBNAMSZ );
|
|
|
|
RequestInformation.RemoteAddress = &ConnectBlock;
|
|
RequestInformation.RemoteAddressLength = sizeof (TRANSPORT_ADDRESS) +
|
|
sizeof (TDI_ADDRESS_NETBIOS);
|
|
((PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&NewIrpSp->Parameters)
|
|
->RequestConnectionInformation = &RequestInformation;
|
|
|
|
PreviousMode = Irp->RequestorMode;
|
|
Irp->RequestorMode = KernelMode;
|
|
ChangedMode=TRUE;
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// Avoid situation where adapter has more names added than the process and
|
|
// then extra names get added to the end of the buffer.
|
|
//
|
|
|
|
//
|
|
// Map the users buffer now so that the whole buffer is mapped (not
|
|
// just sizeof ADAPTER_STATUS).
|
|
//
|
|
|
|
if (Irp->MdlAddress) {
|
|
if (MmGetSystemAddressForMdlSafe(
|
|
Irp->MdlAddress, NormalPagePriority
|
|
) == NULL) {
|
|
|
|
IF_NBDBG (NB_DEBUG_ASTAT) {
|
|
NbPrint(( "\nFAILED on mapping MDL ******\n" ));
|
|
}
|
|
NCB_COMPLETE( pdncb, NRC_SYSTEM );
|
|
ExFreePool( usDeviceName.Buffer );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
} else {
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
Irp->MdlAddress->ByteCount = sizeof(ADAPTER_STATUS);
|
|
|
|
}
|
|
|
|
IoCallDriver (DeviceObject, Irp);
|
|
|
|
if (ChangedMode) {
|
|
Irp->RequestorMode = PreviousMode;
|
|
}
|
|
|
|
do {
|
|
Status = KeWaitForSingleObject(
|
|
&Event1, Executive, KernelMode, TRUE, NULL
|
|
);
|
|
} while (Status == STATUS_ALERTED);
|
|
|
|
|
|
//
|
|
// Restore length now that the transport has filled in no more than
|
|
// is required of it.
|
|
//
|
|
|
|
if (Irp->MdlAddress) {
|
|
Irp->MdlAddress->ByteCount = Buffer2Length;
|
|
}
|
|
|
|
NbAddressClose( TdiHandle, TdiObject );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
NCB_COMPLETE( pdncb, NRC_SYSTEM );
|
|
ExFreePool( usDeviceName.Buffer );
|
|
return Status;
|
|
}
|
|
|
|
Status = Irp->IoStatus.Status;
|
|
if (( Status == STATUS_BUFFER_OVERFLOW ) &&
|
|
( pdncb->ncb_callname[0] == '*')) {
|
|
//
|
|
// This is a local ASTAT. Don't worry if there was not enough room in the
|
|
// users buffer for all the addresses that the transport knows about. There
|
|
// only needs to be space for the names the user has added and we will check
|
|
// that later.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
pdncb->ncb_length = (WORD)Irp->IoStatus.Information;
|
|
NCB_COMPLETE( pdncb, NbMakeNbError(Status) );
|
|
|
|
} else {
|
|
|
|
if ( pdncb->ncb_callname[0] == '*') {
|
|
//
|
|
// Append the addresses and Netbios maintained counts.
|
|
//
|
|
|
|
CopyAddresses(
|
|
pdncb,
|
|
Irp,
|
|
IrpSp,
|
|
Buffer2Length);
|
|
// CopyAddresses completes the NCB appropriately.
|
|
|
|
} else {
|
|
|
|
pdncb->ncb_length = (WORD)Irp->IoStatus.Information;
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
NCB_COMPLETE( pdncb, NRC_BUFLEN );
|
|
}
|
|
|
|
|
|
//
|
|
// Because the completion routine returned STATUS_MORE_PROCESSING_REQUIRED
|
|
// NbAstat must return a status other than STATUS_PENDING so that the
|
|
// users Irp gets completed.
|
|
//
|
|
|
|
if ( usDeviceName.Buffer != NULL )
|
|
{
|
|
ExFreePool( usDeviceName.Buffer );
|
|
}
|
|
|
|
ASSERT( Status != STATUS_PENDING );
|
|
|
|
return Status;
|
|
|
|
UNREFERENCED_PARAMETER( IrpSp );
|
|
}
|
|
|
|
VOID
|
|
CopyAddresses(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN ULONG Buffer2Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to finish the adapter status.
|
|
|
|
Arguments:
|
|
|
|
pdncb - Pointer to the NCB.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
IrpSp - Pointer to current IRP stack frame.
|
|
|
|
Buffer2Length - User provided buffer length for data.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
ULONG LengthRemaining = Buffer2Length - sizeof(ADAPTER_STATUS);
|
|
|
|
PUADAPTER_STATUS pAdapter;
|
|
PUNAME_BUFFER pNameArray;
|
|
int NextEntry = 0; // Used to walk pNameArray
|
|
|
|
PFCB pfcb = IrpSp->FileObject->FsContext2;
|
|
PLANA_INFO plana;
|
|
int index; // Used to access AddressBlocks
|
|
KIRQL OldIrql; // Used when SpinLock held.
|
|
|
|
LOCK( pfcb, OldIrql );
|
|
|
|
plana = pfcb->ppLana[pdncb->ncb_lana_num];
|
|
if ((plana == NULL ) ||
|
|
(plana->Status != NB_INITIALIZED)) {
|
|
NCB_COMPLETE( pdncb, NRC_ENVNOTDEF ); // need a reset
|
|
UNLOCK( pfcb, OldIrql );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Map the users buffer so we can poke around inside
|
|
//
|
|
|
|
if (Irp->MdlAddress) {
|
|
pAdapter = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
|
|
if (pAdapter == NULL) {
|
|
NCB_COMPLETE( pdncb, NRC_NORES );
|
|
UNLOCK( pfcb, OldIrql );
|
|
return;
|
|
}
|
|
} else {
|
|
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
pNameArray = (PUNAME_BUFFER)((PUCHAR)pAdapter + sizeof(ADAPTER_STATUS));
|
|
|
|
pAdapter->rev_major = 0x03;
|
|
pAdapter->rev_minor = 0x00;
|
|
pAdapter->free_ncbs = 255;
|
|
pAdapter->max_cfg_ncbs = 255;
|
|
pAdapter->max_ncbs = 255;
|
|
|
|
pAdapter->pending_sess = 0;
|
|
for ( index = 0; index <= MAXIMUM_CONNECTION; index++ ) {
|
|
if ( plana->ConnectionBlocks[index] != NULL) {
|
|
pAdapter->pending_sess++;
|
|
}
|
|
}
|
|
|
|
pAdapter->max_cfg_sess = (WORD)plana->MaximumConnection;
|
|
pAdapter->max_sess = (WORD)plana->MaximumConnection;
|
|
pAdapter->name_count = 0;
|
|
|
|
// Don't include the reserved address so start at index=2.
|
|
for ( index = 2; index < MAXIMUM_ADDRESS; index++ ) {
|
|
|
|
if ( plana->AddressBlocks[index] != NULL ) {
|
|
|
|
if ( LengthRemaining >= sizeof(NAME_BUFFER) ) {
|
|
|
|
RtlCopyMemory( (PUCHAR)&pNameArray[NextEntry],
|
|
&plana->AddressBlocks[index]->Name,
|
|
sizeof(NAME));
|
|
pNameArray[NextEntry].name_num =
|
|
plana->AddressBlocks[index]->NameNumber;
|
|
pNameArray[NextEntry].name_flags =
|
|
plana->AddressBlocks[index]->Status;
|
|
|
|
LengthRemaining -= sizeof(NAME_BUFFER);
|
|
NextEntry++;
|
|
pAdapter->name_count++;
|
|
|
|
} else {
|
|
|
|
NCB_COMPLETE( pdncb, NRC_INCOMP );
|
|
goto exit;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
|
|
exit:
|
|
pdncb->ncb_length = (unsigned short)( sizeof(ADAPTER_STATUS) +
|
|
( sizeof(NAME_BUFFER) * NextEntry));
|
|
UNLOCK( pfcb, OldIrql );
|
|
}
|
|
|
|
NTSTATUS
|
|
NbFindName(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN ULONG Buffer2Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to return the result of a name query.
|
|
|
|
Arguments:
|
|
|
|
pdncb - Pointer to the NCB.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
IrpSp - Pointer to current IRP stack frame.
|
|
|
|
Buffer2Length - User provided buffer length for data.
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
TDI_CONNECTION_INFORMATION RequestInformation;
|
|
TA_NETBIOS_ADDRESS ConnectBlock;
|
|
PTDI_ADDRESS_NETBIOS temp;
|
|
PFCB pfcb = IrpSp->FileObject->FsContext2;
|
|
|
|
KEVENT Event1;
|
|
HANDLE TdiHandle;
|
|
PFILE_OBJECT TdiObject;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
|
|
UNICODE_STRING usDeviceName;
|
|
|
|
PIRP nbtIrp;
|
|
PIO_STACK_LOCATION nbtIrpSp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
if ((pfcb == NULL) || (Buffer2Length < (sizeof(FIND_NAME_HEADER) + sizeof(FIND_NAME_BUFFER)))) {
|
|
NCB_COMPLETE( pdncb, NRC_BUFLEN );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
RtlInitUnicodeString( &usDeviceName, NULL );
|
|
|
|
LOCK_RESOURCE( pfcb );
|
|
|
|
if (( pdncb->ncb_lana_num > pfcb->MaxLana ) ||
|
|
( pfcb == NULL ) ||
|
|
(pfcb->ppLana[pdncb->ncb_lana_num] == NULL ) ||
|
|
(pfcb->ppLana[pdncb->ncb_lana_num]->Status != NB_INITIALIZED)) {
|
|
UNLOCK_RESOURCE( pfcb );
|
|
NCB_COMPLETE( pdncb, NRC_ENVNOTDEF ); // need a reset
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( ( pfcb->pDriverName[pdncb->ncb_lana_num].MaximumLength == 0 ) ||
|
|
( pfcb->pDriverName[pdncb->ncb_lana_num].Buffer == NULL ) ) {
|
|
UNLOCK_RESOURCE( pfcb );
|
|
NCB_COMPLETE( pdncb, NRC_BRIDGE );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Status = AllocateAndCopyUnicodeString(
|
|
&usDeviceName, &pfcb->pDriverName[pdncb->ncb_lana_num]
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
UNLOCK_RESOURCE( pfcb );
|
|
NCB_COMPLETE( pdncb, NRC_NORESOURCES );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
UNLOCK_RESOURCE( pfcb );
|
|
|
|
|
|
// NULL returns a handle for doing control functions
|
|
Status = NbOpenAddress (
|
|
&TdiHandle, (PVOID*)&TdiObject, &usDeviceName,
|
|
pdncb->ncb_lana_num, NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
IF_NBDBG (NB_DEBUG_ASTAT) {
|
|
NbPrint(( "\n FAILED on open of Tdi: %X ******\n", Status ));
|
|
}
|
|
NCB_COMPLETE( pdncb, NRC_SYSTEM );
|
|
ExFreePool( usDeviceName.Buffer );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
KeInitializeEvent (
|
|
&Event1,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( TdiObject );
|
|
|
|
//
|
|
// DDK sez we shouldn't hijack the user mode IRP. We create one of our own
|
|
// to issue to Netbt for the query.
|
|
//
|
|
nbtIrp = TdiBuildInternalDeviceControlIrp(TdiBuildQueryInformation,
|
|
DeviceObject,
|
|
TdiObject,
|
|
&Event1,
|
|
&ioStatus);
|
|
|
|
if ( nbtIrp == NULL ) {
|
|
IF_NBDBG (NB_DEBUG_ASTAT) {
|
|
NbPrint(( "\n FAILED to allocate internal Irp for Tdi: %X ******\n", ioStatus.Status ));
|
|
}
|
|
NCB_COMPLETE( pdncb, NRC_SYSTEM );
|
|
ExFreePool( usDeviceName.Buffer );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
IF_NBDBG (NB_DEBUG_ASTAT) {
|
|
NbPrint(("NbFindName: Allocated IRP %08x for TdiBuildQueryInfo\n", nbtIrp ));
|
|
}
|
|
|
|
//
|
|
// we use our own find name completion routine. We "borrow" the MDL from
|
|
// the user mode IRP, hence it must be cleared from the TDI IRP before it
|
|
// is completed. Findname's completion routine takes care of that detail.
|
|
//
|
|
TdiBuildQueryInformation( nbtIrp,
|
|
DeviceObject,
|
|
TdiObject,
|
|
FindNameCompletion,
|
|
0,
|
|
TDI_QUERY_FIND_NAME,
|
|
Irp->MdlAddress);
|
|
|
|
nbtIrpSp = IoGetNextIrpStackLocation (nbtIrp);
|
|
|
|
//
|
|
// The variables used to specify the remote adapter name
|
|
// are kept the same as those in connect.c to aid maintenance.
|
|
//
|
|
|
|
ConnectBlock.TAAddressCount = 1;
|
|
ConnectBlock.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
|
|
ConnectBlock.Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS);
|
|
temp = (PTDI_ADDRESS_NETBIOS)ConnectBlock.Address[0].Address;
|
|
|
|
temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
|
|
RtlMoveMemory( temp->NetbiosName, pdncb->ncb_callname, NCBNAMSZ );
|
|
|
|
RequestInformation.RemoteAddress = &ConnectBlock;
|
|
RequestInformation.RemoteAddressLength = sizeof (TRANSPORT_ADDRESS) +
|
|
sizeof (TDI_ADDRESS_NETBIOS);
|
|
((PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&nbtIrpSp->Parameters)
|
|
->RequestConnectionInformation = &RequestInformation;
|
|
|
|
Status = IoCallDriver (DeviceObject, nbtIrp);
|
|
|
|
if ( Status == STATUS_PENDING ) {
|
|
do {
|
|
Status = KeWaitForSingleObject(
|
|
&Event1, Executive, KernelMode, TRUE, NULL
|
|
);
|
|
} while (Status == STATUS_ALERTED);
|
|
}
|
|
|
|
NbAddressClose( TdiHandle, TdiObject );
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ioStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
NCB_COMPLETE( pdncb, NbMakeNbError(Status) );
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
pdncb->ncb_length = (WORD)ioStatus.Information;
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
}
|
|
|
|
//
|
|
// Because the completion routine returned STATUS_MORE_PROCESSING_REQUIRED
|
|
// NbFindName must return a status other than STATUS_PENDING so that the
|
|
// users Irp gets completed.
|
|
//
|
|
|
|
ASSERT( Status != STATUS_PENDING );
|
|
|
|
if ( usDeviceName.Buffer != NULL )
|
|
{
|
|
ExFreePool( usDeviceName.Buffer );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NbSstat(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN ULONG Buffer2Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to return session status. It uses only structures
|
|
internal to this driver.
|
|
|
|
Arguments:
|
|
|
|
pdncb - Pointer to the NCB.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
IrpSp - Pointer to current IRP stack frame.
|
|
|
|
Buffer2Length - User provided buffer length for data.
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if ( Buffer2Length >= sizeof(SESSION_HEADER) ) {
|
|
|
|
PFCB pfcb = IrpSp->FileObject->FsContext2;
|
|
PLANA_INFO plana;
|
|
int index;
|
|
PUSESSION_HEADER pSessionHeader = NULL;
|
|
PUSESSION_BUFFER pSessionBuffer = NULL;
|
|
ULONG LengthRemaining;
|
|
PAB pab;
|
|
KIRQL OldIrql; // Used when SpinLock held.
|
|
|
|
//
|
|
// Prevent indications from the transport, post routines being called
|
|
// and another thread making a request while manipulating the netbios
|
|
// data structures.
|
|
//
|
|
|
|
LOCK( pfcb, OldIrql );
|
|
|
|
if (pdncb->ncb_lana_num > pfcb->MaxLana ) {
|
|
UNLOCK( pfcb, OldIrql );
|
|
NCB_COMPLETE( pdncb, NRC_BRIDGE );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (( pfcb == NULL ) ||
|
|
( pfcb->ppLana[pdncb->ncb_lana_num] == (LANA_INFO *) NULL ) ||
|
|
( pfcb->ppLana[pdncb->ncb_lana_num]->Status != NB_INITIALIZED) ) {
|
|
UNLOCK( pfcb, OldIrql );
|
|
NCB_COMPLETE( pdncb, NRC_BRIDGE );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
plana = pfcb->ppLana[pdncb->ncb_lana_num];
|
|
|
|
if ( pdncb->ncb_name[0] != '*') {
|
|
PPAB ppab = FindAb(pfcb, pdncb, FALSE);
|
|
if ( ppab == NULL) {
|
|
UNLOCK( pfcb, OldIrql );
|
|
pdncb->ncb_retcode = NRC_PENDING;
|
|
NCB_COMPLETE( pdncb, NRC_NOWILD );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
pab = *ppab;
|
|
}
|
|
|
|
//
|
|
// Map the users buffer so we can poke around inside
|
|
//
|
|
|
|
if (Irp->MdlAddress) {
|
|
pSessionHeader = MmGetSystemAddressForMdlSafe(
|
|
Irp->MdlAddress, NormalPagePriority);
|
|
}
|
|
|
|
if ((Irp->MdlAddress == NULL) ||
|
|
(pSessionHeader == NULL)) {
|
|
|
|
UNLOCK( pfcb, OldIrql );
|
|
pdncb->ncb_retcode = NRC_PENDING;
|
|
NCB_COMPLETE( pdncb, NRC_NORES );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pSessionHeader->sess_name = 0;
|
|
pSessionHeader->num_sess = 0;
|
|
pSessionHeader->rcv_dg_outstanding = 0;
|
|
pSessionHeader->rcv_any_outstanding = 0;
|
|
|
|
if ( pdncb->ncb_name[0] == '*') {
|
|
for ( index = 0; index <= MAXIMUM_ADDRESS; index++ ) {
|
|
if ( plana->AddressBlocks[index] != NULL ) {
|
|
PLIST_ENTRY Entry;
|
|
|
|
pab = plana->AddressBlocks[index];
|
|
|
|
for (Entry = pab->ReceiveDatagramList.Flink ;
|
|
Entry != &pab->ReceiveDatagramList ;
|
|
Entry = Entry->Flink) {
|
|
pSessionHeader->rcv_dg_outstanding++ ;
|
|
}
|
|
for (Entry = pab->ReceiveBroadcastDatagramList.Flink ;
|
|
Entry != &pab->ReceiveBroadcastDatagramList ;
|
|
Entry = Entry->Flink) {
|
|
pSessionHeader->rcv_dg_outstanding++ ;
|
|
}
|
|
for (Entry = pab->ReceiveAnyList.Flink ;
|
|
Entry != &pab->ReceiveAnyList ;
|
|
Entry = Entry->Flink) {
|
|
pSessionHeader->rcv_any_outstanding++;
|
|
}
|
|
}
|
|
}
|
|
|
|
pSessionHeader->sess_name = MAXIMUM_ADDRESS;
|
|
|
|
} else {
|
|
PLIST_ENTRY Entry;
|
|
PAB pab255;
|
|
|
|
// Add entries for this name alone.
|
|
for (Entry = pab->ReceiveDatagramList.Flink ;
|
|
Entry != &pab->ReceiveDatagramList ;
|
|
Entry = Entry->Flink) {
|
|
pSessionHeader->rcv_dg_outstanding++ ;
|
|
}
|
|
pab255 = plana->AddressBlocks[MAXIMUM_ADDRESS];
|
|
for (Entry = pab255->ReceiveBroadcastDatagramList.Flink ;
|
|
Entry != &pab255->ReceiveBroadcastDatagramList ;
|
|
Entry = Entry->Flink) {
|
|
PDNCB pdncbEntry = CONTAINING_RECORD( Entry, DNCB, ncb_next);
|
|
if ( pdncbEntry->ncb_num == pab->NameNumber ) {
|
|
pSessionHeader->rcv_dg_outstanding++ ;
|
|
}
|
|
}
|
|
for (Entry = pab->ReceiveAnyList.Flink ;
|
|
Entry != &pab->ReceiveAnyList ;
|
|
Entry = Entry->Flink) {
|
|
pSessionHeader->rcv_any_outstanding++;
|
|
}
|
|
pSessionHeader->sess_name = pab->NameNumber;
|
|
}
|
|
|
|
LengthRemaining = Buffer2Length - sizeof(SESSION_HEADER);
|
|
pSessionBuffer = (PUSESSION_BUFFER)( pSessionHeader+1 );
|
|
|
|
for ( index=1 ; index <= MAXIMUM_CONNECTION; index++ ) {
|
|
CopySessionStatus( pdncb,
|
|
plana->ConnectionBlocks[index],
|
|
pSessionHeader,
|
|
&pSessionBuffer,
|
|
&LengthRemaining);
|
|
|
|
}
|
|
|
|
/* Undocumented Netbios 3.0 feature, returned length == requested
|
|
length and not the length of data returned. The following
|
|
expression gives the number of bytes actually used.
|
|
pdncb->ncb_length = (USHORT)
|
|
(sizeof(SESSION_HEADER)+
|
|
(sizeof(SESSION_BUFFER) * pSessionHeader->num_sess));
|
|
*/
|
|
|
|
UNLOCK( pfcb, OldIrql );
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
|
|
} else {
|
|
NCB_COMPLETE( pdncb, NRC_BUFLEN );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
UNREFERENCED_PARAMETER( IrpSp );
|
|
|
|
}
|
|
|
|
VOID
|
|
CopySessionStatus(
|
|
IN PDNCB pdncb,
|
|
IN PCB pcb,
|
|
IN PUSESSION_HEADER pSessionHeader,
|
|
IN PUSESSION_BUFFER* ppSessionBuffer,
|
|
IN PULONG pLengthRemaining
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to determine if a session should be added
|
|
to the callers buffer and if so it fills in the data. If there is an
|
|
error it records the problem in the callers NCB.
|
|
|
|
Arguments:
|
|
|
|
pdncb - Pointer to the NCB.
|
|
|
|
pcb - Connection Block for a particular session
|
|
|
|
pSessionHeader - Start of the callers buffer
|
|
|
|
ppSessionBuffer - Next position to fill in inside the users buffer.
|
|
|
|
pLengthRemaining - size in bytes remaining to be filled.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
PAB pab;
|
|
PLIST_ENTRY Entry;
|
|
|
|
if ( pcb == NULL ) {
|
|
return;
|
|
}
|
|
|
|
pab = *(pcb->ppab);
|
|
|
|
if (( pdncb->ncb_name[0] == '*') ||
|
|
(RtlEqualMemory( &pab->Name, pdncb->ncb_name, NCBNAMSZ))) {
|
|
|
|
pSessionHeader->num_sess++;
|
|
|
|
if ( *pLengthRemaining < sizeof(SESSION_BUFFER) ) {
|
|
NCB_COMPLETE( pdncb, NRC_INCOMP );
|
|
return;
|
|
}
|
|
|
|
(*ppSessionBuffer)->lsn = pcb->SessionNumber;
|
|
(*ppSessionBuffer)->state = pcb->Status;
|
|
RtlMoveMemory((*ppSessionBuffer)->local_name, &pab->Name, NCBNAMSZ);
|
|
RtlMoveMemory((*ppSessionBuffer)->remote_name, &pcb->RemoteName, NCBNAMSZ);
|
|
|
|
(*ppSessionBuffer)->sends_outstanding = 0;
|
|
(*ppSessionBuffer)->rcvs_outstanding = 0;
|
|
|
|
for (Entry = pcb->SendList.Flink ;
|
|
Entry != &pcb->SendList ;
|
|
Entry = Entry->Flink) {
|
|
(*ppSessionBuffer)->sends_outstanding++;
|
|
}
|
|
|
|
for (Entry = pcb->ReceiveList.Flink ;
|
|
Entry != &pcb->ReceiveList ;
|
|
Entry = Entry->Flink) {
|
|
(*ppSessionBuffer)->rcvs_outstanding++;
|
|
}
|
|
|
|
*ppSessionBuffer +=1;
|
|
*pLengthRemaining -= sizeof(SESSION_BUFFER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
NbEnum(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN ULONG Buffer2Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to discover the available lana numbers.
|
|
|
|
Arguments:
|
|
|
|
pdncb - Pointer to the NCB.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
IrpSp - Pointer to current IRP stack frame.
|
|
|
|
Buffer2Length - Length of user provided buffer for data.
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PUCHAR Buffer2;
|
|
PFCB pfcb = IrpSp->FileObject->FsContext2;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Map the users buffer so we can poke around inside
|
|
//
|
|
|
|
if (Irp->MdlAddress) {
|
|
Buffer2 = MmGetSystemAddressForMdlSafe(Irp->MdlAddress,
|
|
NormalPagePriority);
|
|
if (Buffer2 == NULL) {
|
|
Buffer2Length = 0;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Either a zero byte read/write or the request only has an NCB.
|
|
//
|
|
|
|
Buffer2 = NULL;
|
|
Buffer2Length = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// For PNP
|
|
//
|
|
|
|
LOCK_RESOURCE( pfcb );
|
|
|
|
// Copy over as much information as the user allows.
|
|
|
|
if ( (ULONG)pfcb->LanaEnum.length + 1 > Buffer2Length ) {
|
|
if ( Buffer2Length > 0 ) {
|
|
RtlMoveMemory( Buffer2, &pfcb->LanaEnum, Buffer2Length);
|
|
}
|
|
NCB_COMPLETE( pdncb, NRC_BUFLEN );
|
|
} else {
|
|
RtlMoveMemory(
|
|
Buffer2,
|
|
&pfcb->LanaEnum,
|
|
(ULONG)pfcb->LanaEnum.length + 1 );
|
|
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
}
|
|
|
|
UNLOCK_RESOURCE( pfcb );
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
NbReset(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to reset an adapter. Until an adapter is reset,
|
|
no access to the lan is allowed.
|
|
|
|
Arguments:
|
|
|
|
pdncb - Pointer to the NCB.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
IrpSp - Pointer to current IRP stack frame.
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFCB pfcb = IrpSp->FileObject->FsContext2;
|
|
|
|
BOOLEAN bCleanupLana = FALSE;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
IF_NBDBG (NB_DEBUG_FILE | NB_DEBUG_CREATE_FILE) {
|
|
NbPrint(( "\n**** RRRRRRRRESETT ***** LANA : %x, pdncb %lx\n",
|
|
pdncb-> ncb_lana_num, pdncb ));
|
|
NbPrint(( "FCB : %lx\n", pfcb ));
|
|
}
|
|
|
|
LOCK_RESOURCE( pfcb );
|
|
|
|
// MaxLana is really the last assigned lana number hence > not >=
|
|
if ( pdncb->ncb_lana_num > pfcb->MaxLana) {
|
|
UNLOCK_RESOURCE( pfcb );
|
|
NCB_COMPLETE( pdncb, NRC_BRIDGE );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( pfcb->ppLana[pdncb->ncb_lana_num] != NULL ) {
|
|
bCleanupLana = TRUE;
|
|
}
|
|
|
|
UNLOCK_RESOURCE( pfcb );
|
|
|
|
|
|
//
|
|
// Wait till all addnames are completed and prevent any new
|
|
// ones while we reset the lana. Note We lock out addnames for all
|
|
// lanas. This is ok since addnames are pretty rare as are resets.
|
|
//
|
|
|
|
KeEnterCriticalRegion();
|
|
|
|
ExAcquireResourceExclusiveLite( &pfcb->AddResource, TRUE);
|
|
|
|
IF_NBDBG (NB_DEBUG_CALL) {
|
|
NbPrint(( "\nNbReset have resource exclusive\n" ));
|
|
}
|
|
|
|
if ( bCleanupLana ) {
|
|
CleanupLana( pfcb, pdncb->ncb_lana_num, TRUE);
|
|
}
|
|
|
|
if ( pdncb->ncb_lsn == 0 ) {
|
|
// Allocate resources
|
|
OpenLana( pdncb, Irp, IrpSp );
|
|
} else {
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
}
|
|
|
|
// Allow more addnames
|
|
ExReleaseResourceLite( &pfcb->AddResource );
|
|
|
|
KeLeaveCriticalRegion();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NbAction(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to access a transport specific extension. Netbios does not know
|
|
anything about what the extension does.
|
|
|
|
Arguments:
|
|
|
|
pdncb - Pointer to the NCB.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
IrpSp - Pointer to current IRP stack frame.
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFCB pfcb = IrpSp->FileObject->FsContext2;
|
|
PCB pcb;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
KIRQL OldIrql; // Used when SpinLock held.
|
|
|
|
IF_NBDBG (NB_DEBUG_CALL) {
|
|
NbPrint(( "\n****** Start of NbAction ****** pdncb %lx\n", pdncb ));
|
|
}
|
|
|
|
//
|
|
// The operation can only be performed on one handle so if the NCB specifies both
|
|
// a connection and an address then reject the request.
|
|
//
|
|
|
|
if (( pdncb->ncb_lsn != 0) &&
|
|
( pdncb->ncb_num != 0)) {
|
|
NCB_COMPLETE( pdncb, NRC_ILLCMD ); // No really good errorcode for this
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( pdncb->ncb_length < sizeof(ACTION_HEADER) ) {
|
|
NCB_COMPLETE( pdncb, NRC_BUFLEN );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( (ULONG_PTR)pdncb->ncb_buffer & 3 ) {
|
|
NCB_COMPLETE( pdncb, NRC_BADDR ); // Buffer not word aligned
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
LOCK( pfcb, OldIrql );
|
|
|
|
if ( pdncb->ncb_lana_num > pfcb->MaxLana) {
|
|
UNLOCK( pfcb, OldIrql );
|
|
NCB_COMPLETE( pdncb, NRC_BRIDGE );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pdncb->irp = Irp;
|
|
pdncb->pfcb = pfcb;
|
|
|
|
if ( pdncb->ncb_lsn != 0) {
|
|
// Use handle associated with this connection
|
|
PPCB ppcb;
|
|
|
|
ppcb = FindCb( pfcb, pdncb, FALSE);
|
|
|
|
if ( ppcb == NULL ) {
|
|
// FindCb has put the error in the NCB
|
|
UNLOCK( pfcb, OldIrql );
|
|
if ( pdncb->ncb_retcode == NRC_SCLOSED ) {
|
|
// Tell dll to hangup the connection.
|
|
return STATUS_HANGUP_REQUIRED;
|
|
} else {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
pcb = *ppcb;
|
|
|
|
if ( (pcb->DeviceObject == NULL) || (pcb->ConnectionObject == NULL)) {
|
|
UNLOCK( pfcb, OldIrql );
|
|
NCB_COMPLETE( pdncb, NRC_SCLOSED );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
TdiBuildAction (Irp,
|
|
pcb->DeviceObject,
|
|
pcb->ConnectionObject,
|
|
NbCompletionPDNCB,
|
|
pdncb,
|
|
Irp->MdlAddress);
|
|
|
|
DeviceObject = pcb->DeviceObject;
|
|
|
|
UNLOCK( pfcb, OldIrql );
|
|
|
|
IoMarkIrpPending( Irp );
|
|
IoCallDriver (DeviceObject, Irp);
|
|
|
|
IF_NBDBG (NB_DEBUG_ACTION) {
|
|
NbPrint(( "NB ACTION submit connection: %X\n", Irp->IoStatus.Status ));
|
|
}
|
|
|
|
//
|
|
// Transport will complete the request. Return pending so that
|
|
// netbios does not complete as well.
|
|
//
|
|
|
|
return STATUS_PENDING;
|
|
} else if ( pdncb->ncb_num != 0) {
|
|
// Use handle associated with this name
|
|
PPAB ppab;
|
|
PAB pab;
|
|
|
|
ppab = FindAbUsingNum( pfcb, pdncb, pdncb->ncb_num );
|
|
|
|
if ( ppab == NULL ) {
|
|
UNLOCK( pfcb, OldIrql );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
pab = *ppab;
|
|
|
|
TdiBuildAction (Irp,
|
|
pab->DeviceObject,
|
|
pab->AddressObject,
|
|
NbCompletionPDNCB,
|
|
pdncb,
|
|
Irp->MdlAddress);
|
|
|
|
DeviceObject = pab->DeviceObject;
|
|
|
|
UNLOCK( pfcb, OldIrql );
|
|
|
|
IoMarkIrpPending( Irp );
|
|
IoCallDriver (DeviceObject, Irp);
|
|
|
|
IF_NBDBG (NB_DEBUG_ACTION) {
|
|
NbPrint(( "NB ACTION submit address: %X\n", Irp->IoStatus.Status ));
|
|
}
|
|
|
|
//
|
|
// Transport will complete the request. Return pending so that
|
|
// netbios does not complete as well.
|
|
//
|
|
|
|
return STATUS_PENDING;
|
|
|
|
} else {
|
|
// Use the control channel
|
|
PLANA_INFO plana;
|
|
|
|
if (( pdncb->ncb_lana_num > pfcb->MaxLana ) ||
|
|
( pfcb == NULL ) ||
|
|
( pfcb->ppLana[pdncb->ncb_lana_num] == NULL) ||
|
|
( pfcb->ppLana[pdncb->ncb_lana_num]->Status != NB_INITIALIZED) ) {
|
|
UNLOCK( pfcb, OldIrql );
|
|
NCB_COMPLETE( pdncb, NRC_BRIDGE );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
plana = pfcb->ppLana[pdncb->ncb_lana_num];
|
|
|
|
TdiBuildAction (Irp,
|
|
plana->ControlDeviceObject,
|
|
plana->ControlFileObject,
|
|
NbCompletionPDNCB,
|
|
pdncb,
|
|
Irp->MdlAddress);
|
|
|
|
DeviceObject = plana->ControlDeviceObject;
|
|
|
|
UNLOCK( pfcb, OldIrql );
|
|
|
|
IoMarkIrpPending( Irp );
|
|
IoCallDriver (DeviceObject, Irp);
|
|
|
|
IF_NBDBG (NB_DEBUG_ACTION) {
|
|
NbPrint(( "NB ACTION submit control: %X\n", Irp->IoStatus.Status ));
|
|
}
|
|
|
|
//
|
|
// Transport will complete the request. Return pending so that
|
|
// netbios does not complete as well.
|
|
//
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
NbCancel(
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to cancel the ncb pointed to by NCB_BUFFER.
|
|
|
|
Arguments:
|
|
|
|
pdncb - Pointer to the NCB.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
IrpSp - Pointer to current IRP stack frame.
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFCB pfcb = IrpSp->FileObject->FsContext2;
|
|
PDNCB target; // Mapped in location of the USERS NCB. Not the drivers copy of the DNCB!
|
|
BOOL SpinLockHeld;
|
|
KIRQL OldIrql; // Used when SpinLock held.
|
|
|
|
IF_NBDBG (NB_DEBUG_CALL) {
|
|
NbPrint(( "\n****** Start of NbCancel ****** pdncb %lx\n", pdncb ));
|
|
}
|
|
|
|
|
|
LOCK( pfcb, OldIrql );
|
|
SpinLockHeld = TRUE;
|
|
|
|
if ( pdncb->ncb_lana_num > pfcb->MaxLana) {
|
|
UNLOCK( pfcb, OldIrql );
|
|
NCB_COMPLETE( pdncb, NRC_BRIDGE );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
if (( pfcb->ppLana[pdncb->ncb_lana_num] == NULL ) ||
|
|
( pfcb->ppLana[pdncb->ncb_lana_num]->Status != NB_INITIALIZED) ) {
|
|
UNLOCK( pfcb, OldIrql );
|
|
NCB_COMPLETE( pdncb, NRC_BRIDGE );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// Map the users buffer so we can poke around inside
|
|
//
|
|
|
|
if (Irp->MdlAddress) {
|
|
target = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
|
|
}
|
|
|
|
if ((Irp->MdlAddress == NULL) ||
|
|
(target == NULL )) {
|
|
UNLOCK( pfcb, OldIrql );
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
IF_NBDBG (NB_DEBUG_CALL) {
|
|
NbDisplayNcb( target );
|
|
}
|
|
|
|
try {
|
|
if ( target->ncb_lana_num == pdncb->ncb_lana_num ) {
|
|
switch ( target->ncb_command & ~ASYNCH ) {
|
|
|
|
case NCBCALL:
|
|
case NCALLNIU:
|
|
case NCBLISTEN:
|
|
if ( target->ncb_cmd_cplt != NRC_PENDING ) {
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
} else {
|
|
|
|
PPCB ppcb;
|
|
UCHAR ucLana;
|
|
|
|
UNLOCK_SPINLOCK(pfcb, OldIrql);
|
|
SpinLockHeld = FALSE;
|
|
|
|
//
|
|
// Probe the NCB buffer
|
|
//
|
|
|
|
if (ExGetPreviousMode() != KernelMode) {
|
|
ProbeForRead(pdncb->ncb_buffer, sizeof(NCB), 4);
|
|
}
|
|
|
|
|
|
//
|
|
// Get the Lana number for the NCB being cancelled
|
|
// This is to prevent dereferencing the user buffer
|
|
// once the spinlock has been taken (bug #340218)
|
|
//
|
|
|
|
ucLana = ((PNCB)(pdncb->ncb_buffer))->ncb_lana_num;
|
|
|
|
LOCK_SPINLOCK(pfcb, OldIrql);
|
|
SpinLockHeld = TRUE;
|
|
|
|
//
|
|
// Search for the correct ppcb. We cannot use FindCb
|
|
// because the I/O system will not copy back the ncb_lsn
|
|
// field into target until the I/O request completes.
|
|
//
|
|
|
|
//
|
|
// Note : Though we are passing in the user buffer to
|
|
// the following routine, the buffer is never dereferenced
|
|
// in the routine. It is passed in only for address comp.
|
|
// and should not result in a pagefault ever, (with the
|
|
// spinlock held)
|
|
//
|
|
|
|
ppcb = FindCallCb( pfcb, (PNCB)pdncb->ncb_buffer, ucLana);
|
|
|
|
if (( ppcb == NULL ) ||
|
|
((*ppcb)->pdncbCall->ncb_cmd_cplt != NRC_PENDING ) ||
|
|
(( (*ppcb)->Status != CALL_PENDING ) &&
|
|
( (*ppcb)->Status != LISTEN_OUTSTANDING ))) {
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
} else {
|
|
NCB_COMPLETE( (*ppcb)->pdncbCall, NRC_CMDCAN );
|
|
SpinLockHeld = FALSE;
|
|
(*ppcb)->DisconnectReported = TRUE;
|
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|
CleanupCb( ppcb, NULL );
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NCBHANGUP:
|
|
if ( target->ncb_cmd_cplt != NRC_PENDING ) {
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
} else {
|
|
PPCB ppcb = FindCb( pfcb, target, FALSE );
|
|
if (( ppcb != NULL ) &&
|
|
((*ppcb)->Status == HANGUP_PENDING )) {
|
|
PDNCB pdncbHangup;
|
|
// Restore the session status and remove the hangup.
|
|
(*ppcb)->Status = SESSION_ESTABLISHED;
|
|
pdncbHangup = (*ppcb)->pdncbHangup;
|
|
(*ppcb)->pdncbHangup = NULL;
|
|
if ( pdncbHangup != NULL ) {
|
|
NCB_COMPLETE( pdncbHangup, NRC_CMDCAN );
|
|
pdncbHangup->irp->IoStatus.Information =
|
|
FIELD_OFFSET( DNCB, ncb_cmd_cplt );
|
|
NbCompleteRequest( pdncbHangup->irp ,STATUS_SUCCESS);
|
|
}
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
} else {
|
|
// Doesn't look like this is a real hangup so refuse.
|
|
NCB_COMPLETE( pdncb, NRC_CANCEL );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NCBASTAT:
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
break;
|
|
|
|
case NCBLANSTALERT:
|
|
if ( target->ncb_cmd_cplt != NRC_PENDING ) {
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
} else {
|
|
CancelLanAlert( pfcb, pdncb );
|
|
}
|
|
break;
|
|
|
|
case NCBRECVANY:
|
|
if ( target->ncb_cmd_cplt != NRC_PENDING ) {
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
} else {
|
|
PPAB ppab;
|
|
PLIST_ENTRY Entry;
|
|
|
|
ppab = FindAbUsingNum( pfcb, target, target->ncb_num );
|
|
|
|
if ( ppab == NULL ) {
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
break;
|
|
}
|
|
|
|
for (Entry = (*ppab)->ReceiveAnyList.Flink ;
|
|
Entry != &(*ppab)->ReceiveAnyList;
|
|
Entry = Entry->Flink) {
|
|
|
|
PDNCB pReceive = CONTAINING_RECORD( Entry, DNCB, ncb_next);
|
|
|
|
if ( pReceive->users_ncb == (PNCB)pdncb->ncb_buffer ) {
|
|
PIRP Irp;
|
|
|
|
RemoveEntryList( &pReceive->ncb_next );
|
|
|
|
SpinLockHeld = FALSE;
|
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|
|
|
Irp = pReceive->irp;
|
|
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
|
|
//
|
|
// Remove the cancel request for this IRP. If its cancelled then its
|
|
// ok to just process it because we will be returning it to the caller.
|
|
//
|
|
|
|
Irp->Cancel = FALSE;
|
|
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
NCB_COMPLETE( pReceive, NRC_CMDCAN );
|
|
Irp->IoStatus.Status = STATUS_SUCCESS,
|
|
Irp->IoStatus.Information =
|
|
FIELD_OFFSET( DNCB, ncb_cmd_cplt );
|
|
NbCompleteRequest( Irp, STATUS_SUCCESS );
|
|
|
|
// The receive is cancelled, complete the cancel
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// Command not in receive list!
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
|
|
}
|
|
break;
|
|
|
|
case NCBDGRECV:
|
|
if ( target->ncb_cmd_cplt != NRC_PENDING ) {
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
} else {
|
|
PPAB ppab;
|
|
PLIST_ENTRY Entry;
|
|
|
|
ppab = FindAbUsingNum( pfcb, target, target->ncb_num );
|
|
|
|
if ( ppab == NULL ) {
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
break;
|
|
}
|
|
|
|
for (Entry = (*ppab)->ReceiveDatagramList.Flink ;
|
|
Entry != &(*ppab)->ReceiveDatagramList;
|
|
Entry = Entry->Flink) {
|
|
|
|
PDNCB pReceive = CONTAINING_RECORD( Entry, DNCB, ncb_next);
|
|
|
|
if ( pReceive->users_ncb == (PNCB)pdncb->ncb_buffer ) {
|
|
PIRP Irp;
|
|
|
|
RemoveEntryList( &pReceive->ncb_next );
|
|
|
|
SpinLockHeld = FALSE;
|
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|
|
|
Irp = pReceive->irp;
|
|
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
|
|
//
|
|
// Remove the cancel request for this IRP. If its cancelled then its
|
|
// ok to just process it because we will be returning it to the caller.
|
|
//
|
|
|
|
Irp->Cancel = FALSE;
|
|
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
NCB_COMPLETE( pReceive, NRC_CMDCAN );
|
|
Irp->IoStatus.Status = STATUS_SUCCESS,
|
|
Irp->IoStatus.Information =
|
|
FIELD_OFFSET( DNCB, ncb_cmd_cplt );
|
|
NbCompleteRequest( Irp, STATUS_SUCCESS );
|
|
|
|
// The receive is cancelled, complete the cancel
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// Command not in receive list!
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
|
|
}
|
|
break;
|
|
|
|
case NCBDGRECVBC:
|
|
if ( target->ncb_cmd_cplt != NRC_PENDING ) {
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
} else {
|
|
PPAB ppab;
|
|
PLIST_ENTRY Entry;
|
|
|
|
ppab = FindAbUsingNum( pfcb, target, MAXIMUM_ADDRESS );
|
|
|
|
if ( ppab == NULL ) {
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
break;
|
|
}
|
|
|
|
for (Entry = (*ppab)->ReceiveBroadcastDatagramList.Flink ;
|
|
Entry != &(*ppab)->ReceiveBroadcastDatagramList;
|
|
Entry = Entry->Flink) {
|
|
|
|
PDNCB pReceive = CONTAINING_RECORD( Entry, DNCB, ncb_next);
|
|
|
|
if ( pReceive->users_ncb == (PNCB)pdncb->ncb_buffer ) {
|
|
PIRP Irp;
|
|
|
|
RemoveEntryList( &pReceive->ncb_next );
|
|
|
|
SpinLockHeld = FALSE;
|
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|
|
|
Irp = pReceive->irp;
|
|
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
|
|
//
|
|
// Remove the cancel request for this IRP. If its cancelled then its
|
|
// ok to just process it because we will be returning it to the caller.
|
|
//
|
|
|
|
Irp->Cancel = FALSE;
|
|
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
NCB_COMPLETE( pReceive, NRC_CMDCAN );
|
|
Irp->IoStatus.Status = STATUS_SUCCESS,
|
|
Irp->IoStatus.Information =
|
|
FIELD_OFFSET( DNCB, ncb_cmd_cplt );
|
|
NbCompleteRequest( Irp, STATUS_SUCCESS );
|
|
|
|
// The receive is cancelled, complete the cancel
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// Command not in receive list!
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
|
|
}
|
|
break;
|
|
|
|
// Session cancels close the connection.
|
|
|
|
case NCBRECV:
|
|
case NCBSEND:
|
|
case NCBSENDNA:
|
|
case NCBCHAINSEND:
|
|
case NCBCHAINSENDNA:
|
|
|
|
if ( target->ncb_cmd_cplt != NRC_PENDING ) {
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
} else {
|
|
PPCB ppcb;
|
|
ppcb = FindCb( pfcb, target, FALSE);
|
|
if ( ppcb == NULL ) {
|
|
// No such connection
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
} else {
|
|
PDNCB pTarget = NULL;
|
|
PLIST_ENTRY Entry;
|
|
if ((target->ncb_command & ~ASYNCH) == NCBRECV ) {
|
|
for (Entry = (*ppcb)->ReceiveList.Flink ;
|
|
Entry != &(*ppcb)->ReceiveList;
|
|
Entry = Entry->Flink) {
|
|
|
|
pTarget = CONTAINING_RECORD( Entry, DNCB, ncb_next);
|
|
if ( pTarget->users_ncb == (PNCB)pdncb->ncb_buffer ) {
|
|
break;
|
|
}
|
|
pTarget = NULL;
|
|
|
|
}
|
|
} else {
|
|
for (Entry = (*ppcb)->SendList.Flink ;
|
|
Entry != &(*ppcb)->SendList;
|
|
Entry = Entry->Flink) {
|
|
|
|
pTarget = CONTAINING_RECORD( Entry, DNCB, ncb_next);
|
|
if ( pTarget->users_ncb == (PNCB)pdncb->ncb_buffer ) {
|
|
break;
|
|
}
|
|
pTarget = NULL;
|
|
}
|
|
}
|
|
|
|
if ( pTarget != NULL ) {
|
|
// pTarget points to the real Netbios drivers DNCB.
|
|
NCB_COMPLETE( pTarget, NRC_CMDCAN );
|
|
SpinLockHeld = FALSE;
|
|
(*ppcb)->DisconnectReported = TRUE;
|
|
UNLOCK_SPINLOCK( pfcb, OldIrql );
|
|
CleanupCb( ppcb, NULL );
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
} else {
|
|
NCB_COMPLETE( pdncb, NRC_CANOCCR );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
NCB_COMPLETE( pdncb, NRC_CANCEL ); // Invalid command to cancel
|
|
break;
|
|
|
|
}
|
|
} else {
|
|
NCB_COMPLETE( pdncb, NRC_BRIDGE );
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
if ( SpinLockHeld == TRUE ) {
|
|
UNLOCK( pfcb, OldIrql );
|
|
} else {
|
|
UNLOCK_RESOURCE( pfcb );
|
|
}
|
|
|
|
IF_NBDBG (NB_DEBUG_DEVICE_CONTROL) {
|
|
NTSTATUS Status = GetExceptionCode();
|
|
NbPrint( ("NbCancel: Exception1 %X.\n", Status));
|
|
}
|
|
|
|
NCB_COMPLETE( pdncb, NRC_INVADDRESS );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( SpinLockHeld == TRUE ) {
|
|
UNLOCK( pfcb, OldIrql );
|
|
} else {
|
|
UNLOCK_RESOURCE( pfcb );
|
|
}
|
|
|
|
NCB_COMPLETE( pdncb, NRC_GOODRET );
|
|
|
|
return STATUS_SUCCESS;
|
|
UNREFERENCED_PARAMETER( Irp );
|
|
}
|
|
|
|
VOID
|
|
QueueRequest(
|
|
IN PLIST_ENTRY List,
|
|
IN PDNCB pdncb,
|
|
IN PIRP Irp,
|
|
IN PFCB pfcb,
|
|
IN KIRQL OldIrql,
|
|
IN BOOLEAN Head)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to add a dncb to List.
|
|
|
|
Note: QueueRequest UNLOCKS the fcb. This means the resource and
|
|
spinlock are owned when this routine is called.
|
|
|
|
Arguments:
|
|
|
|
List - List of pdncb's.
|
|
|
|
pdncb - Pointer to the NCB.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
pfcb & OldIrql - Used to free locks
|
|
|
|
Head - TRUE if pdncb should be inserted at head of list
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
pdncb->irp = Irp;
|
|
|
|
pdncb->pfcb = pfcb;
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
|
|
if ( Head == FALSE ) {
|
|
InsertTailList(List, &pdncb->ncb_next);
|
|
} else {
|
|
InsertHeadList(List, &pdncb->ncb_next);
|
|
}
|
|
|
|
if (Irp->Cancel) {
|
|
|
|
//
|
|
// CancelRoutine will lock the resource & spinlock and try to find the
|
|
// request from scratch. It may fail to find the request if it has
|
|
// been picked up by an indication from the transport.
|
|
//
|
|
|
|
UNLOCK( pfcb, OldIrql );
|
|
|
|
CancelRoutine (NULL, Irp);
|
|
|
|
} else {
|
|
|
|
IoSetCancelRoutine(Irp, CancelRoutine);
|
|
|
|
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
|
|
|
UNLOCK( pfcb, OldIrql );
|
|
}
|
|
|
|
}
|
|
|
|
PDNCB
|
|
DequeueRequest(
|
|
IN PLIST_ENTRY List
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to remove a dncb from List.
|
|
|
|
Assume fcb spinlock held.
|
|
|
|
Arguments:
|
|
|
|
List - List of pdncb's.
|
|
|
|
Return Value:
|
|
|
|
PDNCB or NULL.
|
|
|
|
--*/
|
|
{
|
|
PIRP Irp;
|
|
PDNCB pdncb;
|
|
PLIST_ENTRY ReceiveEntry;
|
|
|
|
if (IsListEmpty(List)) {
|
|
//
|
|
// There are no waiting request announcement FsControls, so
|
|
// return success.
|
|
//
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ReceiveEntry = RemoveHeadList( List);
|
|
|
|
pdncb = CONTAINING_RECORD( ReceiveEntry, DNCB, ncb_next);
|
|
|
|
Irp = pdncb->irp;
|
|
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
|
|
//
|
|
// Remove the cancel request for this IRP. If its cancelled then its
|
|
// ok to just process it because we will be returning it to the caller.
|
|
//
|
|
|
|
Irp->Cancel = FALSE;
|
|
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
return pdncb;
|
|
|
|
}
|
|
|
|
VOID
|
|
CancelRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the IO system wants to cancel a queued
|
|
request. The netbios driver queues LanAlerts, Receives and Receive
|
|
Datagrams
|
|
|
|
Arguments:
|
|
|
|
IN PDEVICE_OBJECT DeviceObject - Ignored.
|
|
IN PIRP Irp - Irp to cancel.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PFCB pfcb;
|
|
PDNCB pdncb;
|
|
DNCB LocalCopy;
|
|
PLIST_ENTRY List = NULL;
|
|
PPAB ppab;
|
|
PPCB ppcb;
|
|
PFILE_OBJECT FileObject;
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Clear the cancel routine from the IRP - It can't be cancelled anymore.
|
|
//
|
|
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
|
|
//
|
|
// Remove all the info from the pdncb that we will need to find the
|
|
// request. Once we release the cancel spinlock this request could be
|
|
// completed by another action so it is possible that we will not find
|
|
// the request to cancel.
|
|
//
|
|
|
|
pdncb = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
RtlMoveMemory( &LocalCopy, pdncb, sizeof( DNCB ) );
|
|
IF_NBDBG (NB_DEBUG_IOCANCEL) {
|
|
NbPrint(( "IoCancel Irp %lx\n", Irp ));
|
|
NbDisplayNcb(&LocalCopy);
|
|
}
|
|
|
|
#if DBG
|
|
#ifdef _WIN64
|
|
pdncb = (PDNCB)0xDEADBEEFDEADBEEF;
|
|
#else
|
|
pdncb = (PDNCB)0xDEADBEEF;
|
|
#endif
|
|
#endif
|
|
|
|
pfcb = LocalCopy.pfcb;
|
|
|
|
//
|
|
// Reference the FileObject associated with this Irp. This will stop
|
|
// the callers handle to \device\netbios from closing and therefore
|
|
// the fcb will not get deleted while we try to lock the fcb.
|
|
//
|
|
FileObject = (IoGetCurrentIrpStackLocation (Irp))->FileObject;
|
|
ObReferenceObject(FileObject);
|
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|
|
|
LOCK( pfcb, OldIrql );
|
|
//
|
|
// We now have exclusive access to all CB's and AB's with their associated
|
|
// lists.
|
|
//
|
|
|
|
switch ( LocalCopy.ncb_command & ~ASYNCH ) {
|
|
case NCBRECV:
|
|
|
|
ppcb = FindCb( pfcb, &LocalCopy, TRUE);
|
|
if ( ppcb != NULL ) {
|
|
List = &(*ppcb)->ReceiveList;
|
|
}
|
|
break;
|
|
|
|
case NCBRECVANY:
|
|
ppab = FindAbUsingNum( pfcb, &LocalCopy, LocalCopy.ncb_num );
|
|
if ( ppab != NULL ) {
|
|
List = &(*ppab)->ReceiveAnyList;
|
|
}
|
|
break;
|
|
|
|
case NCBDGRECVBC:
|
|
ppab = FindAbUsingNum( pfcb, &LocalCopy, MAXIMUM_ADDRESS );
|
|
|
|
if ( ppab != NULL ) {
|
|
List = &(*ppab)->ReceiveBroadcastDatagramList;
|
|
}
|
|
break;
|
|
|
|
case NCBDGRECV:
|
|
|
|
ppab = FindAbUsingNum( pfcb, &LocalCopy, LocalCopy.ncb_num );
|
|
|
|
if ( ppab != NULL ) {
|
|
List = &(*ppab)->ReceiveDatagramList;
|
|
}
|
|
break;
|
|
|
|
case NCBLANSTALERT:
|
|
List = &(pfcb->ppLana[LocalCopy.ncb_lana_num]->LanAlertList);
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
if ( List != NULL ) {
|
|
|
|
//
|
|
// We have a list to scan for canceled pdncb's
|
|
//
|
|
|
|
PLIST_ENTRY Entry;
|
|
|
|
RestartScan:
|
|
|
|
for (Entry = List->Flink ;
|
|
Entry != List ;
|
|
Entry = Entry->Flink) {
|
|
|
|
PDNCB p = CONTAINING_RECORD( Entry, DNCB, ncb_next);
|
|
|
|
IoAcquireCancelSpinLock( &p->irp->CancelIrql );
|
|
|
|
if ( p->irp->Cancel ) {
|
|
|
|
RemoveEntryList( &p->ncb_next );
|
|
|
|
NCB_COMPLETE( p, NRC_CMDCAN );
|
|
|
|
p->irp->IoStatus.Status = STATUS_SUCCESS;
|
|
p->irp->IoStatus.Information =
|
|
FIELD_OFFSET( DNCB, ncb_cmd_cplt );
|
|
|
|
IoSetCancelRoutine( p->irp, NULL );
|
|
|
|
IoReleaseCancelSpinLock( p->irp->CancelIrql );
|
|
|
|
IoCompleteRequest( p->irp, IO_NETWORK_INCREMENT);
|
|
goto RestartScan;
|
|
}
|
|
|
|
IoReleaseCancelSpinLock( p->irp->CancelIrql );
|
|
}
|
|
}
|
|
|
|
UNLOCK( pfcb, OldIrql );
|
|
ObDereferenceObject(FileObject);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AllocateAndCopyUnicodeString(
|
|
IN OUT PUNICODE_STRING pusDest,
|
|
IN PUNICODE_STRING pusSource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description :
|
|
This function allocates and copies a unicode string.
|
|
|
|
|
|
Arguements :
|
|
pusDest : Destination that the unicode string is to be copied
|
|
|
|
pusSource : Source string that is to be copied
|
|
|
|
|
|
Return Value :
|
|
STATUS_SUCCESS if function is successful.
|
|
|
|
STATUS_NO_MEMORY if function fails to allocate buffer for the dest.
|
|
|
|
Environment :
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
pusDest-> Buffer = ExAllocatePoolWithTag(
|
|
NonPagedPool, pusSource-> MaximumLength, 'nSBN'
|
|
);
|
|
|
|
if ( pusDest-> Buffer == NULL )
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
pusDest-> MaximumLength = pusSource-> MaximumLength;
|
|
|
|
RtlCopyUnicodeString( pusDest, pusSource );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
NbRegisterWait(
|
|
IN PIRP pIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description :
|
|
This function marks the specified IRP as pending and inserts it into
|
|
the global list of IRP that are waiting for stop notification. These
|
|
IRPs will be completed when netbios is being stopped.
|
|
N.B : NbStop
|
|
|
|
|
|
Arguements :
|
|
pIrp : IRP that needs to be pending until netbios is being stopped
|
|
|
|
|
|
Return value :
|
|
|
|
|
|
Environment :
|
|
This function is invoked in response to a IOCTL_NB_REGISTER sent down
|
|
by a user mode component. Acquires/releases the CancelSpinLock and
|
|
g_keStopLock.
|
|
|
|
--*/
|
|
{
|
|
|
|
KIRQL irql;
|
|
|
|
NTSTATUS status;
|
|
|
|
|
|
LOCK_STOP();
|
|
|
|
IF_NBDBG( NB_DEBUG_DISPATCH )
|
|
{
|
|
NbPrint( ("[NETBIOS]: ENTERED NbRegisterWait, Stop status %d, "
|
|
"Num Opens %d\n", g_dwNetbiosState, g_ulNumOpens ) );
|
|
}
|
|
|
|
|
|
if ( g_dwNetbiosState == NETBIOS_STOPPING )
|
|
{
|
|
//
|
|
// Netbios is shutting down, complete this IRP, right away
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
else
|
|
{
|
|
//
|
|
// setup the cancellation routine and pend this IRP
|
|
//
|
|
|
|
IoAcquireCancelSpinLock( &irql );
|
|
|
|
IoMarkIrpPending( pIrp );
|
|
|
|
InsertTailList( &g_leWaitList, &(pIrp->Tail.Overlay.ListEntry) );
|
|
|
|
IoSetCancelRoutine( pIrp, CancelIrp );
|
|
|
|
IoReleaseCancelSpinLock( irql );
|
|
|
|
status = STATUS_PENDING;
|
|
}
|
|
|
|
UNLOCK_STOP();
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
CancelIrp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description :
|
|
This function cancels an IRP that has been pended on behalf of a
|
|
user mode process. This is invoked when the user-mode process
|
|
the had an open FileHandle to this device closes the handle.
|
|
|
|
Arguments :
|
|
DeviceObject : DeviceObject correponding to the Filehandle that was closed
|
|
|
|
Irp : Pended Irp that is being cancelled.
|
|
|
|
Return Value :
|
|
|
|
|
|
Environment :
|
|
Invoked by the IO subsystem when an open Filehandle to \\Device\netbios is
|
|
closed. This is invoked while holding the CancelSpinLock.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Mark this Irp as cancelled
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Take off our own list
|
|
//
|
|
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|
|
|
//
|
|
// Release cancel spin lock which the IO system acquired
|
|
//
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
NbStop(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
This function initiates the process of stopping the netbios driver.
|
|
It does this by completing the pending stop-notification IRPs. The
|
|
user mode components (netapi32.dll) which have open file handles are
|
|
expected to close these handles when the pending IRPs have been
|
|
completed. After completing the IRPs this function waits for
|
|
all the open handles to be closed.
|
|
|
|
Arguments :
|
|
|
|
Return Value :
|
|
STATUS_SUCCESS if all the handles were closed, STATUS_TIMEOUT if
|
|
the wait timed out.
|
|
|
|
Environment :
|
|
|
|
This function is invoked from Services.exe when the netbios driver
|
|
is to be stopped. This is special case behavior for netbios.
|
|
This function acquires (and releases) the global lock g_erStopLock
|
|
and the CancelSpinLock
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
PIRP pIrp;
|
|
|
|
BOOLEAN bWait = FALSE ;
|
|
|
|
DWORD dwTimeOut = 10000 * 1000 * 15;
|
|
|
|
LARGE_INTEGER TimeOut;
|
|
|
|
KIRQL irql;
|
|
|
|
PLIST_ENTRY pleNode;
|
|
|
|
#if AUTO_RESET
|
|
|
|
PLIST_ENTRY ple;
|
|
|
|
PFCB_ENTRY pfe;
|
|
|
|
PNCB pUsersNCB;
|
|
#endif
|
|
|
|
//
|
|
// Acquire the lock protecting stop related data.
|
|
//
|
|
|
|
LOCK_STOP();
|
|
|
|
//
|
|
// Decrement Num Opens, since an extra open has been performed to
|
|
// send the stop IOCTL
|
|
//
|
|
|
|
g_ulNumOpens--;
|
|
|
|
|
|
IF_NBDBG( NB_DEBUG_DISPATCH )
|
|
{
|
|
NbPrint( ("[NETBIOS]: ENTERED NbStop, Stop status %d, "
|
|
"Num Opens %d\n", g_dwNetbiosState, g_ulNumOpens ) );
|
|
}
|
|
|
|
//
|
|
// set netbios state to stopping
|
|
//
|
|
|
|
g_dwNetbiosState = NETBIOS_STOPPING;
|
|
|
|
if ( g_ulNumOpens )
|
|
{
|
|
//
|
|
// if there are open file handles to \\Device\Netbios,
|
|
// wait for them to close
|
|
//
|
|
|
|
bWait = TRUE;
|
|
}
|
|
|
|
|
|
#if AUTO_RESET
|
|
|
|
LOCK_GLOBAL();
|
|
|
|
#endif
|
|
|
|
//
|
|
// Complete each of the pending IRPs to signal the stop event.
|
|
// This causes netapi32.dll to close the open handles
|
|
//
|
|
|
|
IoAcquireCancelSpinLock( &irql );
|
|
|
|
while ( !IsListEmpty( &g_leWaitList ) )
|
|
{
|
|
pleNode = RemoveHeadList( &g_leWaitList );
|
|
|
|
pIrp = CONTAINING_RECORD( pleNode, IRP, Tail.Overlay.ListEntry );
|
|
|
|
IoSetCancelRoutine( pIrp, NULL );
|
|
|
|
pIrp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
|
|
//
|
|
// release lock to complete the IRP
|
|
//
|
|
|
|
IoReleaseCancelSpinLock( irql );
|
|
|
|
IoCompleteRequest( pIrp, IO_NETWORK_INCREMENT );
|
|
|
|
|
|
//
|
|
// Reaquire the lock
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&irql);
|
|
}
|
|
|
|
#if AUTO_RESET
|
|
|
|
//
|
|
// Complete IRPs that have been pended for notfication
|
|
// of a new LANA (in case the LANA needs to be automatically
|
|
// reset)
|
|
//
|
|
|
|
for ( pleNode = g_leFCBList.Flink;
|
|
pleNode != &g_leFCBList;
|
|
pleNode = pleNode-> Flink )
|
|
{
|
|
pfe = CONTAINING_RECORD( pleNode, FCB_ENTRY, leList );
|
|
|
|
if ( !IsListEmpty( &pfe-> leResetIrp ) )
|
|
{
|
|
ple = RemoveHeadList( &pfe-> leResetIrp );
|
|
|
|
pIrp = CONTAINING_RECORD( ple, IRP, Tail.Overlay.ListEntry );
|
|
|
|
IoSetCancelRoutine( pIrp, NULL );
|
|
|
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
pIrp->IoStatus.Information = sizeof( NCB );
|
|
|
|
|
|
//
|
|
// Set the LANA to be reset to special value since NETBIOS
|
|
// is stopping
|
|
//
|
|
|
|
pUsersNCB = (PNCB) pIrp-> AssociatedIrp.SystemBuffer;
|
|
pUsersNCB->ncb_lana_num = MAX_LANA + 1;
|
|
|
|
|
|
NbCheckAndCompleteIrp32(pIrp);
|
|
//
|
|
// release lock to complete the IRP
|
|
//
|
|
|
|
IoReleaseCancelSpinLock( irql );
|
|
|
|
IoCompleteRequest( pIrp, IO_NETWORK_INCREMENT );
|
|
|
|
|
|
//
|
|
// Reaquire the lock
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&irql);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
IoReleaseCancelSpinLock( irql );
|
|
|
|
|
|
#if AUTO_RESET
|
|
|
|
UNLOCK_GLOBAL();
|
|
|
|
#endif
|
|
|
|
//
|
|
// release stop lock
|
|
//
|
|
|
|
UNLOCK_STOP();
|
|
|
|
|
|
//
|
|
// if there are open file handles wait for them to stop
|
|
//
|
|
|
|
IF_NBDBG( NB_DEBUG_DISPATCH )
|
|
{
|
|
NbPrint( ("[NETBIOS]: NbStop : Wait %d\n", bWait ) );
|
|
}
|
|
|
|
|
|
if ( bWait )
|
|
{
|
|
TimeOut.QuadPart = Int32x32To64( -1, dwTimeOut );
|
|
|
|
do
|
|
{
|
|
ntStatus = KeWaitForSingleObject(
|
|
&g_keAllHandlesClosed, Executive, KernelMode,
|
|
TRUE, &TimeOut
|
|
);
|
|
|
|
} while (ntStatus == STATUS_ALERTED);
|
|
}
|
|
|
|
IF_NBDBG( NB_DEBUG_DISPATCH )
|
|
{
|
|
LOCK_STOP();
|
|
|
|
NbPrint( ("[NETBIOS]: LEAVING NbStop, Stop status %d, "
|
|
"Num Opens %d\n", ntStatus, g_ulNumOpens ) );
|
|
|
|
UNLOCK_STOP();
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
|
|
#if AUTO_RESET
|
|
|
|
NTSTATUS
|
|
NbRegisterReset(
|
|
IN PIRP pIrp,
|
|
IN PIO_STACK_LOCATION pIrpSp
|
|
|
|
)
|
|
/*++
|
|
|
|
Routine Description :
|
|
This function marks the specified IRP as pending and inserts it into
|
|
the global FCB list. This IRP will be completed when an adapter is bound
|
|
to netbios, thereby notifying the user mode of a new adapter.
|
|
|
|
Arguements :
|
|
pIrp : IRP that needs to be pending until an adapater (LANA) is bound
|
|
to netbios
|
|
|
|
|
|
Return value :
|
|
|
|
|
|
Environment :
|
|
This function is invoked in response to a IOCTL_NB_REGISTER_RESET sent down
|
|
by a user mode component. Acquires/releases the CancelSpinLock and
|
|
g_erGlobalLost.
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
PFCB pfcb;
|
|
|
|
PLIST_ENTRY ple;
|
|
|
|
PFCB_ENTRY pfe;
|
|
|
|
PRESET_LANA_ENTRY prle;
|
|
|
|
PNCB pUsersNCB;
|
|
|
|
KIRQL irql;
|
|
|
|
ULONG RequiredLength;
|
|
|
|
|
|
|
|
IF_NBDBG( NB_DEBUG_CREATE_FILE )
|
|
{
|
|
NbPrint( ("\n++++ Netbios : ENTERED NbRegisterReset : ++++\n") );
|
|
}
|
|
|
|
|
|
LOCK_STOP();
|
|
|
|
do
|
|
{
|
|
//
|
|
// Check if Netbios is stopping
|
|
//
|
|
|
|
if ( g_dwNetbiosState == NETBIOS_STOPPING )
|
|
{
|
|
NbPrint( ("[NETBIOS] : NbRegisterReset : Netbios is stopping\n") );
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Acquire the global lock
|
|
//
|
|
|
|
LOCK_GLOBAL();
|
|
|
|
|
|
//
|
|
// find the FCB for the user-mode application that sent down
|
|
// the IOCTL
|
|
//
|
|
|
|
pfcb = pIrpSp-> FileObject-> FsContext2;
|
|
|
|
for ( ple = g_leFCBList.Flink; ple != &g_leFCBList; ple = ple-> Flink )
|
|
{
|
|
pfe = CONTAINING_RECORD( ple, FCB_ENTRY, leList );
|
|
|
|
if ( pfe-> pfcb == pfcb )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// if the FCB is not found, print error and quit
|
|
//
|
|
|
|
if ( ple == &g_leFCBList )
|
|
{
|
|
UNLOCK_GLOBAL();
|
|
|
|
NbPrint(
|
|
("[NETBIOS] : NbRegisterReset : FCB %p not found\n", pfcb )
|
|
);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Fix for bug 297936, buffer validation
|
|
//
|
|
RequiredLength = sizeof(NCB);
|
|
#if defined(_WIN64)
|
|
if (IoIs32bitProcess(pIrp) == TRUE)
|
|
{
|
|
RequiredLength = sizeof(NCB32);
|
|
}
|
|
#endif
|
|
if (pIrpSp-> Parameters.DeviceIoControl.OutputBufferLength < RequiredLength)
|
|
{
|
|
UNLOCK_GLOBAL();
|
|
|
|
NbPrint(
|
|
("[NETBIOS] : NbRegisterReset : Output buffer too small\n")
|
|
);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// If there are outstanding LANA that are queued,
|
|
// - Remove the first one from the queue
|
|
// - Set the LANA in the output buffer for the IRP
|
|
// - complete the IRP
|
|
//
|
|
|
|
if ( !IsListEmpty( &pfe-> leResetList ) )
|
|
{
|
|
ple = RemoveHeadList( &pfe-> leResetList );
|
|
|
|
prle = CONTAINING_RECORD( ple, RESET_LANA_ENTRY, leList );
|
|
|
|
pUsersNCB = (PNCB) pIrp-> AssociatedIrp.SystemBuffer;
|
|
pUsersNCB-> ncb_lana_num = prle-> ucLanaNum;
|
|
pIrp->IoStatus.Information = sizeof( NCB );
|
|
|
|
ExFreePool( prle );
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
UNLOCK_GLOBAL();
|
|
|
|
IF_NBDBG( NB_DEBUG_CREATE_FILE )
|
|
{
|
|
NbPrint( (
|
|
"FCB %p : Reset for LANA %d\n", pfcb,
|
|
pUsersNCB->ncb_lana_num
|
|
) );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// No outstanding LANAs that need reseting
|
|
// - Acquire the Cancel spin lock
|
|
// - Set the Cancel Routine
|
|
//
|
|
|
|
IoAcquireCancelSpinLock( &irql );
|
|
|
|
IoMarkIrpPending( pIrp );
|
|
|
|
InsertTailList( &pfe-> leResetIrp, &(pIrp->Tail.Overlay.ListEntry) );
|
|
|
|
IoSetCancelRoutine( pIrp, CancelIrp );
|
|
|
|
IoReleaseCancelSpinLock( irql );
|
|
|
|
Status = STATUS_PENDING;
|
|
|
|
UNLOCK_GLOBAL();
|
|
|
|
|
|
} while ( FALSE );
|
|
|
|
|
|
UNLOCK_STOP();
|
|
|
|
|
|
IF_NBDBG( NB_DEBUG_CREATE_FILE )
|
|
{
|
|
NbPrint( ("\n++++ Netbios : Exiting NbRegisterReset : %lx ++++\n", Status ) );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
#endif
|
|
|