// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs) // // Copyright (c) 1985-2000 Microsoft Corporation // // This file is part of the Microsoft Research IPv6 Network Protocol Stack. // You should have received a copy of the Microsoft End-User License Agreement // for this software along with this release; see the file "license.txt". // If not, please see http://www.research.microsoft.com/msripv6/license.htm, // or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399. // // Abstract: // // NT specific routines for dispatching and handling IRPs. // #include #include #include #include #include #include #include #include #include #include "queue.h" #include "transprt.h" #include "addr.h" #include "tcp.h" #include "udp.h" #include "raw.h" #include #include "tcpcfg.h" #include "tcpconn.h" #include "tdilocal.h" // // Macros // //* Convert100nsToMillisconds // // Converts time expressed in hundreds of nanoseconds to milliseconds. // // REVIEW: replace RtlExtendedMagicDivide with 64 bit compiler support? // // LARGE_INTEGER // Returns: Time in milliseconds. // Convert100nsToMilliseconds( // IN LARGE_INTEGER HnsTime); // Time in hundreds of nanoseconds. // #define SHIFT10000 13 static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758}; #define Convert100nsToMilliseconds(HnsTime) \ RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000) // // Global variables // extern PSECURITY_DESCRIPTOR TcpAdminSecurityDescriptor; 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 ); // // Other external functions // void TCPAbortAndIndicateDisconnect( CONNECTION_CONTEXT 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 IsAdminIoRequest( PIRP Irp, PIO_STACK_LOCATION IrpSp ); BOOLEAN IsDHCPZeroAddress( TRANSPORT_ADDRESS UNALIGNED *AddrList ); ULONG RawExtractProtocolNumber( IN PUNICODE_STRING FileName ); 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) #pragma alloc_text(PAGE, IsAdminIoRequest) #endif // ALLOC_PRAGMA // // Generic Irp completion and cancellation routines. // //* TCPDataRequestComplete - Completes a UDP/TCP send/receive request. // NTSTATUS // Returns: Nothing. TCPDataRequestComplete( void *Context, // A pointer to the IRP for this request. unsigned int Status, // The final TDI status of the request. unsigned int ByteCount) // Bytes sent/received information. { 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; if (IoSetCancelRoutine(irp, NULL) == NULL) { IoAcquireCancelSpinLock(&oldIrql); IoReleaseCancelSpinLock(oldIrql); } KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql); #if DBG IF_TCPDBG(TCP_DEBUG_CANCEL) { PLIST_ENTRY entry, listHead; PIRP item = NULL; if (irp->Cancel) { ASSERT(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; } } ASSERT(item == irp); } #endif ASSERT(tcpContext->ReferenceCount > 0); if (--(tcpContext->ReferenceCount) == 0) { IF_TCPDBG(TCP_DEBUG_CANCEL) { ASSERT(IsListEmpty(&(tcpContext->CancelledIrpList))); ASSERT(IsListEmpty(&(tcpContext->PendingIrpList))); } // // Set the cleanup event. // KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE); } IF_TCPDBG(TCP_DEBUG_IRP) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPDataRequestComplete: Irp %lx was cancelled\n", irp)); } Status = (unsigned int) STATUS_CANCELLED; ByteCount = 0; } KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql); IF_TCPDBG(TCP_DEBUG_IRP) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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 Status; } // TCPDataRequestComplete //* TCPRequestComplete - Completes a TDI request. // // Completes a cancellable TDI request which returns no data by // calling TCPDataRequestComplete with a ByteCount of zero. // void // Returns: Nothing. TCPRequestComplete( void *Context, // A pointer to the IRP for this request. unsigned int Status, // The final TDI status of the request. unsigned int UnUsed) // An unused parameter. { UNREFERENCED_PARAMETER(UnUsed); TCPDataRequestComplete(Context, Status, 0); } // TCPRequestComplete //* TCPNonCancellableRequestComplete - Complete uncancellable TDI request. // // Completes a TDI request which cannot be cancelled. // void // Returns: Nothing. TCPNonCancellableRequestComplete( void *Context, // A pointer to the IRP for this request. unsigned int Status, // The final TDI status of the request. unsigned int UnUsed) // An unused parameter. { PIRP irp; PIO_STACK_LOCATION irpSp; UNREFERENCED_PARAMETER(UnUsed); irp = (PIRP) Context; irpSp = IoGetCurrentIrpStackLocation(irp); IF_TCPDBG(TCP_DEBUG_CLOSE) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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 //* TCPCancelComplete // 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); KeAcquireSpinLock(&tcpContext->EndpointLock, &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) { ASSERT(IsListEmpty(&(tcpContext->CancelledIrpList))); ASSERT(IsListEmpty(&(tcpContext->PendingIrpList))); } // // Set the cleanup event. // KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql); KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE); return; } IF_TCPDBG(TCP_DEBUG_IRP) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPCancelComplete: fileobj %lx refcnt dec to %u\n", fileObject, tcpContext->ReferenceCount)); } KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql); return; } // TCPCancelComplete //* TCPCancelRequest - Cancels an outstanding Irp. // // Cancel an outstanding Irp. // VOID // Returns: Nothing. TCPCancelRequest( PDEVICE_OBJECT Device, // Pointer to the device object for this request. PIRP Irp) // Pointer to I/O request packet. { PIO_STACK_LOCATION irpSp; PTCP_CONTEXT tcpContext; NTSTATUS status = STATUS_SUCCESS; PFILE_OBJECT fileObject; UCHAR minorFunction; TDI_REQUEST request; KIRQL CancelIrql; irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; tcpContext = (PTCP_CONTEXT) fileObject->FsContext; minorFunction = irpSp->MinorFunction; CancelIrql = Irp->CancelIrql; KeAcquireSpinLockAtDpcLevel(&tcpContext->EndpointLock); ASSERT(Irp->Cancel); IoReleaseCancelSpinLock(DISPATCH_LEVEL); IF_TCPDBG(TCP_DEBUG_IRP) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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; } } ASSERT(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. // ASSERT(tcpContext->ReferenceCount > 0); tcpContext->ReferenceCount++; IF_TCPDBG(TCP_DEBUG_IRP) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPCancelRequest: Irp %lx fileobj %lx refcnt inc to %u\n", Irp, fileObject, tcpContext->ReferenceCount)); } // // Try to cancel the request. // switch(minorFunction) { case TDI_SEND: case TDI_RECEIVE: KeReleaseSpinLock(&tcpContext->EndpointLock, CancelIrql); ASSERT((PtrToUlong(fileObject->FsContext2)) == TDI_CONNECTION_FILE); #ifndef UDP_ONLY TCPAbortAndIndicateDisconnect(tcpContext->Handle.ConnectionContext); #endif break; case TDI_SEND_DATAGRAM: ASSERT(PtrToUlong(fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE); TdiCancelSendDatagram(tcpContext->Handle.AddressHandle, Irp, &tcpContext->EndpointLock, CancelIrql); break; case TDI_RECEIVE_DATAGRAM: ASSERT(PtrToUlong(fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE); TdiCancelReceiveDatagram(tcpContext->Handle.AddressHandle, Irp, &tcpContext->EndpointLock, CancelIrql); break; case TDI_DISASSOCIATE_ADDRESS: ASSERT(PtrToUlong(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. // KeReleaseSpinLock(&tcpContext->EndpointLock, CancelIrql); break; default: // // Initiate a disconnect to cancel the request. // KeReleaseSpinLock(&tcpContext->EndpointLock, CancelIrql); 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 //* TCPPrepareIrpForCancel // NTSTATUS TCPPrepareIrpForCancel( PTCP_CONTEXT TcpContext, PIRP Irp, PDRIVER_CANCEL CancelRoutine) { KIRQL oldIrql; // // Set up for cancellation. // KeAcquireSpinLock(&TcpContext->EndpointLock, &oldIrql); ASSERT(Irp->CancelRoutine == NULL); if (!Irp->Cancel) { IoMarkIrpPending(Irp); IoSetCancelRoutine(Irp, CancelRoutine); TcpContext->ReferenceCount++; IF_TCPDBG(TCP_DEBUG_IRP) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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); ASSERT(item != Irp); } for (entry = TcpContext->CancelledIrpList.Flink; entry != &(TcpContext->CancelledIrpList); entry = entry->Flink) { item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); ASSERT(item != Irp); } InsertTailList(&(TcpContext->PendingIrpList), &(Irp->Tail.Overlay.ListEntry)); } #endif // DBG KeReleaseSpinLock(&TcpContext->EndpointLock, oldIrql); return(STATUS_SUCCESS); } // // The IRP has already been cancelled. Complete it now. // IF_TCPDBG(TCP_DEBUG_IRP) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCP: irp %lx already cancelled, completing.\n", Irp)); } KeReleaseSpinLock(&TcpContext->EndpointLock, oldIrql); Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(STATUS_CANCELLED); } // TCPPrepareIrpForCancel // // TDI functions. // //* TCPAssociateAddress - Handle TDI Associate Address IRP. // // Converts a TDI Associate Address IRP into a call to TdiAssociateAddress. // // This routine does not pend. // NTSTATUS // Returns: indication of whether the request was successful. TCPAssociateAddress( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { 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, *IoFileObjectType, Irp->RequestorMode, &fileObject, NULL); if (NT_SUCCESS(status)) { if ((fileObject->DeviceObject == TCPDeviceObject) && (PtrToUlong(fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE)) { tcpContext = (PTCP_CONTEXT) fileObject->FsContext; status = TdiAssociateAddress(&request, tcpContext->Handle.AddressHandle); ASSERT(status != STATUS_PENDING); ObDereferenceObject(fileObject); IF_TCPDBG(TCP_DEBUG_ASSOCIATE) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPAssociateAddress complete on file object %lx\n", IrpSp->FileObject)); } } else { ObDereferenceObject(fileObject); status = STATUS_INVALID_HANDLE; IF_TCPDBG(TCP_DEBUG_ASSOCIATE) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPAssociateAddress: ObReference failed on object" " %lx, status %lx\n", associateInformation->AddressHandle, status)); } } } else { IF_TCPDBG(TCP_DEBUG_ASSOCIATE) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPAssociateAddress: ObReference failed on object %lx," " status %lx\n", associateInformation->AddressHandle, status)); } } return(status); } //* TCPDisassociateAddress - Handle TDI Disassociate Address IRP. // // Converts a TDI Disassociate Address IRP into a call to // TdiDisassociateAddress. NTSTATUS // Returns: Indication of whether the request was successful. TCPDisassociateAddress( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { NTSTATUS status; TDI_REQUEST request; PTCP_CONTEXT tcpContext; IF_TCPDBG(TCP_DEBUG_ASSOCIATE) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCP disassociating address\n")); } ASSERT(PtrToUlong(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 //* TCPConnect - Handle TDI Connect IRP. // // Converts a TDI Connect IRP into a call to TdiConnect. // NTSTATUS // Returns: Whether the request was successfully queued. TCPConnect( IN PIRP Irp, // Pointer to I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { 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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPConnect irp %lx, file object %lx\n", Irp, IrpSp->FileObject)); } ASSERT(PtrToUlong(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 = Convert100nsToMilliseconds(millisecondTimeout); } else { millisecondTimeout.LowPart = 0; millisecondTimeout.HighPart = 0; } ASSERT(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 //* TCPDisconnect - Handler for TDI Disconnect IRP // // Converts a TDI Disconnect IRP into a call to TdiDisconnect. // NTSTATUS // Returns: whether the request was successfully queued. TCPDisconnect( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { 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; ASSERT(PtrToUlong(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 = Convert100nsToMilliseconds( millisecondTimeout); } } else { millisecondTimeout.LowPart = 0; millisecondTimeout.HighPart = 0; } ASSERT(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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPDisconnect pending irp %lx\n", Irp)); } } // // return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING // return(STATUS_PENDING); } return(status); } // TCPDisconnect //* TCPListen - Handler for TDI Listen IRP. // // Converts a TDI Listen IRP into a call to TdiListen. // NTSTATUS // Returns: whether or not the request was successful. TCPListen( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { NTSTATUS status; PTCP_CONTEXT tcpContext; TDI_REQUEST request; PTDI_CONNECTION_INFORMATION requestInformation, returnInformation; PTDI_REQUEST_KERNEL_LISTEN listenRequest; IF_TCPDBG(TCP_DEBUG_CONNECT) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPListen irp %lx on file object %lx\n", Irp, IrpSp->FileObject)); } ASSERT(PtrToUlong(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 //* TCPAccept - Handle a TDI Accept IRP. // // Converts a TDI Accept IRP into a call to TdiAccept. // NTSTATUS // Returns: whether the request was successfully queued. TCPAccept( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { NTSTATUS status; PTCP_CONTEXT tcpContext; TDI_REQUEST request; PTDI_CONNECTION_INFORMATION requestInformation, returnInformation; PTDI_REQUEST_KERNEL_ACCEPT acceptRequest; IF_TCPDBG(TCP_DEBUG_CONNECT) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPAccept irp %lx on file object %lx\n", Irp, IrpSp->FileObject)); } ASSERT(PtrToUlong(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 //* TCPSendData - Handle TDI Send IRP. // // Converts a TDI Send IRP into a call to TdiSend. // NTSTATUS TCPSendData( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { TDI_STATUS status; TDI_REQUEST request; PTCP_CONTEXT tcpContext; PTDI_REQUEST_KERNEL_SEND requestInformation; KIRQL oldIrql; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; ASSERT(PtrToUlong(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; KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql); IoSetCancelRoutine(Irp, TCPCancelRequest); if (!Irp->Cancel) { // // Set up for cancellation. // IoMarkIrpPending(Irp); tcpContext->ReferenceCount++; IF_TCPDBG(TCP_DEBUG_IRP) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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); ASSERT(item != Irp); } for (entry = tcpContext->CancelledIrpList.Flink; entry != &(tcpContext->CancelledIrpList); entry = entry->Flink) { item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); ASSERT(item != Irp); } InsertTailList(&(tcpContext->PendingIrpList), &(Irp->Tail.Overlay.ListEntry)); } #endif // DBG KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql); IF_TCPDBG(TCP_DEBUG_SEND) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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) { ASSERT(requestInformation->SendLength == 0); status = TCPDataRequestComplete(Irp, status, requestInformation->SendLength); } else { IF_TCPDBG(TCP_DEBUG_SEND) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPSendData - irp %lx send failed, status %lx\n", Irp, status)); } status = TCPDataRequestComplete(Irp, status, 0); } } else { // // Irp was cancelled previously. // KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql); // // Ensure that the cancel-routine has executed. // IoAcquireCancelSpinLock(&oldIrql); IoReleaseCancelSpinLock(oldIrql); KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql); IoSetCancelRoutine(Irp, NULL); KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql); IF_TCPDBG(TCP_DEBUG_SEND) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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 //* TCPReceiveData - Handler for TDI Receive IRP. // // Converts a TDI Receive IRP into a call to TdiReceive. // NTSTATUS // Returns: whether the request was successful. TCPReceiveData( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { TDI_STATUS status; TDI_REQUEST request; PTCP_CONTEXT tcpContext; PTDI_REQUEST_KERNEL_RECEIVE requestInformation; KIRQL oldIrql; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; ASSERT(PtrToUlong(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; KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql); IoSetCancelRoutine(Irp, TCPCancelRequest); if (!Irp->Cancel) { // // Set up for cancellation. // IoMarkIrpPending(Irp); tcpContext->ReferenceCount++; IF_TCPDBG(TCP_DEBUG_IRP) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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); ASSERT(item != Irp); } for (entry = tcpContext->CancelledIrpList.Flink; entry != &(tcpContext->CancelledIrpList); entry = entry->Flink) { item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); ASSERT(item != Irp); } InsertTailList(&(tcpContext->PendingIrpList), &(Irp->Tail.Overlay.ListEntry)); } #endif // DBG KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql); IF_TCPDBG(TCP_DEBUG_RECEIVE) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPReceiveData: pending irp %lx\n", Irp)); } return(status); } // // The status is not pending. We reset the pending bit // IrpSp->Control &= ~SL_PENDING_RETURNED; IF_TCPDBG(TCP_DEBUG_RECEIVE) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPReceiveData - irp %lx failed, status %lx\n", Irp, status)); } status = TCPDataRequestComplete(Irp, status, 0); } else { // // Irp was cancelled previously. // KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql); // // Ensure that the cancel-routine has executed. // IoAcquireCancelSpinLock(&oldIrql); IoReleaseCancelSpinLock(oldIrql); KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql); IoSetCancelRoutine(Irp, NULL); KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql); IF_TCPDBG(TCP_DEBUG_SEND) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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 //* UDPSendDatagram - // NTSTATUS // Returns: whether the request was successfully queued. UDPSendDatagram( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { 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); ASSERT(PtrToUlong(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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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); } ASSERT(status != TDI_SUCCESS); ASSERT(bytesSent == 0); KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "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 //* UDPReceiveDatagram - Handle TDI ReceiveDatagram IRP. // // Converts a TDI ReceiveDatagram IRP into a call to TdiReceiveDatagram. // NTSTATUS // Returns: whether the request was successful. UDPReceiveDatagram( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { 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); ASSERT(PtrToUlong(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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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); } ASSERT(status != TDI_SUCCESS); ASSERT(bytesReceived == 0); KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "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 //* TCPSetEventHandler - Handle TDI SetEventHandler IRP. // // Converts a TDI SetEventHandler IRP into a call to TdiSetEventHandler. // NTSTATUS // Returns: whether the request was successful. TCPSetEventHandler( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { 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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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); ASSERT(status != TDI_PENDING); return(status); } // TCPSetEventHandler //* TCPQueryInformation - Handle a TDI QueryInformation IRP. // // Converts a TDI QueryInformation IRP into a call to TdiQueryInformation. // NTSTATUS // Returns: whether request was successful. TCPQueryInformation( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { 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: ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_CONTROL_CHANNEL_FILE); request.Handle.ControlChannel = tcpContext->Handle.ControlChannel; break; case TDI_QUERY_PROVIDER_INFO: // // NetBT does this. Reinstate the ASSERT when it is fixed. // // ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) == // TDI_CONTROL_CHANNEL_FILE); request.Handle.ControlChannel = tcpContext->Handle.ControlChannel; break; case TDI_QUERY_ADDRESS_INFO: if (PtrToUlong(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: if (PtrToUlong(IrpSp->FileObject->FsContext2) != TDI_CONNECTION_FILE){ status = STATUS_INVALID_PARAMETER; } else { isConn = TRUE; request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext; } break; case TDI_QUERY_PROVIDER_STATISTICS: 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) { IrpSp->Control &= ~SL_PENDING_RETURNED; status = 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 //* TCPQueryInformationExComplete - Complete a TdiQueryInformationEx request. // NTSTATUS TCPQueryInformationExComplete( void *Context, // A pointer to the IRP for this request. unsigned int Status, // Final TDI status of the request. unsigned int ByteCount) // Bytes returned in output buffer. { 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); if (bytesCopied != CONTEXT_SIZE) { Status = STATUS_INSUFFICIENT_RESOURCES; ByteCount = 0; } } // // 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. // Status = TCPDataRequestComplete(queryContext->Irp, Status, ByteCount); ExFreePool(queryContext); return Status; } //* TCPQueryInformationEx - Handle a TDI QueryInformationEx IRP. // // Converts a TDI QueryInformationEx IRP into a call to TdiQueryInformationEx. // NTSTATUS // Returns: whether the request was successful. TCPQueryInformationEx( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { 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; BOOLEAN inputBufferValid = FALSE; PAGED_CODE(); IF_TCPDBG(TCP_DEBUG_INFO) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "QueryInformationEx starting - irp %lx fileobj %lx\n", Irp, IrpSp->FileObject)); } tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; switch (PtrToUlong(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: ASSERT(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) && InputBufferLength < MAXLONG) { inputBufferValid = TRUE; } else { inputBufferValid = FALSE; } if (inputBufferValid && (OutputBufferLength != 0)) { OutputBuffer = Irp->UserBuffer; InputBuffer = (PTCP_REQUEST_QUERY_INFORMATION_EX) IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; queryContext = ExAllocatePool(NonPagedPool, InputBufferLength + FIELD_OFFSET(TCP_QUERY_CONTEXT, QueryInformation)); if (queryContext != NULL) { status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL); if (!NT_SUCCESS(status)) { ExFreePool(queryContext); return(status); } // // Allocate Mdls to describe the input and output buffers. // Probe and lock the buffers. // try { inputMdl = IoAllocateMdl(InputBuffer, InputBufferLength, 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, InputBufferLength); } else { IF_TCPDBG(TCP_DEBUG_INFO) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "QueryInfoEx: Couldn't allocate MDL\n")); } IrpSp->Control &= ~SL_PENDING_RETURNED; status = STATUS_INSUFFICIENT_RESOURCES; } } except(EXCEPTION_EXECUTE_HANDLER) { IF_TCPDBG(TCP_DEBUG_INFO) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "QueryInfoEx: " "exception copying input param %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), InputBufferLength - FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX, Context)); if (status != TDI_PENDING) { // // Since status is not pending, clear the // control flag to keep IO verifier happy. // IrpSp->Control &= ~SL_PENDING_RETURNED; TCPQueryInformationExComplete(queryContext, status, size); return(status); } IF_TCPDBG(TCP_DEBUG_INFO) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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); } ExFreePool(queryContext); // Since status is not pending, clear the // control flag to keep IO verifier happy. IrpSp->Control &= ~SL_PENDING_RETURNED; status = TCPDataRequestComplete(Irp, status, 0); return(status); } else { IrpSp->Control &= ~SL_PENDING_RETURNED; status = STATUS_INSUFFICIENT_RESOURCES; IF_TCPDBG(TCP_DEBUG_INFO) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "QueryInfoEx: Unable to allocate query context\n")); } } } else { status = STATUS_INVALID_PARAMETER; IF_TCPDBG(TCP_DEBUG_INFO) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "QueryInfoEx: Bad buffer len, OBufLen %d, InBufLen %d\n", OutputBufferLength, InputBufferLength)); } } IF_TCPDBG(TCP_DEBUG_INFO) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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); } //* TCPSetInformationEx - Handle TDI SetInformationEx IRP. // // Converts a TDI SetInformationEx IRP into a call to TdiSetInformationEx. // // This routine does not pend. // NTSTATUS // Returns: whether the request was successful. TCPSetInformationEx( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { TDI_REQUEST request; TDI_STATUS status; PTCP_CONTEXT tcpContext; PTCP_REQUEST_SET_INFORMATION_EX setInformation; PAGED_CODE(); IF_TCPDBG(TCP_DEBUG_INFO) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "SetInformationEx - irp %lx fileobj %lx\n", Irp, IrpSp->FileObject)); } tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; setInformation = (PTCP_REQUEST_SET_INFORMATION_EX) Irp->AssociatedIrp.SystemBuffer; if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < FIELD_OFFSET(TCP_REQUEST_SET_INFORMATION_EX, Buffer) || IrpSp->Parameters.DeviceIoControl.InputBufferLength - FIELD_OFFSET(TCP_REQUEST_SET_INFORMATION_EX, Buffer) < setInformation->BufferSize) { Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return (STATUS_INVALID_PARAMETER); } switch (PtrToUlong(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: ASSERT(0); Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return(STATUS_INVALID_PARAMETER); } // // Prevent non-privleged access (i.e. IOCTL_TCP_WSH_SET_INFORMATION_EX // calls) from making changes outside of the transport layer. // Privleged calls should be using IOCTL_TCP_SET_INFORMATION_EX instead. // if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_TCP_WSH_SET_INFORMATION_EX) { uint Entity; Entity = setInformation->ID.toi_entity.tei_entity; if ((Entity != CO_TL_ENTITY) && (Entity != CL_TL_ENTITY) ) { Irp->IoStatus.Status = STATUS_ACCESS_DENIED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return (STATUS_ACCESS_DENIED); } } 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) { IrpSp->Control &= ~SL_PENDING_RETURNED; status = TCPDataRequestComplete(Irp, status, 0); return(status); } IF_TCPDBG(TCP_DEBUG_INFO) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "SetInformationEx - pending irp %lx fileobj %lx\n", Irp, IrpSp->FileObject)); } return(STATUS_PENDING); } IF_TCPDBG(TCP_DEBUG_INFO) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "SetInformationEx complete - irp %lx\n", Irp)); } // // The irp has already been completed. // return(status); } #if 0 //* TCPEnumerateConnectionList - // // Processes a request to enumerate the workstation connection list. // // This routine does not pend. // NTSTATUS // Return: whether the request was successful. TCPEnumerateConnectionList( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { 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); } #endif //* TCPCreate - // NTSTATUS // Returns: whether the request was successfully queued. TCPCreate( IN PDEVICE_OBJECT DeviceObject, // Device object for this request. IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { 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); KeInitializeSpinLock(&tcpContext->EndpointLock); ea = (PFILE_FULL_EA_INFORMATION) Irp->AssociatedIrp.SystemBuffer; // // See if this is a Control Channel open. // if (!ea) { IF_TCPDBG(TCP_DEBUG_OPEN) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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 = IP_PROTOCOL_TCP; } else if (DeviceObject == UDPDeviceObject) { protocol = IP_PROTOCOL_UDP; ASSERT(optionsPointer - optionsBuffer <= 3); if (IsDHCPZeroAddress((TRANSPORT_ADDRESS UNALIGNED *) &(targetEA->EaName[targetEA->EaNameLength + 1]))) { if (!IsAdminIoRequest(Irp, IrpSp)) { ExFreePool(tcpContext); return(STATUS_ACCESS_DENIED); } *optionsPointer = TDI_ADDRESS_OPTION_DHCP; optionsPointer++; } ASSERT(optionsPointer - optionsBuffer <= 3); } else { // // This is a raw ip open. // // // Only administrators can create raw addresses // unless this is allowed through registry. // if (!AllowUserRawAccess && !IsAdminIoRequest(Irp, IrpSp)) { ExFreePool(tcpContext); return(STATUS_ACCESS_DENIED); } protocol = RawExtractProtocolNumber( &(IrpSp->FileObject->FileName)); // // We need to protect the IPv6Send routine's packet rewriting // code (for fragmentation, header inclusion, etc) from getting // confused by malformed extension headers coming from user-land. // So we disallow these types. // if ((protocol == 0xFFFFFFFF) || IsExtensionHeader((uchar)protocol)) { ExFreePool(tcpContext); return(STATUS_INVALID_PARAMETER); } *optionsPointer = TDI_ADDRESS_OPTION_RAW; optionsPointer++; } 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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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); KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "TdiOpenAddress failed, status %lx\n", status)); if (status == STATUS_ADDRESS_ALREADY_EXISTS) { status = STATUS_SHARING_VIOLATION; } } ASSERT(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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPCreate: Opening connection for file object %lx\n", IrpSp->FileObject)); } if (targetEA->EaValueLength < sizeof(CONNECTION_CONTEXT)) { status = STATUS_EA_LIST_INCONSISTENT; } else { 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); KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "TdiOpenConnection failed, status %lx\n", status)); } } else { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR, "TCP: TdiOpenConnection issued on UDP device!\n")); status = STATUS_INVALID_DEVICE_REQUEST; ExFreePool(tcpContext); } ASSERT(status != TDI_PENDING); return(status); } KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR, "TCPCreate: didn't find any useful ea's\n")); status = STATUS_INVALID_EA_NAME; ExFreePool(tcpContext); ASSERT(status != TDI_PENDING); return(status); } // TCPCreate //* IsAdminIoRequest - // // (Lifted from AFD - AfdPerformSecurityCheck) // Compares security context of the endpoint creator to that // of the administrator and local system. // BOOLEAN // return TRUE if socket creator has admin or local system privilege IsAdminIoRequest( PIRP Irp, // Pointer to I/O request packet. PIO_STACK_LOCATION IrpSp) // Pointer to the I/O stack location to use for this request. { BOOLEAN accessGranted; PACCESS_STATE accessState; PIO_SECURITY_CONTEXT securityContext; PPRIVILEGE_SET privileges = NULL; ACCESS_MASK grantedAccess; PGENERIC_MAPPING GenericMapping; ACCESS_MASK AccessMask = GENERIC_ALL; NTSTATUS Status; // // Enable access to all the globally defined SIDs // GenericMapping = IoGetFileObjectGenericMapping(); RtlMapGenericMask(&AccessMask, GenericMapping); securityContext = IrpSp->Parameters.Create.SecurityContext; accessState = securityContext->AccessState; SeLockSubjectContext(&accessState->SubjectSecurityContext); accessGranted = SeAccessCheck(TcpAdminSecurityDescriptor, &accessState->SubjectSecurityContext, TRUE, AccessMask, 0, &privileges, IoGetFileObjectGenericMapping(), (KPROCESSOR_MODE) ((IrpSp->Flags & SL_FORCE_ACCESS_CHECK) ? UserMode : Irp->RequestorMode), &grantedAccess, &Status); if (privileges) { (VOID) SeAppendPrivileges(accessState, privileges); SeFreePrivileges(privileges); } if (accessGranted) { accessState->PreviouslyGrantedAccess |= grantedAccess; accessState->RemainingDesiredAccess &= ~(grantedAccess | MAXIMUM_ALLOWED); ASSERT(NT_SUCCESS(Status)); } else { ASSERT(!NT_SUCCESS(Status)); } SeUnlockSubjectContext(&accessState->SubjectSecurityContext); return accessGranted; } // IsAdminIoRequest //* TCPCloseObjectComplete - // // Completes a TdiCloseConnectoin or TdiCloseAddress request. // void // Returns: Nothing. TCPCloseObjectComplete( void *Context, // Pointer to the IRP for this request. unsigned int Status, // Final status of the operation. unsigned int UnUsed) // Unused parameter. { 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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPCloseObjectComplete on file object %lx\n", irpSp->FileObject)); } KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql); ASSERT(tcpContext->ReferenceCount > 0); ASSERT(tcpContext->CancelIrps); // // Remove the initial reference that was put on by TCPCreate. // ASSERT(tcpContext->ReferenceCount > 0); IF_TCPDBG(TCP_DEBUG_IRP) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPCloseObjectComplete: " "irp %lx fileobj %lx refcnt dec to %u\n", irp, irpSp, tcpContext->ReferenceCount - 1)); } if (--(tcpContext->ReferenceCount) == 0) { IF_TCPDBG(TCP_DEBUG_CANCEL) { ASSERT(IsListEmpty(&(tcpContext->CancelledIrpList))); ASSERT(IsListEmpty(&(tcpContext->PendingIrpList))); } KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql); KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE); return; } KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql); return; } // TCPCloseObjectComplete //* TCPCleanup - // // 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. // // This routine blocks, but does not pend. // NTSTATUS // Returns: whether the request was successfully queued. TCPCleanup( IN PDEVICE_OBJECT DeviceObject, // Device object for this request. IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { KIRQL oldIrql; PIRP cancelIrp = NULL; PTCP_CONTEXT tcpContext; NTSTATUS status; TDI_REQUEST request; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql); tcpContext->CancelIrps = TRUE; KeResetEvent(&(tcpContext->CleanupEvent)); KeReleaseSpinLock(&tcpContext->EndpointLock, 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 (PtrToUlong(IrpSp->FileObject->FsContext2)) { case TDI_TRANSPORT_ADDRESS_FILE: IF_TCPDBG(TCP_DEBUG_CLOSE) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPCleanup: Closing Control Channel object on" " file object %lx\n", IrpSp->FileObject)); } status = STATUS_SUCCESS; break; default: // // This should never happen. // ASSERT(FALSE); KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql); tcpContext->CancelIrps = FALSE; KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql); return(STATUS_INVALID_PARAMETER); } if (status != TDI_PENDING) { TCPCloseObjectComplete(Irp, status, 0); } IF_TCPDBG(TCP_DEBUG_CLEANUP) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPCleanup: waiting for completion of Irps on" " file object %lx\n", IrpSp->FileObject)); } status = KeWaitForSingleObject(&(tcpContext->CleanupEvent), UserRequest, KernelMode, FALSE, NULL); ASSERT(NT_SUCCESS(status)); IF_TCPDBG(TCP_DEBUG_CLEANUP) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "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 //* TCPClose - // // Dispatch routine for MJ_CLOSE IRPs. Performs final cleanup of the // open endpoint. // // This request does not pend. // NTSTATUS // Returns: whether the request was successfully queued. TCPClose( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { PTCP_CONTEXT tcpContext; tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext; #if DBG IF_TCPDBG(TCP_DEBUG_CANCEL) { KIRQL oldIrql; IoAcquireCancelSpinLock(&oldIrql); ASSERT(tcpContext->ReferenceCount == 0); ASSERT(IsListEmpty(&(tcpContext->PendingIrpList))); ASSERT(IsListEmpty(&(tcpContext->CancelledIrpList))); IoReleaseCancelSpinLock(oldIrql); } #endif // DBG IF_TCPDBG(TCP_DEBUG_CLOSE) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG, "TCPClose on file object %lx\n", IrpSp->FileObject)); } ExFreePool(tcpContext); return(STATUS_SUCCESS); } // TCPClose //* TCPDispatchDeviceControl - // NTSTATUS // Returns: whether the request was successfully queued. TCPDispatchDeviceControl( IN PIRP Irp, // I/O request packet. IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp. { 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: case IOCTL_TCP_WSH_SET_INFORMATION_EX: return(TCPSetInformationEx(Irp, IrpSp)); break; default: status = STATUS_NOT_IMPLEMENTED; break; } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return status; } // TCPDispatchDeviceControl //* TCPDispatchInternalDeviceControl - // // This is the dispatch routine for Internal Device Control IRPs. // This is the hot path for kernel-mode clients. // NTSTATUS // Returns: whether the request was successfully queued. TCPDispatchInternalDeviceControl( IN PDEVICE_OBJECT DeviceObject, // Device object for target device. IN PIRP Irp) // I/O request packet. { PIO_STACK_LOCATION irpSp; NTSTATUS status; if (DeviceObject != IPDeviceObject) { irpSp = IoGetCurrentIrpStackLocation(Irp); if (PtrToUlong(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 (PtrToUlong(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. // } ASSERT( (PtrToUlong(irpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE) || (PtrToUlong(irpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) || (PtrToUlong(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: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR, "TCP: Call to unimplemented TDI function 0x%p\n", irpSp->MinorFunction)); status = STATUS_NOT_IMPLEMENTED; break; default: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR, "TCP: call to invalid TDI function 0x%p\n", irpSp->MinorFunction)); status = STATUS_INVALID_DEVICE_REQUEST; } ASSERT(status != TDI_PENDING); Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); return status; } return(IPDispatch(DeviceObject, Irp)); } //* TCPDispatch - // // This is the generic dispatch routine for TCP/UDP/RawIP. // NTSTATUS // Returns: whether the request was successfully queued. TCPDispatch( IN PDEVICE_OBJECT DeviceObject, // Device object for target device. IN PIRP Irp) // I/O request packet. { PIO_STACK_LOCATION irpSp; NTSTATUS status; if (DeviceObject != IPDeviceObject) { irpSp = IoGetCurrentIrpStackLocation(Irp); ASSERT(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)); } if (irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER) { if (Irp->RequestorMode == KernelMode) { *(PULONG_PTR)irpSp->Parameters.DeviceIoControl.Type3InputBuffer = (ULONG_PTR)TCPSendData; status = STATUS_SUCCESS; } else { status = STATUS_ACCESS_DENIED; } break; } 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: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR, "TCPDispatch: Irp %lx unsupported major function 0x%lx\n", irpSp, irpSp->MajorFunction)); status = STATUS_INVALID_DEVICE_REQUEST; break; } ASSERT(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 // //* FindEA - // // Parses and extended attribute list for a given target attribute. // FILE_FULL_EA_INFORMATION UNALIGNED * // Returns: requested attribute or NULL. FindEA( PFILE_FULL_EA_INFORMATION StartEA, // First extended attribute in list. CHAR *TargetName, // Name of target attribute. USHORT TargetNameLength) // Length of above. { 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); } //* IsDHCPZeroAddress - // // 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). // BOOLEAN // Returns: TRUE iff first IP address found had the flag set. IsDHCPZeroAddress( TRANSPORT_ADDRESS UNALIGNED *AddrList) // TDI transport address list // passed in the create IRP. { int i; // Index variable. TA_ADDRESS *CurrentAddr; // Address we're examining and may use. // First, verify that someplace in Address is an address we can use. CurrentAddr = (TA_ADDRESS *)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 *) (CurrentAddr->Address + CurrentAddr->AddressLength); } } return FALSE; // Didn't find a match. } //* TCPGetMdlChainByteCount - // // Sums the byte counts of each MDL in a chain. // ULONG // Returns: byte count of the MDL chain. TCPGetMdlChainByteCount( PMDL Mdl) // MDL chain to sum. { ULONG count = 0; while (Mdl != NULL) { count += MmGetMdlByteCount(Mdl); Mdl = Mdl->Next; } return(count); } //* RawExtractProtocolNumber - // // Extracts the protocol number from the file object name. // ULONG // Returns: the protocol number or 0xFFFFFFFF on error. RawExtractProtocolNumber( IN PUNICODE_STRING FileName) // File name (Unicode). { 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); }