windows-nt/Source/XPSP1/NT/net/tcpip/tpipv6/tcpip6/sys/ntdisp.c
2020-09-26 16:20:57 +08:00

2996 lines
88 KiB
C

// -*- 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 <oscfg.h>
#include <ndis.h>
#include <tdikrnl.h>
#include <tdint.h>
#include <tdistat.h>
#include <tdiinfo.h>
#include <ip6imp.h>
#include <ip6def.h>
#include <ntddip6.h>
#include "queue.h"
#include "transprt.h"
#include "addr.h"
#include "tcp.h"
#include "udp.h"
#include "raw.h"
#include <ntddtcp.h>
#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);
}