/*++ Copyright (c) 1989-2001 Microsoft Corporation Module Name: session.c Abstract: Implement TDI_ASSOCIATE_ADDRESS, TDI_DISASSOCIATE_ADDRESS, Create connection/ Close Connection Author: Jiandong Ruan Revision History: --*/ #include "precomp.h" #include "session.tmh" NTSTATUS DisAssociateAddress( PSMB_CONNECT ConnectObject ); PSMB_CONNECT SmbVerifyAndReferenceConnect( PFILE_OBJECT FileObject, SMB_REF_CONTEXT ctx ) { PSMB_CONNECT ConnectObject; KIRQL Irql; // // Rdr could issue request at DISPATCH level, we'd better use spinlock instead of resource lock. // SMB_ACQUIRE_SPINLOCK(&SmbCfg, Irql); if (FileObject->FsContext2 != UlongToPtr(SMB_TDI_CONNECT)) { ConnectObject = NULL; } else { ConnectObject = (PSMB_CONNECT)FileObject->FsContext; SmbReferenceConnect(ConnectObject, ctx); } SMB_RELEASE_SPINLOCK(&SmbCfg, Irql); return ConnectObject; } VOID SmbDeleteConnect(PSMB_CONNECT ob) /*++ Routine Description: Kill a ConnectObject This routine will be called when the last reference is removed from the ob. Arguments: Return Value: --*/ { KIRQL Irql; SMB_ACQUIRE_SPINLOCK(ob->Device, Irql); ASSERT(EntryIsInList(&ob->Device->PendingDeleteConnectionList, &ob->Linkage)); RemoveEntryList(&ob->Linkage); SMB_RELEASE_SPINLOCK(ob->Device, Irql); if (ob->PartialMdl) { IoFreeMdl(ob->PartialMdl); ob->PartialMdl = NULL; } SmbPrint(SMB_TRACE_CALL, ("SmbDeleteConnect: free connect %p\n", ob)); _delete_ConnectObject(ob); } void SmbReuseConnectObject(PSMB_CONNECT ConnectObject) { int i; for (i = 0; i < SMB_PENDING_MAX; i++) { ASSERT (ConnectObject->PendingIRPs[i] == NULL); ConnectObject->PendingIRPs[i] = NULL; } ConnectObject->BytesReceived = 0; ConnectObject->BytesSent = 0; ConnectObject->BytesInXport = 0; ConnectObject->CurrentPktLength = 0; ConnectObject->BytesRemaining = 0; ConnectObject->ClientIrp = NULL; ConnectObject->ClientMdl = NULL; ConnectObject->DpcRequestQueued = FALSE; ConnectObject->BytesInIndicate = 0; ConnectObject->HeaderBytesRcved = 0; ConnectObject->ClientBufferSize = 0; ConnectObject->FreeBytesInMdl = 0; ConnectObject->StateRcvHandler = WaitingHeader; ConnectObject->HeaderBytesRcved = 0; ResetDisconnectOriginator(ConnectObject); #ifdef ENABLE_RCV_TRACE SmbInitTraceRcv(&ConnectObject->TraceRcv); #endif } NTSTATUS SmbCreateConnection( PSMB_DEVICE Device, PIRP Irp, PFILE_FULL_EA_INFORMATION ea ) /*++ Routine Description: Create a ConnectObject Arguments: Return Value: --*/ { CONNECTION_CONTEXT ClientContext; PSMB_CONNECT ConnectObject; PIO_STACK_LOCATION IrpSp; KIRQL Irql; PAGED_CODE(); SmbPrint(SMB_TRACE_CALL, ("Enter SmbCreateConnection\n")); if (ea->EaValueLength < sizeof(ClientContext)) { ASSERT (0); return STATUS_INVALID_ADDRESS_COMPONENT; } RtlCopyMemory(&ClientContext, ((PUCHAR)ea->EaName) + ea->EaNameLength + 1, sizeof(ClientContext)); ConnectObject = _new_ConnectObject(); if (NULL == ConnectObject) { return STATUS_NO_MEMORY; } RtlZeroMemory(ConnectObject, sizeof(ConnectObject[0])); SmbInitializeObject((PSMB_OBJECT)ConnectObject, TAG_CONNECT_OBJECT, (PSMB_OBJECT_CLEANUP)SmbDeleteConnect); KeInitializeDpc(&ConnectObject->SmbHeaderDpc, (PKDEFERRED_ROUTINE)SmbGetHeaderDpc, ConnectObject); // // Reserved resource for getting smb header so that we don't need to worry about // the annoying insufficient error later. // ASSERT(ConnectObject->PartialMdl == NULL); ConnectObject->PartialMdl = IoAllocateMdl( &ConnectObject->IndicateBuffer, // fake address. SMB_MAX_SESSION_PACKET, FALSE, FALSE, NULL ); if (ConnectObject->PartialMdl == NULL) { _delete_ConnectObject(ConnectObject); return STATUS_INSUFFICIENT_RESOURCES; } ConnectObject->Device = Device; KeInitializeSpinLock(&ConnectObject->Lock); ConnectObject->ClientContext = ClientContext; ConnectObject->ClientObject = NULL; ConnectObject->TcpContext = NULL; InitializeListHead(&ConnectObject->RcvList); ConnectObject->State = SMB_IDLE; ConnectObject->BytesReceived = 0; ConnectObject->BytesSent = 0; ConnectObject->BytesInXport = 0; ConnectObject->CurrentPktLength = 0; ConnectObject->BytesRemaining = 0; SmbInitTraceRcv(&ConnectObject->TraceRcv); SMB_ACQUIRE_SPINLOCK(Device, Irql); InsertTailList(&Device->UnassociatedConnectionList, &ConnectObject->Linkage); SMB_RELEASE_SPINLOCK(Device, Irql); IrpSp = IoGetCurrentIrpStackLocation(Irp); IrpSp->FileObject->FsContext = ConnectObject; IrpSp->FileObject->FsContext2 = UlongToPtr(SMB_TDI_CONNECT); SmbPrint(SMB_TRACE_CALL, ("Leave SmbCreateConnection: new Connect %p\n", ConnectObject)); return STATUS_SUCCESS; } NTSTATUS SmbAssociateAddress( PSMB_DEVICE Device, PIRP Irp ) /*++ Routine Description: TDI_ASSOCIATE_ADDRESS Arguments: Return Value: --*/ { PIO_STACK_LOCATION IrpSp; PSMB_CONNECT ConnectObject = NULL; PSMB_CLIENT_ELEMENT ClientObject = NULL; HANDLE AddressHandle; PFILE_OBJECT AddressObject = NULL; NTSTATUS status; KIRQL Irql; extern POBJECT_TYPE *IoFileObjectType; PAGED_CODE(); IrpSp = IoGetCurrentIrpStackLocation(Irp); AddressHandle = ((PTDI_REQUEST_KERNEL_ASSOCIATE)(&IrpSp->Parameters))->AddressHandle; if (NULL == AddressHandle) { ASSERT(0); return STATUS_INVALID_HANDLE; } ConnectObject = SmbVerifyAndReferenceConnect(IrpSp->FileObject, SMB_REF_ASSOCIATE); if (NULL == ConnectObject) { ASSERT(0); return STATUS_INVALID_HANDLE; } SmbPrint(SMB_TRACE_CALL, ("SmbAssociateAddress: connect %p\n", ConnectObject)); status = ObReferenceObjectByHandle( AddressHandle, FILE_READ_DATA, *IoFileObjectType, Irp->RequestorMode, &AddressObject, NULL ); BAIL_OUT_ON_ERROR(status); ClientObject = SmbVerifyAndReferenceClient(AddressObject, SMB_REF_ASSOCIATE); if (NULL == ClientObject) { ASSERT(0); SmbDereferenceConnect(ConnectObject, SMB_REF_ASSOCIATE); return STATUS_INVALID_HANDLE; } ASSERT(ConnectObject->TcpContext == NULL); // // We need to hold acqure 3 locks here because we need to // 1. remove ConnectObject from Device->UnassociatedConnectionList // 2. insert ConnectObject into ClientObject->AssociatedConnectionList // 3. update ConnectObject->ClientObject // SMB_ACQUIRE_SPINLOCK(Device, Irql); SMB_ACQUIRE_SPINLOCK_DPC(ClientObject); SMB_ACQUIRE_SPINLOCK_DPC(ConnectObject); if (IsAssociated(ConnectObject)) { status = STATUS_INVALID_HANDLE; goto cleanup1; } ASSERT(EntryIsInList(&Device->UnassociatedConnectionList, &ConnectObject->Linkage)); ASSERT(EntryIsInList(&Device->ClientList, &ClientObject->Linkage)); ConnectObject->ClientObject = ClientObject; RemoveEntryList(&ConnectObject->Linkage); InsertTailList(&ClientObject->AssociatedConnection, &ConnectObject->Linkage); status = STATUS_SUCCESS; SMB_RELEASE_SPINLOCK_DPC(ConnectObject); SMB_RELEASE_SPINLOCK_DPC(ClientObject); SMB_RELEASE_SPINLOCK(Device, Irql); SmbDereferenceConnect(ConnectObject, SMB_REF_ASSOCIATE); // // We're done, release the reference // ObDereferenceObject(AddressObject); return status; cleanup1: SMB_RELEASE_SPINLOCK_DPC(ConnectObject); SMB_RELEASE_SPINLOCK_DPC(ClientObject); SMB_RELEASE_SPINLOCK(Device, Irql); cleanup: if (AddressObject) { ObDereferenceObject(AddressObject); AddressObject = NULL; } if (ConnectObject) SmbDereferenceConnect(ConnectObject, SMB_REF_ASSOCIATE); if (ClientObject) SmbDereferenceClient(ClientObject, SMB_REF_ASSOCIATE); return status; } NTSTATUS DisAssociateAddress( PSMB_CONNECT ConnectObject ) /*++ Routine Description: This routine do the disassociation stuff. It can be called from 1. TDI_DISASSOCIATE_ADDRESS 2. SmbCloseConnection Arguments: Return Value: --*/ { KIRQL Irql; PSMB_CLIENT_ELEMENT ClientObject; PSMB_DEVICE Device; PAGED_CODE(); SmbDoDisconnect(ConnectObject); if (SMB_IDLE != ConnectObject->State) { ASSERT(0); return STATUS_INVALID_DEVICE_REQUEST; } ClientObject = InterlockedExchangePointer(&ConnectObject->ClientObject, NULL); if (NULL == ClientObject) { return STATUS_SUCCESS; } // // Remove the ConnectObject from the list in ClientObject // SMB_ACQUIRE_SPINLOCK(&SmbCfg, Irql); SMB_ACQUIRE_SPINLOCK_DPC(ClientObject); SMB_ACQUIRE_SPINLOCK_DPC(ConnectObject); ASSERT (EntryIsInList(&ClientObject->AssociatedConnection, &ConnectObject->Linkage)); ASSERT (ConnectObject->TcpContext == NULL); ConnectObject->ClientObject = NULL; // // Disassociate the ConnectObject with the ClientObject // RemoveEntryList(&ConnectObject->Linkage); InitializeListHead(&ConnectObject->Linkage); Device = ConnectObject->Device; SMB_RELEASE_SPINLOCK_DPC(ConnectObject); SMB_RELEASE_SPINLOCK_DPC(ClientObject); SMB_RELEASE_SPINLOCK(&SmbCfg, Irql); // // Put the ConnectObject back into the Device->UnassociatedConnectionList // SMB_ACQUIRE_SPINLOCK(Device, Irql); InsertTailList(&Device->UnassociatedConnectionList, &ConnectObject->Linkage); SMB_RELEASE_SPINLOCK(Device, Irql); SmbDereferenceClient(ClientObject, SMB_REF_ASSOCIATE); return STATUS_SUCCESS; } NTSTATUS SmbDisAssociateAddress( PSMB_DEVICE Device, PIRP Irp ) /*++ Routine Description: TDI_DISASSOCIATE_ADDRESS Arguments: Return Value: STATUS_INVALID_HANDLE If the connection FileObject is corrupted. STATUS_INVALID_DEVICE_REQUEST the connection object is not in assocated state or it is not in disconnected state (SMB_IDLE). STATUS_SUCCESS success --*/ { PIO_STACK_LOCATION IrpSp; PSMB_CONNECT ConnectObject; NTSTATUS status; PAGED_CODE(); IrpSp = IoGetCurrentIrpStackLocation(Irp); ConnectObject = SmbVerifyAndReferenceConnect(IrpSp->FileObject, SMB_REF_ASSOCIATE); if (NULL == ConnectObject) { ASSERT(0); return STATUS_INVALID_HANDLE; } SmbPrint(SMB_TRACE_CALL, ("SmbDisAssociateAddress: connect %p\n", ConnectObject)); if (IsDisAssociated(ConnectObject)) { ASSERT(0); return STATUS_INVALID_DEVICE_REQUEST; } status = DisAssociateAddress(ConnectObject); ASSERT(status != STATUS_PENDING); SmbDereferenceConnect(ConnectObject, SMB_REF_ASSOCIATE); return status; } NTSTATUS SmbListen( PSMB_DEVICE Device, PIRP Irp ) /*++ Routine Description: TDI_LISTEN SRV is not using this. Postpone the implementation. Arguments: Return Value: --*/ { SmbPrint(SMB_TRACE_CALL, ("SmbListen\n")); ASSERT(0); return STATUS_SUCCESS; } NTSTATUS SmbAccept( PSMB_DEVICE Device, PIRP Irp ) /*++ Routine Description: TDI_ACCEPT SRV is not using this. Postpone the implementation. Arguments: Return Value: --*/ { SmbPrint(SMB_TRACE_CALL, ("SmbAccept\n")); ASSERT(0); return STATUS_SUCCESS; } NTSTATUS SmbCloseConnection( PSMB_DEVICE Device, PIRP Irp ) /*++ Routine Description: This routine close a connection. A connection should be in disconnected state before it can be closed. Note: it is unnecessary for a TDI client to disassociate the connection endpoint from its associated transport address before making a close-connection-endpoint request. If necessary, we should simulate the effects of a disassociation. Arguments: Return Value: STATUS_INVALID_HANDLE If the connection FileObject is corrupted. STATUS_INVALID_DEVICE_REQUEST the connection object is not in disconnected state. STATUS_SUCCESS Success --*/ { PIO_STACK_LOCATION IrpSp; KIRQL Irql; PSMB_CONNECT ConnectObject; IrpSp = IoGetCurrentIrpStackLocation(Irp); if (IrpSp->FileObject->FsContext2 != UlongToPtr(SMB_TDI_CONNECT)) { ASSERT (0); return STATUS_INTERNAL_ERROR; } // // Invalidate FsContext2 so that the object cannot be used anymore. // SMB_ACQUIRE_SPINLOCK(&SmbCfg, Irql); IrpSp->FileObject->FsContext2 = UlongToPtr(SMB_TDI_INVALID); SMB_RELEASE_SPINLOCK(&SmbCfg, Irql); if (NULL == IrpSp->FileObject->FsContext) { ASSERT(0); return STATUS_INVALID_HANDLE; } ConnectObject = (PSMB_CONNECT)IrpSp->FileObject->FsContext; SmbPrint(SMB_TRACE_CALL, ("SmbCloseConnection: Connect %p\n", ConnectObject)); DisAssociateAddress(ConnectObject); SMB_ACQUIRE_SPINLOCK(Device, Irql); ASSERT(EntryIsInList(&Device->UnassociatedConnectionList, &ConnectObject->Linkage)); RemoveEntryList(&ConnectObject->Linkage); InsertTailList(&Device->PendingDeleteConnectionList, &ConnectObject->Linkage); SMB_RELEASE_SPINLOCK(Device, Irql); SmbDereferenceConnect(ConnectObject, SMB_REF_CREATE); IrpSp->FileObject->FsContext = NULL; return STATUS_SUCCESS; }