/*++ Copyright (c) 1991 Microsoft Corporation Module Name: ntdisp.c Abstract: NT specific routines for dispatching and handling IRPs. Author: Mike Massa (mikemas) Aug 13, 1993 Revision History: Who When What -------- -------- ---------------------------------------------- mikemas 08-13-93 created Notes: --*/ #include #include #include #include #include #include #include #include #include #include #include "queue.h" #include "addr.h" #include "tcp.h" #include "udp.h" #include "raw.h" #include #include #include "tcpcfg.h" #include "secfltr.h" #include "tcpconn.h" // // Macros // //++ // // LARGE_INTEGER // CTEConvert100nsToMilliseconds( // IN LARGE_INTEGER HnsTime // ); // // Routine Description: // // Converts time expressed in hundreds of nanoseconds to milliseconds. // // Arguments: // // HnsTime - Time in hundreds of nanoseconds. // // Return Value: // // Time in milliseconds. // //-- #define SHIFT10000 13 static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758}; #define CTEConvert100nsToMilliseconds(HnsTime) \ RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000) // // Global variables // extern PDEVICE_OBJECT TCPDeviceObject, UDPDeviceObject; extern PDEVICE_OBJECT IPDeviceObject; extern PDEVICE_OBJECT RawIPDeviceObject; // // Local types // typedef struct { PIRP Irp; PMDL InputMdl; PMDL OutputMdl; TCP_REQUEST_QUERY_INFORMATION_EX QueryInformation; } TCP_QUERY_CONTEXT, *PTCP_QUERY_CONTEXT; // // General external function prototypes // extern NTSTATUS IPDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); // // External TDI function prototypes // extern TDI_STATUS TdiOpenAddress( PTDI_REQUEST Request, TRANSPORT_ADDRESS UNALIGNED *AddrList, uint protocol, void *Reuse ); extern TDI_STATUS TdiCloseAddress( PTDI_REQUEST Request ); extern TDI_STATUS TdiOpenConnection( PTDI_REQUEST Request, PVOID Context ); extern TDI_STATUS TdiCloseConnection( PTDI_REQUEST Request ); extern TDI_STATUS TdiAssociateAddress( PTDI_REQUEST Request, HANDLE AddrHandle ); extern TDI_STATUS TdiCancelDisAssociateAddress( PTDI_REQUEST Request ); extern TDI_STATUS TdiDisAssociateAddress( PTDI_REQUEST Request ); extern TDI_STATUS TdiConnect( PTDI_REQUEST Request, void *Timeout, PTDI_CONNECTION_INFORMATION RequestAddr, PTDI_CONNECTION_INFORMATION ReturnAddr ); extern TDI_STATUS TdiListen( PTDI_REQUEST Request, ushort Flags, PTDI_CONNECTION_INFORMATION AcceptableAddr, PTDI_CONNECTION_INFORMATION ConnectedAddr ); extern TDI_STATUS TdiAccept( PTDI_REQUEST Request, PTDI_CONNECTION_INFORMATION AcceptInfo, PTDI_CONNECTION_INFORMATION ConnectedInfo ); extern TDI_STATUS TdiDisconnect( PTDI_REQUEST Request, void *TO, ushort Flags, PTDI_CONNECTION_INFORMATION DiscConnInfo, PTDI_CONNECTION_INFORMATION ReturnInfo ); extern TDI_STATUS TdiSend( PTDI_REQUEST Request, ushort Flags, uint SendLength, PNDIS_BUFFER SendBuffer ); extern TDI_STATUS TdiReceive( PTDI_REQUEST Request, ushort *Flags, uint *RcvLength, PNDIS_BUFFER Buffer ); extern TDI_STATUS TdiSendDatagram( PTDI_REQUEST Request, PTDI_CONNECTION_INFORMATION ConnInfo, uint DataSize, uint *BytesSent, PNDIS_BUFFER Buffer ); VOID TdiCancelSendDatagram( AddrObj *SrcAO, PVOID Context ); extern TDI_STATUS TdiReceiveDatagram( PTDI_REQUEST Request, PTDI_CONNECTION_INFORMATION ConnInfo, PTDI_CONNECTION_INFORMATION ReturnInfo, uint RcvSize, uint *BytesRcvd, PNDIS_BUFFER Buffer ); VOID TdiCancelReceiveDatagram( AddrObj *SrcAO, PVOID Context ); extern TDI_STATUS TdiSetEvent( PVOID Handle, int Type, PVOID Handler, PVOID Context ); extern TDI_STATUS TdiQueryInformation( PTDI_REQUEST Request, uint QueryType, PNDIS_BUFFER Buffer, uint *BytesReturned, uint IsConn ); extern TDI_STATUS TdiSetInformation( PTDI_REQUEST Request, uint SetType, PNDIS_BUFFER Buffer, uint BufferSize, uint IsConn ); extern TDI_STATUS TdiQueryInformationEx( PTDI_REQUEST Request, struct TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, void *Context ); extern TDI_STATUS TdiSetInformationEx( PTDI_REQUEST Request, struct TDIObjectID *ID, void *Buffer, uint Size ); extern TDI_STATUS TdiAction( PTDI_REQUEST Request, uint ActionType, PNDIS_BUFFER Buffer, uint BufferSize ); // // Other external functions // void TCPAbortAndIndicateDisconnect( uint ConnnectionContext ); // // Local pageable function prototypes // NTSTATUS TCPDispatchDeviceControl( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS TCPCreate( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS TCPAssociateAddress( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS TCPSetEventHandler( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS TCPQueryInformation( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); FILE_FULL_EA_INFORMATION UNALIGNED * FindEA( PFILE_FULL_EA_INFORMATION StartEA, CHAR *TargetName, USHORT TargetNameLength ); BOOLEAN IsDHCPZeroAddress( TRANSPORT_ADDRESS UNALIGNED *AddrList ); ULONG RawExtractProtocolNumber( IN PUNICODE_STRING FileName ); #ifdef SECFLTR NTSTATUS TCPControlSecurityFilter( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS TCPProcessSecurityFilterRequest( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS TCPEnumerateSecurityFilter( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); #endif // SECFLTR NTSTATUS TCPEnumerateConnectionList( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); // // Local helper routine prototypes. // ULONG TCPGetMdlChainByteCount( PMDL Mdl ); // // All of this code is pageable. // #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, TCPDispatchDeviceControl) #pragma alloc_text(PAGE, TCPCreate) #pragma alloc_text(PAGE, TCPAssociateAddress) #pragma alloc_text(PAGE, TCPSetEventHandler) #pragma alloc_text(PAGE, FindEA) #pragma alloc_text(PAGE, IsDHCPZeroAddress) #pragma alloc_text(PAGE, RawExtractProtocolNumber) #ifdef SECFLTR #pragma alloc_text(PAGE, TCPControlSecurityFilter) #pragma alloc_text(PAGE, TCPProcessSecurityFilterRequest) #pragma alloc_text(PAGE, TCPEnumerateSecurityFilter) #endif // SECFLTR #pragma alloc_text(PAGE, TCPEnumerateSecurityFilter) #endif // ALLOC_PRAGMA // // Generic Irp completion and cancellation routines. // void TCPDataRequestComplete( void *Context, unsigned int Status, unsigned int ByteCount ) /*++ Routine Description: Completes a UDP/TCP send/receive request. Arguments: Context - A pointer to the IRP for this request. Status - The final TDI status of the request. ByteCount - Bytes sent/received information. Return Value: None. Notes: --*/ { KIRQL oldIrql; PIRP irp; PIO_STACK_LOCATION irpSp; PTCP_CONTEXT tcpContext; PIRP item = NULL; irp = (PIRP) Context; irpSp = IoGetCurrentIrpStackLocation(irp); tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext; IoAcquireCancelSpinLock(&oldIrql); #if DBG IF_TCPDBG(TCP_DEBUG_CANCEL) { PLIST_ENTRY entry, listHead; PIRP item = NULL; if (irp->Cancel) { CTEAssert(irp->CancelRoutine == NULL); listHead = &(tcpContext->CancelledIrpList); } else { listHead = &(tcpContext->PendingIrpList); } // // Verify that the Irp is on the appropriate list // for ( entry = listHead->Flink; entry != listHead; entry = entry->Flink ) { item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); if (item == irp) { RemoveEntryList(&(irp->Tail.Overlay.ListEntry)); break; } } CTEAssert(item == irp); } #endif IoSetCancelRoutine(irp, NULL); CTEAssert(tcpContext->ReferenceCount > 0); if (--(tcpContext->ReferenceCount) == 0) { IF_TCPDBG(TCP_DEBUG_CANCEL) { CTEAssert(IsListEmpty(&(tcpContext->CancelledIrpList))); CTEAssert(IsListEmpty(&(tcpContext->PendingIrpList))); } // // Set the cleanup event. // KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE); } IF_TCPDBG(TCP_DEBUG_IRP) { TCPTRACE(( "TCPDataRequestComplete: Irp %lx fileobj %lx refcnt dec to %u\n", irp, irpSp->FileObject, tcpContext->ReferenceCount )); } if (irp->Cancel || tcpContext->CancelIrps) { IF_TCPDBG(TCP_DEBUG_IRP) { TCPTRACE(("TCPDataRequestComplete: Irp %lx was cancelled\n", irp)); } Status = (unsigned int) STATUS_CANCELLED; ByteCount = 0; } IoReleaseCancelSpinLock(oldIrql); IF_TCPDBG(TCP_DEBUG_IRP) { TCPTRACE(( "TCPDataRequestComplete: completing irp %lx, status %lx, byte count %lx\n", irp, Status, ByteCount )); } irp->IoStatus.Status = (NTSTATUS) Status; irp->IoStatus.Information = ByteCount; IoCompleteRequest(irp, IO_NETWORK_INCREMENT); return; } // TCPDataRequestComplete void TCPRequestComplete( void *Context, unsigned int Status, unsigned int UnUsed ) /*++ Routine Description: Completes a cancellable TDI request which returns no data by calling TCPDataRequestComplete with a ByteCount of zero. Arguments: Context - A pointer to the IRP for this request. Status - The final TDI status of the request. UnUsed - An unused parameter Return Value: None. Notes: --*/ { UNREFERENCED_PARAMETER(UnUsed); TCPDataRequestComplete(Context, Status, 0); } // TCPRequestComplete void TCPNonCancellableRequestComplete( void *Context, unsigned int Status, unsigned int UnUsed ) /*++ Routine Description: Completes a TDI request which cannot be cancelled. Arguments: Context - A pointer to the IRP for this request. Status - The final TDI status of the request. UnUsed - An unused parameter Return Value: None. Notes: --*/ { PIRP irp; PIO_STACK_LOCATION irpSp; UNREFERENCED_PARAMETER(UnUsed); irp = (PIRP) Context; irpSp = IoGetCurrentIrpStackLocation(irp); IF_TCPDBG(TCP_DEBUG_CLOSE) { TCPTRACE(( "TCPNonCancellableRequestComplete: irp %lx status %lx\n", irp, Status )); } // // Complete the IRP // irp->IoStatus.Status = (NTSTATUS) Status; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NETWORK_INCREMENT); return; } // TCPNonCancellableRequestComplete void TCPCancelComplete( void *Context, unsigned int Unused1, unsigned int Unused2 ) { PFILE_OBJECT fileObject = (PFILE_OBJECT) Context; PTCP_CONTEXT tcpContext = (PTCP_CONTEXT) fileObject->FsContext; KIRQL oldIrql; UNREFERENCED_PARAMETER(Unused1); UNREFERENCED_PARAMETER(Unused2); IoAcquireCancelSpinLock(&oldIrql); // // Remove the reference placed on the endpoint by the cancel routine. // The cancelled Irp will be completed by the completion routine for the // request. // if (--(tcpContext->ReferenceCount) == 0) { IF_TCPDBG(TCP_DEBUG_CANCEL) { CTEAssert(IsListEmpty(&(tcpContext->CancelledIrpList))); CTEAssert(IsListEmpty(&(tcpContext->PendingIrpList))); } // // Set the cleanup event. // KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE); } IF_TCPDBG(TCP_DEBUG_IRP) { TCPTRACE(( "TCPCancelComplete: fileobj %lx refcnt dec to %u\n", fileObject, tcpContext->ReferenceCount )); } IoReleaseCancelSpinLock(oldIrql); return; } // TCPCancelComplete VOID TCPCancelRequest( PDEVICE_OBJECT Device, PIRP Irp ) /*++ Routine Description: Cancels an outstanding Irp. Arguments: Device - Pointer to the device object for this request. Irp - Pointer to I/O request packet Return Value: None. --*/ { PIO_STACK_LOCATION irpSp; PTCP_CONTEXT tcpContext; NTSTATUS status = STATUS_SUCCESS; PFILE_OBJECT fileObject; UCHAR minorFunction; TDI_REQUEST request; irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; tcpContext = (PTCP_CONTEXT) fileObject->FsContext; minorFunction = irpSp->MinorFunction; CTEAssert(Irp->Cancel); IoSetCancelRoutine(Irp, NULL); IF_TCPDBG(TCP_DEBUG_IRP) { TCPTRACE(( "TCPCancelRequest: cancelling irp %lx, file object %lx\n", Irp, fileObject )); } #if DBG IF_TCPDBG(TCP_DEBUG_CANCEL) { // // Verify that the Irp is on the pending list // PLIST_ENTRY entry; PIRP item = NULL; for ( entry = tcpContext->PendingIrpList.Flink; entry != &(tcpContext->PendingIrpList); entry = entry->Flink ) { item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); if (item == Irp) { RemoveEntryList( &(Irp->Tail.Overlay.ListEntry)); break; } } CTEAssert(item == Irp); InsertTailList( &(tcpContext->CancelledIrpList), &(Irp->Tail.Overlay.ListEntry) ); } #endif // DBG // // Add a reference so the object can't be closed while the cancel routine // is executing. // CTEAssert(tcpContext->ReferenceCount > 0); tcpContext->ReferenceCount++; IF_TCPDBG(TCP_DEBUG_IRP) { TCPTRACE(( "TCPCancelRequest: Irp %lx fileobj %lx refcnt inc to %u\n", Irp, fileObject, tcpContext->ReferenceCount )); } IoReleaseCancelSpinLock(Irp->CancelIrql); // // Try to cancel the request. // switch(minorFunction) { case TDI_SEND: case TDI_RECEIVE: CTEAssert(((int)fileObject->FsContext2) == TDI_CONNECTION_FILE); TCPAbortAndIndicateDisconnect( (uint) tcpContext->Handle.ConnectionContext ); break; case TDI_SEND_DATAGRAM: CTEAssert(((int)fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE); TdiCancelSendDatagram(tcpContext->Handle.AddressHandle, Irp); break; case TDI_RECEIVE_DATAGRAM: CTEAssert(((int)fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE); TdiCancelReceiveDatagram(tcpContext->Handle.AddressHandle, Irp); break; case TDI_DISASSOCIATE_ADDRESS: CTEAssert(((int)fileObject->FsContext2) == TDI_CONNECTION_FILE); // // This pends but is not cancellable. We put it thru the cancel code // anyway so a reference is made for it and so it can be tracked in // a debug build. // break; default: // // Initiate a disconnect to cancel the request. // request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; request.RequestNotifyObject = TCPCancelComplete; request.RequestContext = fileObject; status = TdiDisconnect( &request, NULL, TDI_DISCONNECT_ABORT, NULL, NULL ); break; } if (status != TDI_PENDING) { TCPCancelComplete(fileObject, 0, 0); } return; } // TCPCancelRequest NTSTATUS TCPPrepareIrpForCancel( PTCP_CONTEXT TcpContext, PIRP Irp, PDRIVER_CANCEL CancelRoutine ) { KIRQL oldIrql; // // Set up for cancellation // IoAcquireCancelSpinLock(&oldIrql); CTEAssert(Irp->CancelRoutine == NULL); if (!Irp->Cancel) { IoMarkIrpPending(Irp); IoSetCancelRoutine(Irp, CancelRoutine); TcpContext->ReferenceCount++; IF_TCPDBG(TCP_DEBUG_IRP) { TCPTRACE(( "TCPPrepareIrpForCancel: irp %lx fileobj %lx refcnt inc to %u\n", Irp, (IoGetCurrentIrpStackLocation(Irp))->FileObject, TcpContext->ReferenceCount )); } #if DBG IF_TCPDBG(TCP_DEBUG_CANCEL) { PLIST_ENTRY entry; PIRP item = NULL; // // Verify that the Irp has not already been submitted. // for ( entry = TcpContext->PendingIrpList.Flink; entry != &(TcpContext->PendingIrpList); entry = entry->Flink ) { item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); CTEAssert(item != Irp); } for ( entry = TcpContext->CancelledIrpList.Flink; entry != &(TcpContext->CancelledIrpList); entry = entry->Flink ) { item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); CTEAssert(item != Irp); } InsertTailList( &(TcpContext->PendingIrpList), &(Irp->Tail.Overlay.ListEntry) ); } #endif // DBG IoReleaseCancelSpinLock(oldIrql); return(STATUS_SUCCESS); } // // The IRP has already been cancelled. Complete it now. // IF_TCPDBG(TCP_DEBUG_IRP) { TCPTRACE(("TCP: irp %lx already cancelled, completing.\n", Irp)); } IoReleaseCancelSpinLock(oldIrql); Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(STATUS_CANCELLED); } // TCPPrepareIrpForCancel // // TDI functions // NTSTATUS TCPAssociateAddress( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI Associate Address IRP into a call to TdiAssociateAddress. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. Notes: This routine does not pend. --*/ { NTSTATUS status; TDI_REQUEST request; PTCP_CONTEXT tcpContext; PTDI_REQUEST_KERNEL_ASSOCIATE associateInformation; PFILE_OBJECT fileObject; PAGED_CODE(); tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; associateInformation = (PTDI_REQUEST_KERNEL_ASSOCIATE) &(IrpSp->Parameters); // // Get the file object for the address. Then extract the Address Handle // from the TCP_CONTEXT associated with it. // status = ObReferenceObjectByHandle( associateInformation->AddressHandle, 0, NULL, KernelMode, &fileObject, NULL ); if (NT_SUCCESS(status)) { if ( (fileObject->DeviceObject == TCPDeviceObject) && (((int)fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE) ) { tcpContext = (PTCP_CONTEXT) fileObject->FsContext; status = TdiAssociateAddress( &request, tcpContext->Handle.AddressHandle ); CTEAssert(status != STATUS_PENDING); ObDereferenceObject(fileObject); IF_TCPDBG(TCP_DEBUG_ASSOCIATE) { TCPTRACE(( "TCPAssociateAddress complete on file object %lx\n", IrpSp->FileObject )); } } else { ObDereferenceObject(fileObject); status = STATUS_INVALID_HANDLE; IF_TCPDBG(TCP_DEBUG_ASSOCIATE) { TCPTRACE(( "TCPAssociateAddress: ObReference failed on object %lx, status %lx\n", associateInformation->AddressHandle, status )); } } } else { IF_TCPDBG(TCP_DEBUG_ASSOCIATE) { TCPTRACE(( "TCPAssociateAddress: ObReference failed on object %lx, status %lx\n", associateInformation->AddressHandle, status )); } } return(status); } NTSTATUS TCPDisassociateAddress( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI Associate Address IRP into a call to TdiAssociateAddress. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. --*/ { NTSTATUS status; TDI_REQUEST request; PTCP_CONTEXT tcpContext; IF_TCPDBG(TCP_DEBUG_ASSOCIATE) { TCPTRACE(("TCP disassociating address\n")); } CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE ); tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; request.RequestNotifyObject = TCPRequestComplete; request.RequestContext = Irp; status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); if (NT_SUCCESS(status)) { status = TdiDisAssociateAddress(&request); if (status != TDI_PENDING) { TCPRequestComplete(Irp, status, 0); } // // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING // return(TDI_PENDING); } return(status); } // TCPDisassociateAddress NTSTATUS TCPConnect( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI Connect IRP into a call to TdiConnect. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { NTSTATUS status; PTCP_CONTEXT tcpContext; TDI_REQUEST request; PTDI_CONNECTION_INFORMATION requestInformation, returnInformation; PTDI_REQUEST_KERNEL_CONNECT connectRequest; LARGE_INTEGER millisecondTimeout; PLARGE_INTEGER requestTimeout; IF_TCPDBG(TCP_DEBUG_CONNECT) { TCPTRACE(( "TCPConnect irp %lx, file object %lx\n", Irp, IrpSp->FileObject )); } CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE); connectRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(IrpSp->Parameters); requestInformation = connectRequest->RequestConnectionInformation; returnInformation = connectRequest->ReturnConnectionInformation; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; request.RequestNotifyObject = TCPRequestComplete; request.RequestContext = Irp; requestTimeout = (PLARGE_INTEGER) connectRequest->RequestSpecific; if (requestTimeout != NULL) { // // NT relative timeouts are negative. Negate first to get a positive // value to pass to the transport. // millisecondTimeout.QuadPart = -((*requestTimeout).QuadPart); millisecondTimeout = CTEConvert100nsToMilliseconds( millisecondTimeout ); } else { millisecondTimeout.LowPart = 0; millisecondTimeout.HighPart = 0; } CTEAssert(millisecondTimeout.HighPart == 0); status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); if (NT_SUCCESS(status)) { status = TdiConnect( &request, ((millisecondTimeout.LowPart != 0) ? &(millisecondTimeout.LowPart) : NULL), requestInformation, returnInformation ); if (status != STATUS_PENDING) { TCPRequestComplete(Irp, status, 0); } // // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING // return(STATUS_PENDING); } return(status); } // TCPConnect NTSTATUS TCPDisconnect( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI Disconnect IRP into a call to TdiDisconnect. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. Notes: Abortive disconnects may pend, but cannot be cancelled. --*/ { NTSTATUS status; PTCP_CONTEXT tcpContext; TDI_REQUEST request; PTDI_CONNECTION_INFORMATION requestInformation, returnInformation; PTDI_REQUEST_KERNEL_DISCONNECT disconnectRequest; LARGE_INTEGER millisecondTimeout; PLARGE_INTEGER requestTimeout; BOOLEAN abortive = FALSE; CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE); disconnectRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(IrpSp->Parameters); requestInformation = disconnectRequest->RequestConnectionInformation; returnInformation = disconnectRequest->ReturnConnectionInformation; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; request.RequestContext = Irp; // // Set up the timeout value. // if (disconnectRequest->RequestSpecific != NULL) { requestTimeout = (PLARGE_INTEGER) disconnectRequest->RequestSpecific; if ((requestTimeout->LowPart == -1) && (requestTimeout->HighPart == -1)) { millisecondTimeout.LowPart = requestTimeout->LowPart; millisecondTimeout.HighPart = 0; } else { // // NT relative timeouts are negative. Negate first to get a // positive value to pass to the transport. // millisecondTimeout.QuadPart = -((*requestTimeout).QuadPart); millisecondTimeout = CTEConvert100nsToMilliseconds( millisecondTimeout ); } } else { millisecondTimeout.LowPart = 0; millisecondTimeout.HighPart = 0; } CTEAssert(millisecondTimeout.HighPart == 0); if (disconnectRequest->RequestFlags & TDI_DISCONNECT_ABORT) { // // Abortive disconnects cannot be cancelled and must use // a specific completion routine. // abortive = TRUE; IoMarkIrpPending(Irp); request.RequestNotifyObject = TCPNonCancellableRequestComplete; status = STATUS_SUCCESS; } else { // // Non-abortive disconnects can use the generic cancellation and // completion routines. // status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); request.RequestNotifyObject = TCPRequestComplete; } IF_TCPDBG(TCP_DEBUG_CLOSE) { TCPTRACE(( "TCPDisconnect irp %lx, flags %lx, fileobj %lx, abortive = %d\n", Irp, disconnectRequest->RequestFlags, IrpSp->FileObject, abortive )); } if (NT_SUCCESS(status)) { status = TdiDisconnect( &request, ((millisecondTimeout.LowPart != 0) ? &(millisecondTimeout.LowPart) : NULL), (ushort) disconnectRequest->RequestFlags, requestInformation, returnInformation ); if (status != STATUS_PENDING) { if (abortive) { TCPNonCancellableRequestComplete(Irp, status, 0); } else { TCPRequestComplete(Irp, status, 0); } } else { IF_TCPDBG(TCP_DEBUG_CLOSE) { TCPTRACE(("TCPDisconnect pending irp %lx\n", Irp)); } } // // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING // return(STATUS_PENDING); } return(status); } // TCPDisconnect NTSTATUS TCPListen( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI Listen IRP into a call to TdiListen. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. --*/ { NTSTATUS status; PTCP_CONTEXT tcpContext; TDI_REQUEST request; PTDI_CONNECTION_INFORMATION requestInformation, returnInformation; PTDI_REQUEST_KERNEL_LISTEN listenRequest; IF_TCPDBG(TCP_DEBUG_CONNECT) { TCPTRACE(( "TCPListen irp %lx on file object %lx\n", Irp, IrpSp->FileObject )); } CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE); listenRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(IrpSp->Parameters); requestInformation = listenRequest->RequestConnectionInformation; returnInformation = listenRequest->ReturnConnectionInformation; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; request.RequestNotifyObject = TCPRequestComplete; request.RequestContext = Irp; status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); if (NT_SUCCESS(status)) { status = TdiListen( &request, (ushort) listenRequest->RequestFlags, requestInformation, returnInformation ); if (status != TDI_PENDING) { TCPRequestComplete(Irp, status, 0); } // // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING // return(TDI_PENDING); } return(status); } // TCPListen NTSTATUS TCPAccept( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI Accept IRP into a call to TdiAccept. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { NTSTATUS status; PTCP_CONTEXT tcpContext; TDI_REQUEST request; PTDI_CONNECTION_INFORMATION requestInformation, returnInformation; PTDI_REQUEST_KERNEL_ACCEPT acceptRequest; IF_TCPDBG(TCP_DEBUG_CONNECT) { TCPTRACE(( "TCPAccept irp %lx on file object %lx\n", Irp, IrpSp->FileObject )); } CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE); acceptRequest = (PTDI_REQUEST_KERNEL_ACCEPT) &(IrpSp->Parameters); requestInformation = acceptRequest->RequestConnectionInformation; returnInformation = acceptRequest->ReturnConnectionInformation; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; request.RequestNotifyObject = TCPRequestComplete; request.RequestContext = Irp; status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); if (NT_SUCCESS(status)) { status = TdiAccept( &request, requestInformation, returnInformation ); if (status != TDI_PENDING) { TCPRequestComplete(Irp, status, 0); } // // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING // return(TDI_PENDING); } return(status); } // TCPAccept NTSTATUS TCPSendData( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI Send IRP into a call to TdiSend. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. --*/ { TDI_STATUS status; TDI_REQUEST request; PTCP_CONTEXT tcpContext; PTDI_REQUEST_KERNEL_SEND requestInformation; KIRQL oldIrql; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE ); requestInformation = (PTDI_REQUEST_KERNEL_SEND) &(IrpSp->Parameters); request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; request.RequestNotifyObject = TCPDataRequestComplete; request.RequestContext = Irp; IoAcquireCancelSpinLock(&oldIrql); CTEAssert(Irp->CancelRoutine == NULL); if (!Irp->Cancel) { // // Set up for cancellation // IoMarkIrpPending(Irp); IoSetCancelRoutine(Irp, TCPCancelRequest); tcpContext->ReferenceCount++; IF_TCPDBG(TCP_DEBUG_IRP) { TCPTRACE(( "TCPSendData: irp %lx fileobj %lx refcnt inc to %u\n", Irp, IrpSp, tcpContext->ReferenceCount )); } #if DBG IF_TCPDBG(TCP_DEBUG_CANCEL) { PLIST_ENTRY entry; PIRP item = NULL; // // Verify that the Irp has not already been submitted. // for ( entry = tcpContext->PendingIrpList.Flink; entry != &(tcpContext->PendingIrpList); entry = entry->Flink ) { item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); CTEAssert(item != Irp); } for ( entry = tcpContext->CancelledIrpList.Flink; entry != &(tcpContext->CancelledIrpList); entry = entry->Flink ) { item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); CTEAssert(item != Irp); } InsertTailList( &(tcpContext->PendingIrpList), &(Irp->Tail.Overlay.ListEntry) ); } #endif // DBG IoReleaseCancelSpinLock(oldIrql); IF_TCPDBG(TCP_DEBUG_SEND) { TCPTRACE(( "TCPSendData irp %lx sending %d bytes, flags %lx, fileobj %lx\n", Irp, requestInformation->SendLength, requestInformation->SendFlags, IrpSp->FileObject )); } status = TdiSend( &request, (ushort) requestInformation->SendFlags, requestInformation->SendLength, (PNDIS_BUFFER) Irp->MdlAddress ); if (status == TDI_PENDING) { IF_TCPDBG(TCP_DEBUG_SEND) { TCPTRACE(("TCPSendData pending irp %lx\n", Irp)); } return(status); } // // The status is not pending. We reset the pending bit // IrpSp->Control &= ~SL_PENDING_RETURNED; if (status == TDI_SUCCESS) { CTEAssert(requestInformation->SendLength == 0); TCPDataRequestComplete(Irp, status, requestInformation->SendLength); } else { IF_TCPDBG(TCP_DEBUG_SEND) { TCPTRACE(( "TCPSendData - irp %lx send failed, status %lx\n", Irp, status )); } TCPDataRequestComplete(Irp, status, 0); } } else { // // Irp was cancelled previously. // IoReleaseCancelSpinLock(oldIrql); IF_TCPDBG(TCP_DEBUG_SEND) { TCPTRACE(( "TCPSendData: Irp %lx on fileobj %lx was cancelled\n", Irp, IrpSp->FileObject )); } Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); status = STATUS_CANCELLED; } return(status); } // TCPSendData NTSTATUS TCPReceiveData( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI Receive IRP into a call to TdiReceive. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. --*/ { TDI_STATUS status; TDI_REQUEST request; PTCP_CONTEXT tcpContext; PTDI_REQUEST_KERNEL_RECEIVE requestInformation; KIRQL oldIrql; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; CTEAssert( ((int)IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE ); requestInformation = (PTDI_REQUEST_KERNEL_RECEIVE) &(IrpSp->Parameters); request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; request.RequestNotifyObject = TCPDataRequestComplete; request.RequestContext = Irp; IoAcquireCancelSpinLock(&oldIrql); CTEAssert(Irp->CancelRoutine == NULL); if (!Irp->Cancel) { // // Set up for cancellation // IoMarkIrpPending(Irp); IoSetCancelRoutine(Irp, TCPCancelRequest); tcpContext->ReferenceCount++; IF_TCPDBG(TCP_DEBUG_IRP) { TCPTRACE(( "TCPReceiveData: irp %lx fileobj %lx refcnt inc to %u\n", Irp, IrpSp->FileObject, tcpContext->ReferenceCount )); } #if DBG IF_TCPDBG(TCP_DEBUG_CANCEL) { PLIST_ENTRY entry; PIRP item = NULL; // // Verify that the Irp has not already been submitted. // for ( entry = tcpContext->PendingIrpList.Flink; entry != &(tcpContext->PendingIrpList); entry = entry->Flink ) { item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); CTEAssert(item != Irp); } for ( entry = tcpContext->CancelledIrpList.Flink; entry != &(tcpContext->CancelledIrpList); entry = entry->Flink ) { item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); CTEAssert(item != Irp); } InsertTailList( &(tcpContext->PendingIrpList), &(Irp->Tail.Overlay.ListEntry) ); } #endif // DBG IoReleaseCancelSpinLock(oldIrql); IF_TCPDBG(TCP_DEBUG_RECEIVE) { TCPTRACE(( "TCPReceiveData irp %lx receiving %d bytes flags %lx filobj %lx\n", Irp, requestInformation->ReceiveLength, requestInformation->ReceiveFlags, IrpSp->FileObject )); } status = TdiReceive( &request, (ushort *) &(requestInformation->ReceiveFlags), &(requestInformation->ReceiveLength), (PNDIS_BUFFER) Irp->MdlAddress ); if (status == TDI_PENDING) { IF_TCPDBG(TCP_DEBUG_RECEIVE) { TCPTRACE(("TCPReceiveData: pending irp %lx\n", Irp)); } return(status); } // // The status is not pending. We reset the pending bit // IrpSp->Control &= ~SL_PENDING_RETURNED; CTEAssert(status != TDI_SUCCESS); IF_TCPDBG(TCP_DEBUG_RECEIVE) { TCPTRACE(( "TCPReceiveData - irp %lx failed, status %lx\n", Irp, status )); } TCPDataRequestComplete(Irp, status, 0); } else { // // Irp was cancelled previously. // IoReleaseCancelSpinLock(oldIrql); IF_TCPDBG(TCP_DEBUG_SEND) { TCPTRACE(( "TCPReceiveData: Irp %lx on fileobj %lx was cancelled\n", Irp, IrpSp->FileObject )); } Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); status = STATUS_CANCELLED; } return status; } // TCPReceiveData NTSTATUS UDPSendDatagram( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { TDI_STATUS status; TDI_REQUEST request; PTCP_CONTEXT tcpContext; PTDI_REQUEST_KERNEL_SENDDG datagramInformation; ULONG bytesSent = 0; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; datagramInformation = (PTDI_REQUEST_KERNEL_SENDDG) &(IrpSp->Parameters); CTEAssert(((int)IrpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE); request.Handle.AddressHandle = tcpContext->Handle.AddressHandle; request.RequestNotifyObject = TCPDataRequestComplete; request.RequestContext = Irp; IF_TCPDBG(TCP_DEBUG_SEND_DGRAM) { TCPTRACE(( "UDPSendDatagram irp %lx sending %d bytes\n", Irp, datagramInformation->SendLength )); } status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); if (NT_SUCCESS(status)) { status = TdiSendDatagram( &request, datagramInformation->SendDatagramInformation, datagramInformation->SendLength, &bytesSent, (PNDIS_BUFFER) Irp->MdlAddress ); if (status == TDI_PENDING) { return(status); } CTEAssert(status != TDI_SUCCESS); CTEAssert(bytesSent == 0); TCPTRACE(( "UDPSendDatagram - irp %lx send failed, status %lx\n", Irp, status )); TCPDataRequestComplete(Irp, status, bytesSent); // // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING // return(TDI_PENDING); } return status; } // UDPSendDatagram NTSTATUS UDPReceiveDatagram( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI ReceiveDatagram IRP into a call to TdiReceiveDatagram. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. --*/ { TDI_STATUS status; TDI_REQUEST request; PTCP_CONTEXT tcpContext; PTDI_REQUEST_KERNEL_RECEIVEDG datagramInformation; uint bytesReceived = 0; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; datagramInformation = (PTDI_REQUEST_KERNEL_RECEIVEDG) &(IrpSp->Parameters); CTEAssert(((int)IrpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE); request.Handle.AddressHandle = tcpContext->Handle.AddressHandle; request.RequestNotifyObject = TCPDataRequestComplete; request.RequestContext = Irp; IF_TCPDBG(TCP_DEBUG_RECEIVE_DGRAM) { TCPTRACE(( "UDPReceiveDatagram: irp %lx receiveing %d bytes\n", Irp, datagramInformation->ReceiveLength )); } status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest); if (NT_SUCCESS(status)) { status = TdiReceiveDatagram( &request, datagramInformation->ReceiveDatagramInformation, datagramInformation->ReturnDatagramInformation, datagramInformation->ReceiveLength, &bytesReceived, Irp->MdlAddress ); if (status == TDI_PENDING) { return(status); } CTEAssert(status != TDI_SUCCESS); CTEAssert(bytesReceived == 0); TCPTRACE(( "UDPReceiveDatagram: irp %lx send failed, status %lx\n", Irp, status )); TCPDataRequestComplete(Irp, status, bytesReceived); // // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING // return(TDI_PENDING); } return status; } // UDPReceiveDatagram NTSTATUS TCPSetEventHandler( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI SetEventHandler IRP into a call to TdiSetEventHandler. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. Notes: This routine does not pend. --*/ { NTSTATUS status; PTDI_REQUEST_KERNEL_SET_EVENT event; PTCP_CONTEXT tcpContext; PAGED_CODE(); tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; event = (PTDI_REQUEST_KERNEL_SET_EVENT) &(IrpSp->Parameters); IF_TCPDBG(TCP_DEBUG_EVENT_HANDLER) { TCPTRACE(( "TCPSetEventHandler: irp %lx event %lx handler %lx context %lx\n", Irp, event->EventType, event->EventHandler, event->EventContext )); } status = TdiSetEvent( tcpContext->Handle.AddressHandle, event->EventType, event->EventHandler, event->EventContext ); CTEAssert(status != TDI_PENDING); return(status); } // TCPSetEventHandler NTSTATUS TCPQueryInformation( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI QueryInformation IRP into a call to TdiQueryInformation. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. Notes: --*/ { TDI_REQUEST request; TDI_STATUS status = STATUS_SUCCESS; PTCP_CONTEXT tcpContext; PTDI_REQUEST_KERNEL_QUERY_INFORMATION queryInformation; uint isConn = FALSE; uint dataSize = 0; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; queryInformation = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION) &(IrpSp->Parameters); request.RequestNotifyObject = TCPDataRequestComplete; request.RequestContext = Irp; switch(queryInformation->QueryType) { case TDI_QUERY_BROADCAST_ADDRESS: CTEAssert( ((int) IrpSp->FileObject->FsContext2) == TDI_CONTROL_CHANNEL_FILE ); request.Handle.ControlChannel = tcpContext->Handle.ControlChannel; break; case TDI_QUERY_PROVIDER_INFO: // // NetBT does this. Reinstate the CTEAssert when it is fixed. // // CTEAssert( ((int) IrpSp->FileObject->FsContext2) == // TDI_CONTROL_CHANNEL_FILE // ); request.Handle.ControlChannel = tcpContext->Handle.ControlChannel; break; case TDI_QUERY_ADDRESS_INFO: if (((int) IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) { // // This is a TCP connection object. // isConn = TRUE; request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; } else { // // This is an address object // request.Handle.AddressHandle = tcpContext->Handle.AddressHandle; } break; case TDI_QUERY_CONNECTION_INFO: CTEAssert(((int) IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE); isConn = TRUE; request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; break; case TDI_QUERY_PROVIDER_STATISTICS: CTEAssert( ((int) IrpSp->FileObject->FsContext2) == TDI_CONTROL_CHANNEL_FILE ); request.Handle.ControlChannel = tcpContext->Handle.ControlChannel; break; default: status = STATUS_NOT_IMPLEMENTED; break; } if (NT_SUCCESS(status)) { // // This request isn't cancellable, but we put it through // the cancel path because it handles some checks for us // and tracks the irp. // status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL); if (NT_SUCCESS(status)) { dataSize = TCPGetMdlChainByteCount(Irp->MdlAddress); status = TdiQueryInformation( &request, queryInformation->QueryType, Irp->MdlAddress, &dataSize, isConn ); if (status != TDI_PENDING) { TCPDataRequestComplete(Irp, status, dataSize); return(status); } return(STATUS_PENDING); } return(status); } Irp->IoStatus.Status = (NTSTATUS) status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(status); } // TCPQueryInformation void TCPQueryInformationExComplete( void *Context, unsigned int Status, unsigned int ByteCount ) /*++ Routine Description: Completes a TdiQueryInformationEx request. Arguments: Context - A pointer to the IRP for this request. Status - The final TDI status of the request. ByteCount - Bytes returned in output buffer. Return Value: None. Notes: --*/ { PTCP_QUERY_CONTEXT queryContext = (PTCP_QUERY_CONTEXT) Context; ULONG bytesCopied; if (NT_SUCCESS(Status)) { // // Copy the returned context to the input buffer. // TdiCopyBufferToMdl( &(queryContext->QueryInformation.Context), 0, CONTEXT_SIZE, queryContext->InputMdl, FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX, Context), &bytesCopied ); CTEAssert(bytesCopied == CONTEXT_SIZE); } // // Unlock the user's buffers and free the MDLs describing them. // MmUnlockPages(queryContext->InputMdl); IoFreeMdl(queryContext->InputMdl); MmUnlockPages(queryContext->OutputMdl); IoFreeMdl(queryContext->OutputMdl); // // Complete the request // TCPDataRequestComplete(queryContext->Irp, Status, ByteCount); CTEFreeMem(queryContext); return; } NTSTATUS TCPQueryInformationEx( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI QueryInformationEx IRP into a call to TdiQueryInformationEx. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. Notes: --*/ { TDI_REQUEST request; TDI_STATUS status = STATUS_SUCCESS; PTCP_CONTEXT tcpContext; uint size; PTCP_REQUEST_QUERY_INFORMATION_EX InputBuffer; PVOID OutputBuffer; PMDL inputMdl = NULL; PMDL outputMdl = NULL; ULONG InputBufferLength, OutputBufferLength; PTCP_QUERY_CONTEXT queryContext; BOOLEAN inputLocked = FALSE; BOOLEAN outputLocked = FALSE; PAGED_CODE(); IF_TCPDBG(TCP_DEBUG_INFO) { TCPTRACE(( "QueryInformationEx starting - irp %lx fileobj %lx\n", Irp, IrpSp->FileObject )); } tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; switch ((int) IrpSp->FileObject->FsContext2) { case TDI_TRANSPORT_ADDRESS_FILE: request.Handle.AddressHandle = tcpContext->Handle.AddressHandle; break; case TDI_CONNECTION_FILE: request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; break; case TDI_CONTROL_CHANNEL_FILE: request.Handle.ControlChannel = tcpContext->Handle.ControlChannel; break; default: CTEAssert(0); Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(STATUS_INVALID_PARAMETER); } InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; // // Validate the input parameters // if ( (InputBufferLength == sizeof(TCP_REQUEST_QUERY_INFORMATION_EX)) && (OutputBufferLength != 0) ) { OutputBuffer = Irp->UserBuffer; InputBuffer = (PTCP_REQUEST_QUERY_INFORMATION_EX) IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; queryContext = CTEAllocMem(sizeof(TCP_QUERY_CONTEXT)); if (queryContext != NULL) { status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL); if (!NT_SUCCESS(status)) { CTEFreeMem(queryContext); return(status); } // // Allocate Mdls to describe the input and output buffers. // Probe and lock the buffers. // try { inputMdl = IoAllocateMdl( InputBuffer, sizeof(TCP_REQUEST_QUERY_INFORMATION_EX), FALSE, TRUE, NULL ); outputMdl = IoAllocateMdl( OutputBuffer, OutputBufferLength, FALSE, TRUE, NULL ); if ((inputMdl != NULL) && (outputMdl != NULL)) { MmProbeAndLockPages( inputMdl, Irp->RequestorMode, IoModifyAccess ); inputLocked = TRUE; MmProbeAndLockPages( outputMdl, Irp->RequestorMode, IoWriteAccess ); outputLocked = TRUE; // // Copy the input parameter to our pool block so // TdiQueryInformationEx can manipulate it directly. // RtlCopyMemory( &(queryContext->QueryInformation), InputBuffer, sizeof(TCP_REQUEST_QUERY_INFORMATION_EX) ); } else { IF_TCPDBG(TCP_DEBUG_INFO) { TCPTRACE(("QueryInfoEx: Couldn't allocate MDL\n")); } status = STATUS_INSUFFICIENT_RESOURCES; } } except( EXCEPTION_EXECUTE_HANDLER ) { IF_TCPDBG(TCP_DEBUG_INFO) { TCPTRACE(( "QueryInfoEx: exception copying input params %lx\n", GetExceptionCode() )); } status = GetExceptionCode(); } if (NT_SUCCESS(status)) { // // It's finally time to do this thing. // size = TCPGetMdlChainByteCount(outputMdl); queryContext->Irp = Irp; queryContext->InputMdl = inputMdl; queryContext->OutputMdl = outputMdl; request.RequestNotifyObject = TCPQueryInformationExComplete; request.RequestContext = queryContext; status = TdiQueryInformationEx( &request, &(queryContext->QueryInformation.ID), outputMdl, &size, &(queryContext->QueryInformation.Context) ); if (status != TDI_PENDING) { TCPQueryInformationExComplete( queryContext, status, size ); return(status); } IF_TCPDBG(TCP_DEBUG_INFO) { TCPTRACE(( "QueryInformationEx - pending irp %lx fileobj %lx\n", Irp, IrpSp->FileObject )); } return(STATUS_PENDING); } // // If we get here, something failed. Clean up. // if (inputMdl != NULL) { if (inputLocked) { MmUnlockPages(inputMdl); } IoFreeMdl(inputMdl); } if (outputMdl != NULL) { if (outputLocked) { MmUnlockPages(outputMdl); } IoFreeMdl(outputMdl); } CTEFreeMem(queryContext); TCPDataRequestComplete(Irp, status, 0); return(status); } else { status = STATUS_INSUFFICIENT_RESOURCES; IF_TCPDBG(TCP_DEBUG_INFO) { TCPTRACE(("QueryInfoEx: Unable to allocate query context\n")); } } } else { status = STATUS_INVALID_PARAMETER; IF_TCPDBG(TCP_DEBUG_INFO) { TCPTRACE(( "QueryInfoEx: Bad buffer len, OBufLen %d, InBufLen %d\n", OutputBufferLength, InputBufferLength )); } } IF_TCPDBG(TCP_DEBUG_INFO) { TCPTRACE(( "QueryInformationEx complete - irp %lx, status %lx\n", Irp, status )); } Irp->IoStatus.Status = (NTSTATUS) status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(status); } NTSTATUS TCPSetInformationEx( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Converts a TDI SetInformationEx IRP into a call to TdiSetInformationEx. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. Notes: This routine does not pend. --*/ { TDI_REQUEST request; TDI_STATUS status; PTCP_CONTEXT tcpContext; PTCP_REQUEST_SET_INFORMATION_EX setInformation; PAGED_CODE(); IF_TCPDBG(TCP_DEBUG_INFO) { TCPTRACE(( "SetInformationEx - irp %lx fileobj %lx\n", Irp, IrpSp->FileObject )); } tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; setInformation = (PTCP_REQUEST_SET_INFORMATION_EX) Irp->AssociatedIrp.SystemBuffer; switch ((int) IrpSp->FileObject->FsContext2) { case TDI_TRANSPORT_ADDRESS_FILE: request.Handle.AddressHandle = tcpContext->Handle.AddressHandle; break; case TDI_CONNECTION_FILE: request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; break; case TDI_CONTROL_CHANNEL_FILE: request.Handle.ControlChannel = tcpContext->Handle.ControlChannel; break; default: CTEAssert(0); Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(STATUS_INVALID_PARAMETER); } status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL); if (NT_SUCCESS(status)) { request.RequestNotifyObject = TCPDataRequestComplete; request.RequestContext = Irp; status = TdiSetInformationEx( &request, &(setInformation->ID), &(setInformation->Buffer[0]), setInformation->BufferSize ); if (status != TDI_PENDING) { TCPDataRequestComplete( Irp, status, 0 ); return(status); } IF_TCPDBG(TCP_DEBUG_INFO) { TCPTRACE(( "SetInformationEx - pending irp %lx fileobj %lx\n", Irp, IrpSp->FileObject )); } return(STATUS_PENDING); } IF_TCPDBG(TCP_DEBUG_INFO) { TCPTRACE(( "SetInformationEx complete - irp %lx\n", Irp )); } // // The irp has already been completed. // return(status); } #ifdef SECFLTR NTSTATUS TCPControlSecurityFilter( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Processes a request to query or set the status of security filtering. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. Notes: This routine does not pend. --*/ { PTCP_SECURITY_FILTER_STATUS request; ULONG requestLength; ULONG requestCode; TDI_STATUS status = STATUS_SUCCESS; PAGED_CODE(); Irp->IoStatus.Information = 0; request = (PTCP_SECURITY_FILTER_STATUS) Irp->AssociatedIrp.SystemBuffer; requestCode = IrpSp->Parameters.DeviceIoControl.IoControlCode; if (requestCode == IOCTL_TCP_QUERY_SECURITY_FILTER_STATUS) { requestLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; if (requestLength < sizeof(TCP_SECURITY_FILTER_STATUS)) { status = STATUS_INVALID_PARAMETER; } else { request->FilteringEnabled = IsSecurityFilteringEnabled(); Irp->IoStatus.Information = sizeof(TCP_SECURITY_FILTER_STATUS); } } else { CTEAssert(requestCode == IOCTL_TCP_SET_SECURITY_FILTER_STATUS); requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; if (requestLength < sizeof(TCP_SECURITY_FILTER_STATUS)) { status = STATUS_INVALID_PARAMETER; } else { ControlSecurityFiltering(request->FilteringEnabled); } } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(status); } NTSTATUS TCPProcessSecurityFilterRequest( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Processes a request to add or delete a transport security filter. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. Notes: This routine does not pend. --*/ { TCPSecurityFilterEntry *request; ULONG requestLength; ULONG i; ULONG requestCode; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); Irp->IoStatus.Information = 0; request = (TCPSecurityFilterEntry *) Irp->AssociatedIrp.SystemBuffer; requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; requestCode = IrpSp->Parameters.DeviceIoControl.IoControlCode; if (requestLength < sizeof(TCPSecurityFilterEntry)) { status = STATUS_INVALID_PARAMETER; } else { if (requestCode == IOCTL_TCP_ADD_SECURITY_FILTER) { status = AddValueSecurityFilter( net_long(request->tsf_address), request->tsf_protocol, request->tsf_value ); } else { CTEAssert(requestCode == IOCTL_TCP_DELETE_SECURITY_FILTER); status = DeleteValueSecurityFilter( net_long(request->tsf_address), request->tsf_protocol, request->tsf_value ); } } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(status); } NTSTATUS TCPEnumerateSecurityFilter( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Processes a request to enumerate a transport security filter list. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. Notes: This routine does not pend. --*/ { TCPSecurityFilterEntry *request; TCPSecurityFilterEnum *response; ULONG requestLength, responseLength; NTSTATUS status; PAGED_CODE(); request = (TCPSecurityFilterEntry *) Irp->AssociatedIrp.SystemBuffer; response = (TCPSecurityFilterEnum *) request; requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; responseLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; if (requestLength < sizeof(TCPSecurityFilterEntry)) { status = STATUS_INVALID_PARAMETER; Irp->IoStatus.Information = 0; } else if (responseLength < sizeof(TCPSecurityFilterEnum)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = 0; } else { EnumerateSecurityFilters( net_long(request->tsf_address), request->tsf_protocol, request->tsf_value, (uchar *) (response + 1), responseLength - sizeof(TCPSecurityFilterEnum), &(response->tfe_entries_returned), &(response->tfe_entries_available) ); status = TDI_SUCCESS; Irp->IoStatus.Information = sizeof(TCPSecurityFilterEnum) + (response->tfe_entries_returned * sizeof(TCPSecurityFilterEntry)); } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(status); } #endif // SECFLTR NTSTATUS TCPEnumerateConnectionList( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Processes a request to enumerate the workstation connection list. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successful. Notes: This routine does not pend. --*/ { TCPConnectionListEntry *request; TCPConnectionListEnum *response; ULONG requestLength, responseLength; NTSTATUS status; PAGED_CODE(); request = (TCPConnectionListEntry *) Irp->AssociatedIrp.SystemBuffer; response = (TCPConnectionListEnum *) request; requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; responseLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; if (responseLength < sizeof(TCPConnectionListEnum)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = 0; } else { EnumerateConnectionList( (uchar *) (response + 1), responseLength - sizeof(TCPConnectionListEnum), &(response->tce_entries_returned), &(response->tce_entries_available) ); status = TDI_SUCCESS; Irp->IoStatus.Information = sizeof(TCPConnectionListEnum) + (response->tce_entries_returned * sizeof(TCPConnectionListEntry)); } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(status); } NTSTATUS TCPCreate( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Arguments: DeviceObject - Pointer to the device object for this request. Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { TDI_REQUEST Request; NTSTATUS status; FILE_FULL_EA_INFORMATION *ea; FILE_FULL_EA_INFORMATION UNALIGNED *targetEA; PTCP_CONTEXT tcpContext; uint protocol; PAGED_CODE(); tcpContext = ExAllocatePool(NonPagedPool, sizeof(TCP_CONTEXT)); if (tcpContext == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } #if DBG InitializeListHead(&(tcpContext->PendingIrpList)); InitializeListHead(&(tcpContext->CancelledIrpList)); #endif tcpContext->ReferenceCount = 1; // put initial reference on open object tcpContext->CancelIrps = FALSE; KeInitializeEvent(&(tcpContext->CleanupEvent), SynchronizationEvent, FALSE); ea = (PFILE_FULL_EA_INFORMATION) Irp->AssociatedIrp.SystemBuffer; // // See if this is a Control Channel open. // if (!ea) { IF_TCPDBG(TCP_DEBUG_OPEN) { TCPTRACE(( "TCPCreate: Opening control channel for file object %lx\n", IrpSp->FileObject )); } tcpContext->Handle.ControlChannel = NULL; IrpSp->FileObject->FsContext = tcpContext; IrpSp->FileObject->FsContext2 = (PVOID) TDI_CONTROL_CHANNEL_FILE; return(STATUS_SUCCESS); } // // See if this is an Address Object open. // targetEA = FindEA( ea, TdiTransportAddress, TDI_TRANSPORT_ADDRESS_LENGTH ); if (targetEA != NULL) { UCHAR optionsBuffer[3]; PUCHAR optionsPointer = optionsBuffer; if (DeviceObject == TCPDeviceObject) { protocol = PROTOCOL_TCP; } else if (DeviceObject == UDPDeviceObject) { protocol = PROTOCOL_UDP; CTEAssert(optionsPointer - optionsBuffer <= 3); if (IsDHCPZeroAddress( (TRANSPORT_ADDRESS UNALIGNED *) &(targetEA->EaName[targetEA->EaNameLength + 1]) )) { *optionsPointer = TDI_ADDRESS_OPTION_DHCP; optionsPointer++; } CTEAssert(optionsPointer - optionsBuffer <= 3); } else { // // This is a raw ip open // protocol = RawExtractProtocolNumber( &(IrpSp->FileObject->FileName) ); if (protocol == 0xFFFFFFFF) { ExFreePool(tcpContext); return(STATUS_INVALID_PARAMETER); } } if ( (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE) ) { *optionsPointer = TDI_ADDRESS_OPTION_REUSE; optionsPointer++; } *optionsPointer = TDI_OPTION_EOL; IF_TCPDBG(TCP_DEBUG_OPEN) { TCPTRACE(( "TCPCreate: Opening address for file object %lx\n", IrpSp->FileObject )); } status = TdiOpenAddress( &Request, (TRANSPORT_ADDRESS UNALIGNED *) &(targetEA->EaName[targetEA->EaNameLength + 1]), protocol, optionsBuffer ); if (NT_SUCCESS(status)) { // // Save off the handle to the AO passed back. // tcpContext->Handle.AddressHandle = Request.Handle.AddressHandle; IrpSp->FileObject->FsContext = tcpContext; IrpSp->FileObject->FsContext2 = (PVOID) TDI_TRANSPORT_ADDRESS_FILE; } else { ExFreePool(tcpContext); TCPTRACE(("TdiOpenAddress failed, status %lx\n", status)); if (status == STATUS_ADDRESS_ALREADY_EXISTS) { status = STATUS_SHARING_VIOLATION; } } CTEAssert(status != TDI_PENDING); return(status); } // // See if this is a Connection Object open. // targetEA = FindEA( ea, TdiConnectionContext, TDI_CONNECTION_CONTEXT_LENGTH ); if (targetEA != NULL) { // // This is an open of a Connection Object. // if (DeviceObject == TCPDeviceObject) { IF_TCPDBG(TCP_DEBUG_OPEN) { TCPTRACE(( "TCPCreate: Opening connection for file object %lx\n", IrpSp->FileObject )); } status = TdiOpenConnection( &Request, *((CONNECTION_CONTEXT UNALIGNED *) &(targetEA->EaName[targetEA->EaNameLength + 1])) ); if (NT_SUCCESS(status)) { // // Save off the Connection Context passed back. // tcpContext->Handle.ConnectionContext = Request.Handle.ConnectionContext; IrpSp->FileObject->FsContext = tcpContext; IrpSp->FileObject->FsContext2 = (PVOID) TDI_CONNECTION_FILE; } else { ExFreePool(tcpContext); TCPTRACE(( "TdiOpenConnection failed, status %lx\n", status )); } } else { TCPTRACE(( "TCP: TdiOpenConnection issued on UDP device!\n" )); status = STATUS_INVALID_DEVICE_REQUEST; ExFreePool(tcpContext); } CTEAssert(status != TDI_PENDING); return(status); } TCPTRACE(("TCPCreate: didn't find any useful ea's\n")); status = STATUS_INVALID_EA_NAME; ExFreePool(tcpContext); CTEAssert(status != TDI_PENDING); return(status); } // TCPCreate void TCPCloseObjectComplete( void *Context, unsigned int Status, unsigned int UnUsed ) /*++ Routine Description: Completes a TdiCloseConnectoin or TdiCloseAddress request. Arguments: Context - A pointer to the IRP for this request. Status - The final status of the operation. UnUsed - An unused parameter Return Value: None. Notes: --*/ { KIRQL oldIrql; PIRP irp; PIO_STACK_LOCATION irpSp; PTCP_CONTEXT tcpContext; UNREFERENCED_PARAMETER(UnUsed); irp = (PIRP) Context; irpSp = IoGetCurrentIrpStackLocation(irp); tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext; irp->IoStatus.Status = Status; IF_TCPDBG(TCP_DEBUG_CLEANUP) { TCPTRACE(( "TCPCloseObjectComplete on file object %lx\n", irpSp->FileObject )); } IoAcquireCancelSpinLock(&oldIrql); CTEAssert(tcpContext->ReferenceCount > 0); CTEAssert(tcpContext->CancelIrps); // // Remove the initial reference that was put on by TCPCreate. // CTEAssert(tcpContext->ReferenceCount > 0); if (--(tcpContext->ReferenceCount) == 0) { IF_TCPDBG(TCP_DEBUG_CANCEL) { CTEAssert(IsListEmpty(&(tcpContext->CancelledIrpList))); CTEAssert(IsListEmpty(&(tcpContext->PendingIrpList))); } KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE); } IF_TCPDBG(TCP_DEBUG_IRP) { TCPTRACE(( "TCPCloseObjectComplete: irp %lx fileobj %lx refcnt dec to %u\n", irp, irpSp, tcpContext->ReferenceCount )); } IoReleaseCancelSpinLock(oldIrql); return; } // TCPCleanupComplete NTSTATUS TCPCleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Cancels all outstanding Irps on a TDI object by calling the close routine for the object. It then waits for them to be completed before returning. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. Notes: This routine blocks, but does not pend. --*/ { KIRQL oldIrql; PIRP cancelIrp = NULL; PTCP_CONTEXT tcpContext; NTSTATUS status; TDI_REQUEST request; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; IoAcquireCancelSpinLock(&oldIrql); tcpContext->CancelIrps = TRUE; KeResetEvent(&(tcpContext->CleanupEvent)); IoReleaseCancelSpinLock(oldIrql); // // Now call the TDI close routine for this object to force all of its Irps // to complete. // request.RequestNotifyObject = TCPCloseObjectComplete; request.RequestContext = Irp; switch ((int) IrpSp->FileObject->FsContext2) { case TDI_TRANSPORT_ADDRESS_FILE: IF_TCPDBG(TCP_DEBUG_CLOSE) { TCPTRACE(( "TCPCleanup: Closing address object on file object %lx\n", IrpSp->FileObject )); } request.Handle.AddressHandle = tcpContext->Handle.AddressHandle; status = TdiCloseAddress(&request); break; case TDI_CONNECTION_FILE: IF_TCPDBG(TCP_DEBUG_CLOSE) { TCPTRACE(( "TCPCleanup: Closing Connection object on file object %lx\n", IrpSp->FileObject )); } request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; status = TdiCloseConnection(&request); break; case TDI_CONTROL_CHANNEL_FILE: IF_TCPDBG(TCP_DEBUG_CLOSE) { TCPTRACE(( "TCPCleanup: Closing Control Channel object on file object %lx\n", IrpSp->FileObject )); } status = STATUS_SUCCESS; break; default: // // This should never happen. // CTEAssert(FALSE); IoAcquireCancelSpinLock(&oldIrql); tcpContext->CancelIrps = FALSE; IoReleaseCancelSpinLock(oldIrql); return(STATUS_INVALID_PARAMETER); } if (status != TDI_PENDING) { TCPCloseObjectComplete(Irp, status, 0); } IF_TCPDBG(TCP_DEBUG_CLEANUP) { TCPTRACE(( "TCPCleanup: waiting for completion of Irps on file object %lx\n", IrpSp->FileObject )); } status = KeWaitForSingleObject( &(tcpContext->CleanupEvent), UserRequest, KernelMode, FALSE, NULL ); CTEAssert(NT_SUCCESS(status)); IF_TCPDBG(TCP_DEBUG_CLEANUP) { TCPTRACE(( "TCPCleanup: Wait on file object %lx finished\n", IrpSp->FileObject )); } // // The cleanup Irp will be completed by the dispatch routine. // return(Irp->IoStatus.Status); } // TCPCleanup NTSTATUS TCPClose( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Dispatch routine for MJ_CLOSE IRPs. Performs final cleanup of the open endpoint. Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. Notes: This request does not pend. --*/ { PTCP_CONTEXT tcpContext; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; #if DBG IF_TCPDBG(TCP_DEBUG_CANCEL) { KIRQL oldIrql; IoAcquireCancelSpinLock(&oldIrql); CTEAssert(tcpContext->ReferenceCount == 0); CTEAssert(IsListEmpty(&(tcpContext->PendingIrpList))); CTEAssert(IsListEmpty(&(tcpContext->CancelledIrpList))); IoReleaseCancelSpinLock(oldIrql); } #endif // DBG IF_TCPDBG(TCP_DEBUG_CLOSE) { TCPTRACE(("TCPClose on file object %lx\n", IrpSp->FileObject)); } ExFreePool(tcpContext); return(STATUS_SUCCESS); } // TCPClose NTSTATUS TCPDispatchDeviceControl( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: Arguments: Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { NTSTATUS status; PAGED_CODE(); // // Set this in advance. Any IOCTL dispatch routine that cares about it // will modify it itself. // Irp->IoStatus.Information = 0; switch(IrpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_TCP_QUERY_INFORMATION_EX: return(TCPQueryInformationEx(Irp, IrpSp)); break; case IOCTL_TCP_SET_INFORMATION_EX: return(TCPSetInformationEx(Irp, IrpSp)); break; #ifdef SECFLTR case IOCTL_TCP_QUERY_SECURITY_FILTER_STATUS: case IOCTL_TCP_SET_SECURITY_FILTER_STATUS: return(TCPControlSecurityFilter(Irp, IrpSp)); break; case IOCTL_TCP_ADD_SECURITY_FILTER: case IOCTL_TCP_DELETE_SECURITY_FILTER: return(TCPProcessSecurityFilterRequest(Irp, IrpSp)); break; case IOCTL_TCP_ENUMERATE_SECURITY_FILTER: return(TCPEnumerateSecurityFilter(Irp, IrpSp)); break; #endif // SECFLTR default: status = STATUS_NOT_IMPLEMENTED; break; } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return status; } // TCPDispatchDeviceControl NTSTATUS TCPDispatchInternalDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This is the dispatch routine for Internal Device Control IRPs. This is the hot path for kernel-mode clients. Arguments: DeviceObject - Pointer to device object for target device Irp - Pointer to I/O request packet Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PIO_STACK_LOCATION irpSp; NTSTATUS status; if (DeviceObject != IPDeviceObject) { irpSp = IoGetCurrentIrpStackLocation(Irp); if (((int)irpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) { // // Send and receive are the performance path, so check for them // right away. // if (irpSp->MinorFunction == TDI_SEND) { return(TCPSendData(Irp, irpSp)); } if (irpSp->MinorFunction == TDI_RECEIVE) { return(TCPReceiveData(Irp, irpSp)); } switch(irpSp->MinorFunction) { case TDI_ASSOCIATE_ADDRESS: status = TCPAssociateAddress(Irp, irpSp); Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(status); case TDI_DISASSOCIATE_ADDRESS: return(TCPDisassociateAddress(Irp, irpSp)); case TDI_CONNECT: return(TCPConnect(Irp, irpSp)); case TDI_DISCONNECT: return(TCPDisconnect(Irp, irpSp)); case TDI_LISTEN: return(TCPListen(Irp, irpSp)); case TDI_ACCEPT: return(TCPAccept(Irp, irpSp)); default: break; } // // Fall through. // } else if ( ((int)irpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE ) { if (irpSp->MinorFunction == TDI_SEND_DATAGRAM) { return(UDPSendDatagram(Irp, irpSp)); } if (irpSp->MinorFunction == TDI_RECEIVE_DATAGRAM) { return(UDPReceiveDatagram(Irp, irpSp)); } if (irpSp->MinorFunction == TDI_SET_EVENT_HANDLER) { status = TCPSetEventHandler(Irp, irpSp); Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(status); } // // Fall through. // } CTEAssert( (((int)irpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE) || (((int)irpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) || (((int)irpSp->FileObject->FsContext2) == TDI_CONTROL_CHANNEL_FILE) ); // // These functions are common to all endpoint types. // switch(irpSp->MinorFunction) { case TDI_QUERY_INFORMATION: return(TCPQueryInformation(Irp, irpSp)); case TDI_SET_INFORMATION: case TDI_ACTION: TCPTRACE(( "TCP: Call to unimplemented TDI function 0x%x\n", irpSp->MinorFunction )); status = STATUS_NOT_IMPLEMENTED; break; default: TCPTRACE(( "TCP: call to invalid TDI function 0x%x\n", irpSp->MinorFunction )); status = STATUS_INVALID_DEVICE_REQUEST; } CTEAssert(status != TDI_PENDING); Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return status; } return(IPDispatch(DeviceObject, Irp)); } NTSTATUS TCPDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This is the generic dispatch routine for TCP/UDP/RawIP. Arguments: DeviceObject - Pointer to device object for target device Irp - Pointer to I/O request packet Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PIO_STACK_LOCATION irpSp; NTSTATUS status; if (DeviceObject != IPDeviceObject) { irpSp = IoGetCurrentIrpStackLocation(Irp); CTEAssert(irpSp->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL); switch (irpSp->MajorFunction) { case IRP_MJ_CREATE: status = TCPCreate(DeviceObject, Irp, irpSp); break; case IRP_MJ_CLEANUP: status = TCPCleanup(DeviceObject, Irp, irpSp); break; case IRP_MJ_CLOSE: status = TCPClose(Irp, irpSp); break; case IRP_MJ_DEVICE_CONTROL: status = TdiMapUserRequest(DeviceObject, Irp, irpSp); if (status == STATUS_SUCCESS) { return(TCPDispatchInternalDeviceControl(DeviceObject, Irp)); } return(TCPDispatchDeviceControl( Irp, IoGetCurrentIrpStackLocation(Irp) )); break; case IRP_MJ_QUERY_SECURITY: // // This is generated on Raw endpoints. We don't do anything // for it. // status = STATUS_INVALID_DEVICE_REQUEST; break; case IRP_MJ_WRITE: case IRP_MJ_READ: default: TCPTRACE(( "TCPDispatch: Irp %lx unsupported major function 0x%lx\n", irpSp, irpSp->MajorFunction )); status = STATUS_INVALID_DEVICE_REQUEST; break; } CTEAssert(status != TDI_PENDING); Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return status; } return(IPDispatch(DeviceObject, Irp)); } // TCPDispatch // // Private utility functions // FILE_FULL_EA_INFORMATION UNALIGNED * FindEA( PFILE_FULL_EA_INFORMATION StartEA, CHAR *TargetName, USHORT TargetNameLength ) /*++ Routine Description: Parses and extended attribute list for a given target attribute. Arguments: StartEA - the first extended attribute in the list. TargetName - the name of the target attribute. TargetNameLength - the length of the name of the target attribute. Return Value: A pointer to the requested attribute or NULL if the target wasn't found. --*/ { USHORT i; BOOLEAN found; FILE_FULL_EA_INFORMATION UNALIGNED *CurrentEA; PAGED_CODE(); do { found = TRUE; CurrentEA = StartEA; StartEA += CurrentEA->NextEntryOffset; if (CurrentEA->EaNameLength != TargetNameLength) { continue; } for (i=0; i < CurrentEA->EaNameLength; i++) { if (CurrentEA->EaName[i] == TargetName[i]) { continue; } found = FALSE; break; } if (found) { return(CurrentEA); } } while(CurrentEA->NextEntryOffset != 0); return(NULL); } BOOLEAN IsDHCPZeroAddress( TRANSPORT_ADDRESS UNALIGNED *AddrList ) /*++ Routine Description: Checks a TDI IP address list for an address from DHCP binding to the IP address zero. Normally, binding to zero means wildcard. For DHCP, it really means bind to an interface with an address of zero. This semantic is flagged by a special value in an unused portion of the address structure (ie. this is a kludge). Arguments: AddrList - The TDI transport address list passed in the create IRP. Return Value: TRUE if the first IP address found had the flag set. FALSE otherwise. --*/ { int i; // Index variable. TA_ADDRESS UNALIGNED *CurrentAddr; // Address we're examining and may use. // First, verify that someplace in Address is an address we can use. CurrentAddr = (TA_ADDRESS UNALIGNED *)AddrList->Address; for (i = 0; i < AddrList->TAAddressCount; i++) { if (CurrentAddr->AddressType == TDI_ADDRESS_TYPE_IP) { if (CurrentAddr->AddressLength == TDI_ADDRESS_LENGTH_IP) { TDI_ADDRESS_IP UNALIGNED *ValidAddr; ValidAddr = (TDI_ADDRESS_IP UNALIGNED *)CurrentAddr->Address; if (*((ULONG UNALIGNED *) ValidAddr->sin_zero) == 0x12345678) { return TRUE; } } else { return FALSE; // Wrong length for address. } } else { CurrentAddr = (TA_ADDRESS UNALIGNED *) (CurrentAddr->Address + CurrentAddr->AddressLength); } } return FALSE; // Didn't find a match. } ULONG TCPGetMdlChainByteCount( PMDL Mdl ) /*++ Routine Description: Sums the byte counts of each MDL in a chain. Arguments: Mdl - Pointer to the MDL chain to sum. Return Value: The byte count of the MDL chain. --*/ { ULONG count = 0; while (Mdl != NULL) { count += MmGetMdlByteCount(Mdl); Mdl = Mdl->Next; } return(count); } ULONG RawExtractProtocolNumber( IN PUNICODE_STRING FileName ) /*++ Routine Description: Extracts the protocol number from the file object name. Arguments: FileName - The unicode file name. Return Value: The protocol number or 0xFFFFFFFF on error. --*/ { PWSTR name; UNICODE_STRING unicodeString; USHORT length; ULONG protocol; NTSTATUS status; PAGED_CODE(); name = FileName->Buffer; if ( FileName->Length < (sizeof(OBJ_NAME_PATH_SEPARATOR) + sizeof(WCHAR)) ) { return(0xFFFFFFFF); } // // Step over separator // if (*name++ != OBJ_NAME_PATH_SEPARATOR) { return(0xFFFFFFFF); } if (*name == UNICODE_NULL) { return(0xFFFFFFFF); } // // Convert the remaining name into a number. // RtlInitUnicodeString(&unicodeString, name); status = RtlUnicodeStringToInteger( &unicodeString, 10, &protocol ); if (!NT_SUCCESS(status)) { return(0xFFFFFFFF); } if (protocol > 255) { return(0xFFFFFFFF); } return(protocol); }