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.
637 lines
19 KiB
637 lines
19 KiB
/*++
|
|
|
|
Copyright (c) 1989-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
tdihndlr.c
|
|
|
|
Abstract:
|
|
|
|
TDI event handlers
|
|
|
|
Author:
|
|
|
|
Jiandong Ruan
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "ip2netbios.h"
|
|
#include "tdihndlr.tmh"
|
|
|
|
NTSTATUS
|
|
TdiAcceptCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PSMB_TCP_CONNECT TcpConnect
|
|
);
|
|
|
|
void
|
|
Inet_ntoa_nb(
|
|
ULONG Address,
|
|
PCHAR Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description: (Lifted from NBT4 tdihndlr.c)
|
|
|
|
This routine converts an IP address into its "dotted quad" representation. The IP address is
|
|
expected to be in network byte order. No attempt is made to handle the other dotted notions as
|
|
defined in in.h. No error checking is done: all address values are permissible including 0
|
|
and -1. The output string is blank padded to 16 characters to make the name look like a netbios
|
|
name.
|
|
|
|
The string representation is in ANSI, not UNICODE.
|
|
|
|
The caller must allocate the storage, which should be 16 characters.
|
|
|
|
Arguments:
|
|
|
|
Address - IP address in network byte order
|
|
Buffer - Pointer to buffer to receive string representation, ANSI
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
UCHAR byte, c0, c1, c2;
|
|
PCHAR p = Buffer;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
byte = (UCHAR) (Address & 0xff);
|
|
|
|
c0 = byte % 10;
|
|
byte /= 10;
|
|
c1 = byte % 10;
|
|
byte /= 10;
|
|
c2 = byte;
|
|
|
|
if (c2 != 0) {
|
|
*p++ = c2 + '0';
|
|
*p++ = c1 + '0';
|
|
} else if (c1 != 0) {
|
|
*p++ = c1 + '0';
|
|
}
|
|
*p++ = c0 + '0';
|
|
|
|
if (i != 3)
|
|
*p++ = '.';
|
|
|
|
Address >>= 8;
|
|
}
|
|
|
|
// space pad up to 16 characters
|
|
while (p < (Buffer + 16)) {
|
|
*p++ = ' ';
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmbTdiConnectHandler(
|
|
IN PSMB_DEVICE DeviceObject,
|
|
IN LONG RemoteAddressLength,
|
|
IN PTRANSPORT_ADDRESS RemoteAddress,
|
|
IN LONG UserDataLength,
|
|
IN PVOID UserData,
|
|
IN LONG OptionsLength,
|
|
IN PVOID Options,
|
|
OUT CONNECTION_CONTEXT *ConnectionContext,
|
|
OUT PIRP *AcceptIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PSMB_CLIENT_ELEMENT SmbServer = NULL;
|
|
KIRQL Irql;
|
|
PTDI_IND_CONNECT evConnect = NULL;
|
|
PVOID ConEvContext = NULL;
|
|
NTSTATUS status = STATUS_SUCCESS, client_status = STATUS_SUCCESS;
|
|
PVOID ClientConnectContext = NULL;
|
|
PIRP ClientAcceptIrp = NULL;
|
|
PIO_STACK_LOCATION IrpSp = NULL;
|
|
PSMB_CONNECT ConnectObject = NULL;
|
|
PSMB_CLIENT_ELEMENT ClientObject = NULL;
|
|
PSMB_TCP_CONTEXT TcpContext = NULL;
|
|
TA_NETBIOS_ADDRESS TaAddr;
|
|
PTCPSEND_DISPATCH FastSend = NULL;
|
|
BOOL NetbiosNameAllocated = FALSE;
|
|
|
|
UNREFERENCED_PARAMETER(UserDataLength);
|
|
UNREFERENCED_PARAMETER(UserData);
|
|
UNREFERENCED_PARAMETER(OptionsLength);
|
|
UNREFERENCED_PARAMETER(Options);
|
|
|
|
//
|
|
// Some smoke checks before acquiring the lock
|
|
//
|
|
if (SmbCfg.Unloading) {
|
|
status = STATUS_DELETE_PENDING;
|
|
goto error;
|
|
}
|
|
|
|
if (RemoteAddressLength < sizeof(TA_IP_ADDRESS) &&
|
|
RemoteAddressLength < sizeof(TA_IP6_ADDRESS)) {
|
|
status = STATUS_INVALID_ADDRESS;
|
|
goto error;
|
|
}
|
|
|
|
if (RemoteAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_IP &&
|
|
RemoteAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_IP6) {
|
|
status = STATUS_INVALID_ADDRESS_COMPONENT;
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// Prevent SmbServer to go away by referencing the SmbServer
|
|
//
|
|
SMB_ACQUIRE_SPINLOCK(&SmbCfg, Irql);
|
|
SmbServer = DeviceObject->SmbServer;
|
|
if (NULL == SmbServer) {
|
|
SMB_RELEASE_SPINLOCK(&SmbCfg, Irql);
|
|
status = STATUS_REMOTE_NOT_LISTENING;
|
|
goto error;
|
|
}
|
|
SmbReferenceClient(SmbServer, SMB_REF_CONNECT);
|
|
SMB_RELEASE_SPINLOCK(&SmbCfg, Irql);
|
|
|
|
if (SmbServer->PendingAcceptNumber > DeviceObject->MaxBackLog) {
|
|
status = STATUS_NETWORK_BUSY;
|
|
goto error;
|
|
}
|
|
|
|
SMB_ACQUIRE_SPINLOCK(SmbServer, Irql);
|
|
if (!IsListEmpty(&SmbServer->ListenHead)) {
|
|
ASSERTMSG ("TDI_LISTEN isn't supported", 0);
|
|
SMB_RELEASE_SPINLOCK(SmbServer, Irql);
|
|
|
|
status = STATUS_NOT_SUPPORTED;
|
|
goto error;
|
|
}
|
|
evConnect = SmbServer->evConnect;
|
|
ConEvContext = SmbServer->ConEvContext;
|
|
SMB_RELEASE_SPINLOCK(SmbServer, Irql);
|
|
|
|
SmbTrace(SMB_TRACE_CONNECT, ("Receive connect request"));
|
|
|
|
//
|
|
// No Listen
|
|
//
|
|
if (NULL == evConnect || NULL == ConEvContext) {
|
|
status = STATUS_REMOTE_NOT_LISTENING;
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// Srv expects a TDI_ADDRESS_NETBIOS
|
|
//
|
|
TaAddr.TAAddressCount = 1;
|
|
TaAddr.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
|
|
TaAddr.Address[0].AddressLength = sizeof(TDI_ADDRESS_NETBIOS);
|
|
TaAddr.Address[0].Address[0].NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
|
|
TcpContext = NULL;
|
|
if (RemoteAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IP) {
|
|
FastSend = DeviceObject->Tcp4.FastSend;
|
|
TcpContext = SmbAllocateInbound(&DeviceObject->Tcp4);
|
|
Inet_ntoa_nb(((PTA_IP_ADDRESS)RemoteAddress)->Address[0].Address[0].in_addr,
|
|
TaAddr.Address[0].Address[0].NetbiosName);
|
|
|
|
SmbPrint(SMB_TRACE_CONNECT, ("Connect request from %15.15s:%d\n",
|
|
TaAddr.Address[0].Address[0].NetbiosName,
|
|
htons(((PTA_IP_ADDRESS)RemoteAddress)->Address[0].Address[0].sin_port)
|
|
));
|
|
|
|
SmbTrace(SMB_TRACE_CONNECT, ("Connect request from %!ipaddr!:%!port!",
|
|
((PTA_IP_ADDRESS)RemoteAddress)->Address[0].Address[0].in_addr,
|
|
((PTA_IP_ADDRESS)RemoteAddress)->Address[0].Address[0].sin_port
|
|
));
|
|
|
|
} else {
|
|
SmbTrace(SMB_TRACE_CONNECT, ("Connect request from %!IPV6ADDR!:%!port!",
|
|
(struct in6_addr*)&(((PTA_IP6_ADDRESS)RemoteAddress)->Address[0].Address[0].sin6_addr),
|
|
((PTA_IP6_ADDRESS)RemoteAddress)->Address[0].Address[0].sin6_port
|
|
));
|
|
|
|
if (GetNetbiosNameFromIp6Address(((BYTE *)((PTA_IP6_ADDRESS)RemoteAddress)->Address[0].Address[0].sin6_addr),
|
|
TaAddr.Address[0].Address[0].NetbiosName)) {
|
|
|
|
NetbiosNameAllocated = TRUE;
|
|
FastSend = DeviceObject->Tcp6.FastSend;
|
|
TcpContext = SmbAllocateInbound(&DeviceObject->Tcp6);
|
|
|
|
TODO("TdiConnectHandler: convert IP6 address into Netbios address");
|
|
}
|
|
|
|
}
|
|
|
|
if (TcpContext == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto error;
|
|
}
|
|
|
|
ClientConnectContext = NULL;
|
|
ClientAcceptIrp = NULL;
|
|
client_status = (*evConnect)(
|
|
ConEvContext,
|
|
sizeof(TaAddr),
|
|
&TaAddr,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&ClientConnectContext,
|
|
&ClientAcceptIrp
|
|
);
|
|
|
|
SmbTrace(SMB_TRACE_CONNECT, ("evConnect: %!status! pIrp %p ClientContext %p",
|
|
client_status, ClientAcceptIrp, ClientConnectContext));
|
|
|
|
if (client_status != STATUS_MORE_PROCESSING_REQUIRED) {
|
|
SmbFreeInbound(TcpContext);
|
|
goto error;
|
|
}
|
|
|
|
ASSERT(ClientAcceptIrp);
|
|
if (NULL == ClientAcceptIrp) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto error;
|
|
}
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(ClientAcceptIrp);
|
|
ConnectObject = IrpSp->FileObject->FsContext;
|
|
if (IrpSp->FileObject->FsContext2 != UlongToPtr(SMB_TDI_CONNECT) ||
|
|
ConnectObject->State != SMB_IDLE ||
|
|
NULL != ConnectObject->TcpContext ||
|
|
NULL == (ClientObject = ConnectObject->ClientObject)) {
|
|
ClientAcceptIrp->IoStatus.Status = STATUS_INVALID_HANDLE;
|
|
ClientAcceptIrp->IoStatus.Information = 0;
|
|
IoCompleteRequest(ClientAcceptIrp, IO_NETWORK_INCREMENT);
|
|
|
|
SmbFreeInbound(TcpContext);
|
|
|
|
ASSERT (0);
|
|
|
|
status = STATUS_INTERNAL_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
ConnectObject->Originator = FALSE;
|
|
ConnectObject->FastSend = FastSend;
|
|
|
|
SmbReuseConnectObject(ConnectObject);
|
|
|
|
//
|
|
// Reset the disconnection originator informaiton
|
|
//
|
|
ResetDisconnectOriginator(ConnectObject);
|
|
|
|
ASSERT (TcpContext->Address.AddressHandle == NULL);
|
|
ASSERT (TcpContext->Address.AddressObject == NULL);
|
|
ASSERT (TcpContext->Connect.ConnectHandle);
|
|
ASSERT (TcpContext->Connect.ConnectObject);
|
|
|
|
SMB_ACQUIRE_SPINLOCK(DeviceObject, Irql);
|
|
ASSERT(ClientObject == SmbServer);
|
|
SMB_ACQUIRE_SPINLOCK_DPC(ClientObject);
|
|
SMB_ACQUIRE_SPINLOCK_DPC(ConnectObject);
|
|
|
|
ASSERT(EntryIsInList(&ClientObject->AssociatedConnection, &ConnectObject->Linkage));
|
|
ASSERT (ConnectObject->ClientContext == ClientConnectContext);
|
|
|
|
//
|
|
// Don't use SMB_CONNECTING because TCP4 could indicate the data before the AcceptCompletion
|
|
// is called.
|
|
//
|
|
ConnectObject->State = SMB_CONNECTED;
|
|
ConnectObject->TcpContext = TcpContext;
|
|
TcpContext->Connect.pLastUprCnt = TcpContext->Connect.UpperConnect = ConnectObject;
|
|
|
|
RtlCopyMemory(ConnectObject->RemoteName, TaAddr.Address[0].Address[0].NetbiosName, NETBIOS_NAME_SIZE);
|
|
if (RemoteAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IP) {
|
|
ConnectObject->RemoteIpAddress.sin_family = SMB_AF_INET;
|
|
ConnectObject->RemoteIpAddress.ip4.sin4_addr =
|
|
((PTA_IP_ADDRESS)RemoteAddress)->Address[0].Address[0].in_addr;
|
|
} else {
|
|
ConnectObject->RemoteIpAddress.sin_family = SMB_AF_INET6;
|
|
RtlCopyMemory(ConnectObject->RemoteIpAddress.ip6.sin6_addr,
|
|
((PTA_IP6_ADDRESS)RemoteAddress)->Address[0].Address[0].sin6_addr,
|
|
sizeof (ConnectObject->RemoteIpAddress.ip6.sin6_addr));
|
|
ConnectObject->RemoteIpAddress.ip6.sin6_scope_id =
|
|
((PTA_IP6_ADDRESS)RemoteAddress)->Address[0].Address[0].sin6_scope_id;
|
|
}
|
|
|
|
//
|
|
// Move the conection from AssociatedConnection list into PendingAcceptConnection
|
|
//
|
|
RemoveEntryList(&ConnectObject->Linkage);
|
|
InsertTailList(&ClientObject->PendingAcceptConnection, &ConnectObject->Linkage);
|
|
ClientObject->PendingAcceptNumber++;
|
|
ASSERT(ClientObject->PendingAcceptNumber > 0);
|
|
|
|
ConnectObject->PendingIRPs[SMB_PENDING_ACCEPT] = ClientAcceptIrp;
|
|
|
|
SmbReferenceConnect(ConnectObject, SMB_REF_CONNECT);
|
|
SMB_RELEASE_SPINLOCK_DPC(ConnectObject);
|
|
SMB_RELEASE_SPINLOCK_DPC(ClientObject);
|
|
SMB_RELEASE_SPINLOCK(DeviceObject, Irql);
|
|
|
|
TdiBuildAccept(
|
|
ClientAcceptIrp,
|
|
IoGetRelatedDeviceObject(TcpContext->Connect.ConnectObject),
|
|
TcpContext->Connect.ConnectObject,
|
|
TdiAcceptCompletion,
|
|
&TcpContext->Connect,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
*ConnectionContext = &TcpContext->Connect;
|
|
*AcceptIrp = ClientAcceptIrp;
|
|
|
|
IoSetNextIrpStackLocation(ClientAcceptIrp);
|
|
SmbDereferenceClient(SmbServer, SMB_REF_CONNECT);
|
|
|
|
SmbTrace(SMB_TRACE_CONNECT, ("return STATUS_MORE_PROCESSING_REQUIRED to TCP/IP pIrp %p ClientContext %p",
|
|
(*AcceptIrp), (*ConnectionContext)));
|
|
|
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
|
|
|
error:
|
|
SmbTrace(SMB_TRACE_CONNECT, ("Refuse the connection: status=%!status! client status=%!status!",
|
|
status, client_status));
|
|
|
|
if (NetbiosNameAllocated) {
|
|
FreeNetbiosNameForIp6Address(((BYTE *)((PTA_IP6_ADDRESS)RemoteAddress)->Address[0].Address[0].sin6_addr));
|
|
}
|
|
|
|
if (SmbServer) {
|
|
SmbDereferenceClient(SmbServer, SMB_REF_CONNECT);
|
|
}
|
|
return STATUS_CONNECTION_REFUSED;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
TdiAcceptCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PSMB_TCP_CONNECT TcpConnect
|
|
)
|
|
{
|
|
PSMB_CONNECT ConnectObject;
|
|
PSMB_CLIENT_ELEMENT ClientObject;
|
|
KIRQL Irql;
|
|
PSMB_TCP_CONTEXT TcpContext;
|
|
|
|
//
|
|
// For performance consideration: check it before acquiring a lock:
|
|
// This will allow SmbSynchAttackDetection and SmbSynchAttackCleanup
|
|
// to run concurrently most of the time.
|
|
//
|
|
if (NULL == TcpConnect->UpperConnect) {
|
|
goto cleanup2;
|
|
}
|
|
|
|
//
|
|
// Grab the global lock to synchronize with the DoDisconnect
|
|
//
|
|
|
|
SMB_ACQUIRE_SPINLOCK (SmbCfg.SmbDeviceObject, Irql);
|
|
ConnectObject = (PSMB_CONNECT)TcpConnect->UpperConnect;
|
|
if (NULL == ConnectObject) {
|
|
goto cleanup;
|
|
}
|
|
|
|
ClientObject = ConnectObject->ClientObject;
|
|
ASSERT (NULL != ClientObject);
|
|
ASSERT (NULL != ConnectObject->ClientObject);
|
|
ASSERT (TcpConnect == &ConnectObject->TcpContext->Connect);
|
|
|
|
SMB_ACQUIRE_SPINLOCK_DPC(ClientObject);
|
|
SMB_ACQUIRE_SPINLOCK_DPC(ConnectObject);
|
|
|
|
ASSERT (ConnectObject->PendingIRPs[SMB_PENDING_ACCEPT] == Irp);
|
|
ConnectObject->PendingIRPs[SMB_PENDING_ACCEPT] = NULL;
|
|
|
|
ASSERT (EntryIsInList(&ClientObject->PendingAcceptConnection, &ConnectObject->Linkage));
|
|
ASSERT (ClientObject->PendingAcceptNumber > 0);
|
|
|
|
RemoveEntryList(&ConnectObject->Linkage);
|
|
ClientObject->PendingAcceptNumber--;
|
|
|
|
SmbTrace(SMB_TRACE_CONNECT, ("TDI_ACCEPT pIrp %p complete with %!status!",
|
|
Irp, Irp->IoStatus.Status));
|
|
|
|
//
|
|
// The connection could have been disconnected when TCP completes the accepting IRP!!!
|
|
// Only move it to active list if it hasn't been disconnected.
|
|
//
|
|
if (Irp->IoStatus.Status != STATUS_SUCCESS || ConnectObject->State != SMB_CONNECTED) {
|
|
// BREAK_WHEN_TAKE();
|
|
|
|
TcpContext = ConnectObject->TcpContext;
|
|
ConnectObject->TcpContext = NULL;
|
|
SmbDelayedDestroyTcpContext(TcpContext);
|
|
|
|
InsertTailList(&ClientObject->AssociatedConnection, &ConnectObject->Linkage);
|
|
ConnectObject->State = SMB_IDLE;
|
|
if (Irp->IoStatus.Status == STATUS_SUCCESS) {
|
|
Irp->IoStatus.Status = STATUS_CONNECTION_RESET;
|
|
}
|
|
if (ConnectObject->RemoteIpAddress.sin_family == SMB_AF_INET6) {
|
|
FreeNetbiosNameForIp6Address(ConnectObject->RemoteIpAddress.ip6.sin6_addr_bytes);
|
|
ConnectObject->RemoteIpAddress.sin_family = SMB_AF_INVALID_INET6;
|
|
}
|
|
} else {
|
|
InsertTailList(&ClientObject->ActiveConnection, &ConnectObject->Linkage);
|
|
}
|
|
SMB_RELEASE_SPINLOCK_DPC(ConnectObject);
|
|
SMB_RELEASE_SPINLOCK_DPC(ClientObject);
|
|
|
|
cleanup:
|
|
SMB_RELEASE_SPINLOCK(SmbCfg.SmbDeviceObject, Irql);
|
|
if (ConnectObject) {
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_CONNECT);
|
|
}
|
|
|
|
cleanup2:
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
TdiSetEventHandler(
|
|
PFILE_OBJECT FileObject,
|
|
ULONG EventType,
|
|
PVOID EventHandler,
|
|
PVOID Context
|
|
)
|
|
{
|
|
PIRP Irp;
|
|
KEVENT Event;
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
Irp = SmbAllocIrp(DeviceObject->StackSize);
|
|
if (Irp == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
TdiBuildSetEventHandler(
|
|
Irp,
|
|
DeviceObject,
|
|
FileObject,
|
|
SmbSynchTdiCompletion,
|
|
&Event,
|
|
EventType,
|
|
EventHandler,
|
|
Context
|
|
);
|
|
|
|
status = IoCallDriver(DeviceObject, Irp);
|
|
if (!NT_SUCCESS(status)) {
|
|
SmbFreeIrp(Irp);
|
|
return status;
|
|
}
|
|
|
|
if (status == STATUS_PENDING) {
|
|
status = KeWaitForSingleObject(
|
|
&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
ASSERT(status == STATUS_WAIT_0);
|
|
status = Irp->IoStatus.Status;
|
|
}
|
|
SmbFreeIrp(Irp);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbTdiDisconnectHandler (
|
|
IN PSMB_DEVICE DeviceObject,
|
|
IN PSMB_TCP_CONNECT TcpConnect,
|
|
IN LONG DisconnectDataLength,
|
|
IN PVOID DisconnectData,
|
|
IN LONG DisconnectInformationLength,
|
|
IN PVOID DisconnectInformation,
|
|
IN ULONG DisconnectFlags
|
|
)
|
|
{
|
|
KIRQL Irql;
|
|
NTSTATUS status;
|
|
PSMB_CONNECT ConnectObject;
|
|
|
|
//
|
|
// Reference the connection object make sure it won't go away
|
|
//
|
|
SMB_ACQUIRE_SPINLOCK(&SmbCfg, Irql);
|
|
ConnectObject = (PSMB_CONNECT)TcpConnect->UpperConnect;
|
|
if (NULL == ConnectObject) {
|
|
SMB_RELEASE_SPINLOCK(&SmbCfg, Irql);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
SmbReferenceConnect(ConnectObject, SMB_REF_DISCONNECT);
|
|
SMB_RELEASE_SPINLOCK(&SmbCfg, Irql);
|
|
|
|
SmbTrace (SMB_TRACE_CONNECT, ("TCP Disconnect Indication: ConnOb %p Flag %d",
|
|
ConnectObject, DisconnectFlags));
|
|
|
|
status = CommonDisconnectHandler(DeviceObject, ConnectObject, DisconnectFlags);
|
|
SmbDereferenceConnect(ConnectObject, SMB_REF_DISCONNECT);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CommonDisconnectHandler (
|
|
IN PSMB_DEVICE DeviceObject,
|
|
IN PSMB_CONNECT ConnectObject,
|
|
IN ULONG DisconnectFlags
|
|
)
|
|
{
|
|
PSMB_CLIENT_ELEMENT ClientObject;
|
|
KIRQL Irql;
|
|
PTDI_IND_DISCONNECT evDisConnect;
|
|
PVOID DiscEvContext;
|
|
PVOID ClientContext;
|
|
PSMB_TCP_CONTEXT TcpContext;
|
|
NTSTATUS status;
|
|
|
|
SMB_ACQUIRE_SPINLOCK(ConnectObject, Irql);
|
|
|
|
ClientObject = ConnectObject->ClientObject;
|
|
if (NULL == ClientObject) {
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
SmbTrace(SMB_TRACE_CONNECT, ("receive disconnection indication for %p", ConnectObject));
|
|
|
|
TcpContext = ConnectObject->TcpContext;
|
|
ConnectObject->TcpContext = NULL;
|
|
|
|
if (NULL == TcpContext) {
|
|
ASSERT(ConnectObject->State == SMB_IDLE);
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
ASSERT(ConnectObject->State == SMB_CONNECTED || ConnectObject->State == SMB_CONNECTING);
|
|
ConnectObject->State = SMB_IDLE;
|
|
|
|
SaveDisconnectOriginator(ConnectObject, SMB_DISCONNECT_FROM_TRANSPORT);
|
|
|
|
evDisConnect = ClientObject->evDisconnect;
|
|
DiscEvContext = ClientObject->DiscEvContext;
|
|
ClientContext = ConnectObject->ClientContext;
|
|
SMB_RELEASE_SPINLOCK(ConnectObject, Irql);
|
|
|
|
//
|
|
// Cleanup the endpoint so that the client can reuse it
|
|
//
|
|
// Don't wait for the tcp-layer disconnect completion since
|
|
// the disconnection isn't generated by our clients.
|
|
//
|
|
SmbDisconnectCleanup(DeviceObject, ClientObject, ConnectObject,
|
|
TcpContext, TDI_DISCONNECT_ABORT, FALSE);
|
|
|
|
//
|
|
// Notify our client
|
|
//
|
|
if (evDisConnect) {
|
|
status = (*evDisConnect)(
|
|
DiscEvContext,
|
|
ClientContext,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
DisconnectFlags
|
|
);
|
|
SmbTrace(SMB_TRACE_CONNECT, ("client returns %!status! for disconnect event", status));
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|