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

5178 lines
141 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991-2000 Microsoft Corporation
Module Name:
ntdisp.c
Abstract:
NT specific routines for dispatching and handling IRPs.
Author:
Mike Massa (mikemas) Aug 13, 1993
Revision History:
Who When What
-------- -------- ----------------------------------------------
mikemas 08-13-93 created
Notes:
--*/
#include "precomp.h"
#include "addr.h"
#include "tcp.h"
#include "udp.h"
#include "raw.h"
#include "info.h"
#include <tcpinfo.h>
#include "tcpcfg.h"
#include "secfltr.h"
#include "tcpconn.h"
#include "mdl2ndis.h"
//
// Macros
//
//++
//
// LARGE_INTEGER
// CTEConvert100nsToMilliseconds(
// IN LARGE_INTEGER HnsTime
// );
//
// Routine Description:
//
// Converts time expressed in hundreds of nanoseconds to milliseconds.
//
// Arguments:
//
// HnsTime - Time in hundreds of nanoseconds.
//
// Return Value:
//
// Time in milliseconds.
//
//--
#define SHIFT10000 13
static LARGE_INTEGER Magic10000 =
{0xe219652c, 0xd1b71758};
#define CTEConvert100nsToMilliseconds(HnsTime) \
RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000)
#if ACC
GENERIC_MAPPING AddressGenericMapping =
{READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL};
extern PSECURITY_DESCRIPTOR TcpAdminSecurityDescriptor;
uint AllowUserRawAccess;
#endif
//
// Global variables
//
extern PDEVICE_OBJECT TCPDeviceObject, UDPDeviceObject;
extern PDEVICE_OBJECT IPDeviceObject;
#if IPMCAST
extern PDEVICE_OBJECT IpMcastDeviceObject;
#endif
extern PDEVICE_OBJECT RawIPDeviceObject;
AddrObj *FindAddrObjWithPort(ushort Port);
ReservedPortListEntry *BlockedPortList = NULL;
extern uint LogPerPartitionSize;
extern CTELock *pTWTCBTableLock;
#define GET_PARTITION(i) (i >> (ulong) LogPerPartitionSize)
extern ReservedPortListEntry *PortRangeList;
extern uint TcpHostOpts;
extern TCPInternalStats TStats;
CACHE_LINE_ULONG CancelId = { 1 };
//
// Local types
//
typedef struct {
PIRP Irp;
PMDL InputMdl;
PMDL OutputMdl;
TCP_REQUEST_QUERY_INFORMATION_EX QueryInformation;
} TCP_QUERY_CONTEXT, *PTCP_QUERY_CONTEXT;
extern POBJECT_TYPE *IoFileObjectType;
#if TRACE_EVENT
//
// CP Handler routine set/unset by WMI through IRP_MN_SET_TRACE_NOTIFY
//
PTDI_DATA_REQUEST_NOTIFY_ROUTINE TCPCPHandlerRoutine;
#endif
PIRP CanceledIrp = NULL;
//
// General external function prototypes
//
extern
NTSTATUS
IPDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
#if IPMCAST
NTSTATUS
IpMcastDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
#endif
//
// External TDI function prototypes
//
extern TDI_STATUS
TdiOpenAddress(
PTDI_REQUEST Request,
TRANSPORT_ADDRESS UNALIGNED * AddrList,
uint protocol,
void *Reuse
);
extern TDI_STATUS
TdiCloseAddress(
PTDI_REQUEST Request
);
extern TDI_STATUS
TdiOpenConnection(
PTDI_REQUEST Request,
PVOID Context
);
extern TDI_STATUS
TdiCloseConnection(
PTDI_REQUEST Request
);
extern TDI_STATUS
TdiAssociateAddress(
PTDI_REQUEST Request,
HANDLE AddrHandle
);
extern TDI_STATUS
TdiCancelDisAssociateAddress(
PTDI_REQUEST Request
);
extern TDI_STATUS
TdiDisAssociateAddress(
PTDI_REQUEST Request
);
extern TDI_STATUS
TdiConnect(
PTDI_REQUEST Request,
void *Timeout,
PTDI_CONNECTION_INFORMATION RequestAddr,
PTDI_CONNECTION_INFORMATION ReturnAddr
);
extern TDI_STATUS
UDPConnect(
PTDI_REQUEST Request,
void *Timeout,
PTDI_CONNECTION_INFORMATION RequestAddr,
PTDI_CONNECTION_INFORMATION ReturnAddr
);
extern TDI_STATUS
UDPDisconnect(
PTDI_REQUEST Request,
void *TO,
PTDI_CONNECTION_INFORMATION DiscConnInfo,
PTDI_CONNECTION_INFORMATION ReturnInfo
);
extern TDI_STATUS
TdiListen(
PTDI_REQUEST Request,
ushort Flags,
PTDI_CONNECTION_INFORMATION AcceptableAddr,
PTDI_CONNECTION_INFORMATION ConnectedAddr
);
extern TDI_STATUS
TdiAccept(
PTDI_REQUEST Request,
PTDI_CONNECTION_INFORMATION AcceptInfo,
PTDI_CONNECTION_INFORMATION ConnectedInfo
);
extern TDI_STATUS
TdiDisconnect(
PTDI_REQUEST Request,
void *TO,
ushort Flags,
PTDI_CONNECTION_INFORMATION DiscConnInfo,
PTDI_CONNECTION_INFORMATION ReturnInfo
);
extern TDI_STATUS
TdiSend(
PTDI_REQUEST Request,
ushort Flags,
uint SendLength,
PNDIS_BUFFER SendBuffer
);
extern TDI_STATUS
TdiReceive(
PTDI_REQUEST Request,
ushort * Flags,
uint * RcvLength,
PNDIS_BUFFER Buffer
);
extern TDI_STATUS
TdiSendDatagram(
PTDI_REQUEST Request,
PTDI_CONNECTION_INFORMATION ConnInfo,
uint DataSize,
uint * BytesSent,
PNDIS_BUFFER Buffer
);
VOID
TdiCancelSendDatagram(
AddrObj * SrcAO,
PVOID Context,
KIRQL inHandle
);
extern TDI_STATUS
TdiReceiveDatagram(
PTDI_REQUEST Request,
PTDI_CONNECTION_INFORMATION ConnInfo,
PTDI_CONNECTION_INFORMATION ReturnInfo,
uint RcvSize,
uint * BytesRcvd,
PNDIS_BUFFER Buffer
);
VOID
TdiCancelReceiveDatagram(
AddrObj * SrcAO,
PVOID Context,
KIRQL inHandle
);
extern TDI_STATUS
TdiSetEvent(
PVOID Handle,
int Type,
PVOID Handler,
PVOID Context
);
extern TDI_STATUS
TdiQueryInformation(
PTDI_REQUEST Request,
uint QueryType,
PNDIS_BUFFER Buffer,
uint * BytesReturned,
uint IsConn
);
extern TDI_STATUS
TdiSetInformation(
PTDI_REQUEST Request,
uint SetType,
PNDIS_BUFFER Buffer,
uint BufferSize,
uint IsConn
);
extern TDI_STATUS
TdiQueryInformationEx(
PTDI_REQUEST Request,
struct TDIObjectID *ID,
PNDIS_BUFFER Buffer,
uint * Size,
void *Context
);
extern TDI_STATUS
TdiSetInformationEx(
PTDI_REQUEST Request,
struct TDIObjectID *ID,
void *Buffer,
uint Size
);
extern TDI_STATUS
TdiAction(
PTDI_REQUEST Request,
uint ActionType,
PNDIS_BUFFER Buffer,
uint BufferSize
);
extern
NTSTATUS
TCPDispatchPnPPower(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
extern
uint
GetTCBInfo(
PTCP_FINDTCB_RESPONSE TCBInfo,
IPAddr Dest,
IPAddr Src,
ushort DestPort,
ushort SrcPort
);
//
// Other external functions
//
BOOLEAN
TCPAbortAndIndicateDisconnect(
uint ConnnectionContext, PVOID reqcontext, uint receive, KIRQL Handle
);
//
// Local pageable function prototypes
//
NTSTATUS
TCPDispatchDeviceControl(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
TCPCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
TCPAssociateAddress(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
TCPSetEventHandler(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
TCPQueryInformation(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
FILE_FULL_EA_INFORMATION UNALIGNED *
FindEA(
PFILE_FULL_EA_INFORMATION StartEA,
CHAR * TargetName,
USHORT TargetNameLength
);
BOOLEAN
IsDHCPZeroAddress(
TRANSPORT_ADDRESS UNALIGNED * AddrList
);
ULONG
RawExtractProtocolNumber(
IN PUNICODE_STRING FileName
);
NTSTATUS
TCPControlSecurityFilter(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
TCPProcessSecurityFilterRequest(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
TCPEnumerateSecurityFilter(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
NTSTATUS
TCPEnumerateConnectionList(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
//
// Local helper routine prototypes.
//
ULONG
TCPGetMdlChainByteCount(
PMDL Mdl
);
ULONG
TCPGetNdisBufferChainByteCount(
PNDIS_BUFFER pBuffer
);
#if ACC
BOOLEAN
IsAdminIoRequest(
PIRP Irp,
PIO_STACK_LOCATION IrpSp
);
#endif
//
// All of this code is pageable.
//
#if !MILLEN
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, TCPCreate)
#pragma alloc_text(PAGE, TCPSetEventHandler)
#pragma alloc_text(PAGE, FindEA)
#pragma alloc_text(PAGE, IsDHCPZeroAddress)
#pragma alloc_text(PAGE, RawExtractProtocolNumber)
#pragma alloc_text(PAGE, TCPControlSecurityFilter)
#pragma alloc_text(PAGE, TCPProcessSecurityFilterRequest)
#pragma alloc_text(PAGE, TCPEnumerateSecurityFilter)
#pragma alloc_text(PAGE, TCPEnumerateSecurityFilter)
#if ACC
#pragma alloc_text(PAGE, IsAdminIoRequest)
#endif
#endif
#endif // !MILLEN
//
// Generic Irp completion and cancellation routines.
//
NTSTATUS
TCPDataRequestComplete(
void *Context,
unsigned int Status,
unsigned int ByteCount
)
/*++
Routine Description:
Completes a UDP/TCP send/receive request.
Arguments:
Context - A pointer to the IRP for this request.
Status - The final TDI status of the request.
ByteCount - Bytes sent/received information.
Return Value:
None.
Notes:
--*/
{
KIRQL oldIrql;
PIRP irp;
PIO_STACK_LOCATION irpSp;
PTCP_CONTEXT tcpContext;
PIRP item = NULL;
CTELockHandle CancelHandle;
irp = (PIRP) Context;
irpSp = IoGetCurrentIrpStackLocation(irp);
tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext;
FreeMdlToNdisBufferChain(irp);
if (IoSetCancelRoutine(irp, NULL) == NULL) {
// Cancel routine have been invoked and can possibly
// still be running. However, it won't find this IRP
// on the list (TCB or AO). Just make sure the cancel
// routine got far enough to acquire the endpoint lock
// before proceeding to do this ourselves.
IoAcquireCancelSpinLock(&oldIrql);
IoReleaseCancelSpinLock(oldIrql);
}
CTEGetLock(&tcpContext->EndpointLock, &CancelHandle);
#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;
}
}
if ((item == NULL) && irp->Cancel) {
listHead = &(tcpContext->PendingIrpList);
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;
}
}
}
}
#endif
//note that if we are not holding cancel spinlock
//cancel can be in progress already
//it should be still okay since this irp is already dequeued
//from ao/tcb
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) {
TCPTRACE((
"TCPDataRequestComplete: Irp %lx fileobj %lx refcnt dec to %u\n",
irp,
irpSp->FileObject,
tcpContext->ReferenceCount
));
}
if (!((Status == TDI_CANCELLED) && ByteCount)) {
if (irp->Cancel || tcpContext->CancelIrps) {
IF_TCPDBG(TCP_DEBUG_IRP) {
TCPTRACE(("TCPDataRequestComplete: Irp %lx was cancelled\n", irp));
}
irp->IoStatus.Status = Status = (unsigned int)STATUS_CANCELLED;
ByteCount = 0;
}
} else {
Status = STATUS_SUCCESS;
}
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
IF_TCPDBG(TCP_DEBUG_IRP) {
TCPTRACE((
"TCPDataRequestComplete: completing irp %lx, status %lx, byte count %lx\n",
irp,
Status,
ByteCount
));
}
irp->IoStatus.Status = (NTSTATUS) Status;
irp->IoStatus.Information = ByteCount;
IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
return Status;
} // TCPDataRequestComplete
void
TCPRequestComplete(
void *Context,
unsigned int Status,
unsigned int UnUsed
)
/*++
Routine Description:
Completes a cancellable TDI request which returns no data by
calling TCPDataRequestComplete with a ByteCount of zero.
Arguments:
Context - A pointer to the IRP for this request.
Status - The final TDI status of the request.
UnUsed - An unused parameter
Return Value:
None.
Notes:
--*/
{
UNREFERENCED_PARAMETER(UnUsed);
TCPDataRequestComplete(Context, Status, 0);
} // TCPRequestComplete
void
TCPNonCancellableRequestComplete(
void *Context,
unsigned int Status,
unsigned int UnUsed
)
/*++
Routine Description:
Completes a TDI request which cannot be cancelled.
Arguments:
Context - A pointer to the IRP for this request.
Status - The final TDI status of the request.
UnUsed - An unused parameter
Return Value:
None.
Notes:
--*/
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
UNREFERENCED_PARAMETER(UnUsed);
irp = (PIRP) Context;
irpSp = IoGetCurrentIrpStackLocation(irp);
IF_TCPDBG(TCP_DEBUG_CLOSE) {
TCPTRACE((
"TCPNonCancellableRequestComplete: irp %lx status %lx\n",
irp,
Status
));
}
//
// Complete the IRP
//
irp->IoStatus.Status = (NTSTATUS) Status;
irp->IoStatus.Information = 0;
IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
return;
} // TCPNonCancellableRequestComplete
void
TCPCancelComplete(
void *Context,
unsigned int Unused1,
unsigned int Unused2
)
{
PFILE_OBJECT fileObject = (PFILE_OBJECT) Context;
PTCP_CONTEXT tcpContext = (PTCP_CONTEXT) fileObject->FsContext;
KIRQL oldIrql;
CTELockHandle CancelHandle;
UNREFERENCED_PARAMETER(Unused1);
UNREFERENCED_PARAMETER(Unused2);
CTEGetLock(&tcpContext->EndpointLock, &CancelHandle);
//
// 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 after releasing the lock
//
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE);
return;
}
IF_TCPDBG(TCP_DEBUG_IRP) {
TCPTRACE((
"TCPCancelComplete: fileobj %lx refcnt dec to %u\n",
fileObject,
tcpContext->ReferenceCount
));
}
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
return;
} // TCPCancelComplete
VOID
TCPCancelRequest(
PDEVICE_OBJECT Device,
PIRP Irp
)
/*++
Routine Description:
Cancels an outstanding Irp.
Arguments:
Device - Pointer to the device object for this request.
Irp - Pointer to I/O request packet
Return Value:
None.
--*/
{
PIO_STACK_LOCATION irpSp;
PTCP_CONTEXT tcpContext;
NTSTATUS status = STATUS_SUCCESS;
PFILE_OBJECT fileObject;
UCHAR minorFunction;
TDI_REQUEST request;
CTELockHandle CancelHandle;
KIRQL oldirql;
KIRQL UserIrql;
irpSp = IoGetCurrentIrpStackLocation(Irp);
fileObject = irpSp->FileObject;
tcpContext = (PTCP_CONTEXT) fileObject->FsContext;
minorFunction = irpSp->MinorFunction;
CTEGetLock(&tcpContext->EndpointLock, &CancelHandle);
ASSERT(Irp->Cancel);
UserIrql = Irp->CancelIrql;
IoReleaseCancelSpinLock(CancelHandle);
IF_TCPDBG(TCP_DEBUG_IRP) {
TCPTRACE((
"TCPCancelRequest: cancelling irp %lx, file object %lx\n",
Irp,
fileObject
));
}
#if DBG
IF_TCPDBG(TCP_DEBUG_CANCEL) {
//
// Remove the Irp if it is on the pending list and place it on
// the cancel 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;
}
}
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) {
TCPTRACE((
"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:
ASSERT((PtrToUlong(fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE) ||
(PtrToUlong(fileObject->FsContext2) == TDI_CONNECTION_FILE));
if (PtrToUlong(fileObject->FsContext2) == TDI_CONNECTION_FILE) {
if (TCPAbortAndIndicateDisconnect(
PtrToUlong(tcpContext->Handle.ConnectionContext), Irp, (minorFunction == TDI_RECEIVE) ? 1 : 0, UserIrql)) { //
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
}
break;
} else if (PtrToUlong(fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE) {
TdiCancelSendDatagram(tcpContext->Handle.AddressHandle, Irp, UserIrql);
break;
} else {
CTEFreeLock(&tcpContext->EndpointLock, UserIrql);
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Connect on neither address/connect file\n"));
break;
}
case TDI_SEND_DATAGRAM:
ASSERT(PtrToUlong(fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE);
TdiCancelSendDatagram(tcpContext->Handle.AddressHandle, Irp, UserIrql);
break;
case TDI_RECEIVE_DATAGRAM:
ASSERT(PtrToUlong(fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE);
TdiCancelReceiveDatagram(tcpContext->Handle.AddressHandle, Irp, UserIrql);
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.
//
CTEFreeLock(&tcpContext->EndpointLock, UserIrql);
break;
default:
//
// Initiate a disconnect to cancel the request.
//
CTEFreeLock(&tcpContext->EndpointLock, UserIrql);
request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
request.RequestNotifyObject = TCPCancelComplete;
request.RequestContext = fileObject;
status = TdiDisconnect(
&request,
NULL,
TDI_DISCONNECT_ABORT,
NULL,
NULL
);
break;
}
if (status != TDI_PENDING) {
TCPCancelComplete(fileObject, 0, 0);
}
return;
} // TCPCancelRequest
NTSTATUS
TCPPrepareIrpForCancel(
PTCP_CONTEXT TcpContext,
PIRP Irp,
PDRIVER_CANCEL CancelRoutine
)
{
KIRQL oldIrql;
CTELockHandle CancelHandle;
ULONG LocalCancelId;
//
// Set up for cancellation
//
CTEGetLock(&TcpContext->EndpointLock, &CancelHandle);
ASSERT(Irp->CancelRoutine == NULL);
if (!Irp->Cancel && !TcpContext->Cleanup) {
IoMarkIrpPending(Irp);
IoSetCancelRoutine(Irp, CancelRoutine);
TcpContext->ReferenceCount++;
IF_TCPDBG(TCP_DEBUG_IRP) {
TCPTRACE((
"TCPPrepareIrpForCancel: irp %lx fileobj %lx refcnt inc to %u\n",
Irp,
(IoGetCurrentIrpStackLocation(Irp))->FileObject,
TcpContext->ReferenceCount
));
}
#if DBG
IF_TCPDBG(TCP_DEBUG_CANCEL) {
PLIST_ENTRY entry;
PIRP item = NULL;
//
// Verify that the Irp has not already been submitted.
//
for (entry = TcpContext->PendingIrpList.Flink;
entry != &(TcpContext->PendingIrpList);
entry = entry->Flink
) {
item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
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
//Update monotonically increasing cancel ID and
//remember this for later use
while ((LocalCancelId = InterlockedIncrement(&CancelId.Value)) == 0) { }
CTEFreeLock(&TcpContext->EndpointLock, CancelHandle);
Irp->Tail.Overlay.DriverContext[1] = UlongToPtr(LocalCancelId);
Irp->Tail.Overlay.DriverContext[0] = NULL;
return (STATUS_SUCCESS);
}
//
// The IRP has already been cancelled or endpoint in cleanup phase. Complete it now.
//
IF_TCPDBG(TCP_DEBUG_IRP) {
TCPTRACE(("TCP: irp %lx already cancelled, completing.\n", Irp));
}
CTEFreeLock(&TcpContext->EndpointLock, CancelHandle);
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return (STATUS_CANCELLED);
} // TCPPrepareIrpForCancel
//
// TDI functions
//
NTSTATUS
TCPAssociateAddress(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI Associate Address IRP into a call to TdiAssociateAddress.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
Notes:
This routine does not pend.
--*/
{
NTSTATUS status;
TDI_REQUEST request;
PTCP_CONTEXT tcpContext;
PTDI_REQUEST_KERNEL_ASSOCIATE associateInformation;
PFILE_OBJECT fileObject;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPAssociateAddress(%x, %x)\n"),
Irp, IrpSp));
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)
) {
BOOLEAN cleanup;
CTELockHandle CancelHandle;
tcpContext = (PTCP_CONTEXT) fileObject->FsContext;
//if cleanup in progress, do not allow this operation.
CTEGetLock(&tcpContext->EndpointLock, &CancelHandle);
cleanup = tcpContext->Cleanup;
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
status = STATUS_INVALID_HANDLE;
if (!cleanup)
status = TdiAssociateAddress(
&request,
tcpContext->Handle.AddressHandle
);
ASSERT(status != STATUS_PENDING);
ObDereferenceObject(fileObject);
IF_TCPDBG(TCP_DEBUG_ASSOCIATE) {
TCPTRACE((
"TCPAssociateAddress complete on file object %lx\n",
IrpSp->FileObject
));
}
} else {
ObDereferenceObject(fileObject);
status = STATUS_INVALID_HANDLE;
IF_TCPDBG(TCP_DEBUG_ASSOCIATE) {
TCPTRACE((
"TCPAssociateAddress: ObReference failed on object %lx, status %lx\n",
associateInformation->AddressHandle,
status
));
}
}
} else {
DEBUGMSG(DBG_ERROR && DBG_TDI,
(DTEXT("TdiAssociateAddress: ObReference failure %x on handle %x\n"),
status, associateInformation->AddressHandle));
}
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPAssociateAddress [%x]\n"), status));
return (status);
}
NTSTATUS
TCPDisassociateAddress(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI Associate Address IRP into a call to TdiAssociateAddress.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
--*/
{
NTSTATUS status;
TDI_REQUEST request;
PTCP_CONTEXT tcpContext;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPDisassociateAddress \n")));
IF_TCPDBG(TCP_DEBUG_ASSOCIATE) {
TCPTRACE(("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);
}
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPDisassociateAddress \n")));
return (status);
} // TCPDisassociateAddress
NTSTATUS
TCPConnect(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI Connect IRP into a call to TdiConnect.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
NTSTATUS status;
PTCP_CONTEXT tcpContext;
TDI_REQUEST request;
PTDI_CONNECTION_INFORMATION requestInformation, returnInformation;
PTDI_REQUEST_KERNEL_CONNECT connectRequest;
LARGE_INTEGER millisecondTimeout;
PLARGE_INTEGER requestTimeout;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPConnect \n")));
IF_TCPDBG(TCP_DEBUG_CONNECT) {
TCPTRACE((
"TCPConnect irp %lx, file object %lx\n",
Irp,
IrpSp->FileObject
));
}
ASSERT((PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE) ||
(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 = CTEConvert100nsToMilliseconds(
millisecondTimeout
);
} else {
millisecondTimeout.LowPart = 0;
millisecondTimeout.HighPart = 0;
}
ASSERT(millisecondTimeout.HighPart == 0);
status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
if (NT_SUCCESS(status)) {
if (PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) {
status = TdiConnect(
&request,
((millisecondTimeout.LowPart != 0) ?
&(millisecondTimeout.LowPart) : NULL),
requestInformation,
returnInformation
);
} else if (PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE) {
status = UDPConnect(
&request,
((millisecondTimeout.LowPart != 0) ?
&(millisecondTimeout.LowPart) : NULL),
requestInformation,
returnInformation
);
} else {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Connect on neither address/connect file\n"));
ASSERT(FALSE);
}
if (status != STATUS_PENDING) {
TCPRequestComplete(Irp, status, 0);
}
//
// return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
//
return (STATUS_PENDING);
}
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPConnect \n")));
return (status);
} // TCPConnect
NTSTATUS
TCPDisconnect(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI Disconnect IRP into a call to TdiDisconnect.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
Notes:
Abortive disconnects may pend, but cannot be cancelled.
--*/
{
NTSTATUS status;
PTCP_CONTEXT tcpContext;
TDI_REQUEST request;
PTDI_CONNECTION_INFORMATION requestInformation, returnInformation;
PTDI_REQUEST_KERNEL_DISCONNECT disconnectRequest;
LARGE_INTEGER millisecondTimeout;
PLARGE_INTEGER requestTimeout;
BOOLEAN abortive = FALSE;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPDisconnect \n")));
ASSERT((PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE) ||
(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)) {
// This is infinite time timeout period
// Just use 0 timeout value
millisecondTimeout.LowPart = 0;
millisecondTimeout.HighPart = 0;
} else {
//
// NT relative timeouts are negative. Negate first to get a
// positive value to pass to the transport.
//
millisecondTimeout.QuadPart = -((*requestTimeout).QuadPart);
millisecondTimeout = CTEConvert100nsToMilliseconds(
millisecondTimeout
);
}
} else {
millisecondTimeout.LowPart = 0;
millisecondTimeout.HighPart = 0;
}
if (disconnectRequest->RequestFlags & TDI_DISCONNECT_ABORT) {
//
// Abortive disconnects cannot be cancelled and must use
// a specific completion routine.
//
abortive = TRUE;
IoMarkIrpPending(Irp);
request.RequestNotifyObject = TCPNonCancellableRequestComplete;
status = STATUS_SUCCESS;
} else {
//
// Non-abortive disconnects can use the generic cancellation and
// completion routines.
//
status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
request.RequestNotifyObject = TCPRequestComplete;
}
IF_TCPDBG(TCP_DEBUG_CLOSE) {
TCPTRACE((
"TCPDisconnect irp %lx, flags %lx, fileobj %lx, abortive = %d\n",
Irp,
disconnectRequest->RequestFlags,
IrpSp->FileObject,
abortive
));
}
if (NT_SUCCESS(status)) {
if (PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) {
status = TdiDisconnect(
&request,
((millisecondTimeout.LowPart != 0) ?
&(millisecondTimeout.LowPart) : NULL),
(ushort) disconnectRequest->RequestFlags,
requestInformation,
returnInformation
);
} else if (PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"DisConnect on address file Irp %x \n", Irp));
status = UDPDisconnect(
&request,
((millisecondTimeout.LowPart != 0) ?
&(millisecondTimeout.LowPart) : NULL),
requestInformation,
returnInformation
);
} else {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"DisConnect on neither address/connect file\n"));
ASSERT(FALSE);
}
if (status != STATUS_PENDING) {
if (abortive) {
TCPNonCancellableRequestComplete(Irp, status, 0);
} else {
TCPRequestComplete(Irp, status, 0);
}
} else {
IF_TCPDBG(TCP_DEBUG_CLOSE) {
TCPTRACE(("TCPDisconnect pending irp %lx\n", Irp));
}
}
//
// return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
//
return (STATUS_PENDING);
}
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPDisconnect \n")));
return (status);
} // TCPDisconnect
NTSTATUS
TCPListen(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI Listen IRP into a call to TdiListen.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
--*/
{
NTSTATUS status;
PTCP_CONTEXT tcpContext;
TDI_REQUEST request;
PTDI_CONNECTION_INFORMATION requestInformation, returnInformation;
PTDI_REQUEST_KERNEL_LISTEN listenRequest;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPListen \n")));
IF_TCPDBG(TCP_DEBUG_CONNECT) {
TCPTRACE((
"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);
}
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPListen \n")));
return (status);
} // TCPListen
NTSTATUS
TCPAccept(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI Accept IRP into a call to TdiAccept.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
NTSTATUS status;
PTCP_CONTEXT tcpContext;
TDI_REQUEST request;
PTDI_CONNECTION_INFORMATION requestInformation, returnInformation;
PTDI_REQUEST_KERNEL_ACCEPT acceptRequest;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPAccept \n")));
IF_TCPDBG(TCP_DEBUG_CONNECT) {
TCPTRACE((
"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);
}
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPAccept \n")));
return (status);
} // TCPAccept
NTSTATUS
TCPSendData(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI Send IRP into a call to TdiSend.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
--*/
{
TDI_STATUS status;
TDI_REQUEST request;
PTCP_CONTEXT tcpContext;
PTDI_REQUEST_KERNEL_SEND requestInformation;
KIRQL oldIrql;
CTELockHandle CancelHandle;
PNDIS_BUFFER pNdisBuffer;
ULONG LocalCancelId;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPSendData \n")));
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;
ASSERT(Irp->CancelRoutine == NULL);
CTEGetLock(&tcpContext->EndpointLock, &CancelHandle);
IoSetCancelRoutine(Irp, TCPCancelRequest);
if (!Irp->Cancel) {
//
// Set up for cancellation
//
IoMarkIrpPending(Irp);
tcpContext->ReferenceCount++;
IF_TCPDBG(TCP_DEBUG_IRP) {
TCPTRACE((
"TCPSendData: irp %lx fileobj %lx refcnt inc to %u\n",
Irp,
IrpSp,
tcpContext->ReferenceCount
));
}
#if DBG
IF_TCPDBG(TCP_DEBUG_CANCEL) {
PLIST_ENTRY entry;
PIRP item = NULL;
//
// Verify that the Irp has not already been submitted.
//
for (entry = tcpContext->PendingIrpList.Flink;
entry != &(tcpContext->PendingIrpList);
entry = entry->Flink
) {
item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
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
//Update monotonically increasing cancel ID and
//remember this for later use
while ((LocalCancelId = InterlockedIncrement(&CancelId.Value)) == 0) { }
Irp->Tail.Overlay.DriverContext[1] = UlongToPtr(LocalCancelId);
Irp->Tail.Overlay.DriverContext[0] = NULL;
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
IF_TCPDBG(TCP_DEBUG_SEND) {
TCPTRACE((
"TCPSendData irp %lx sending %d bytes, flags %lx, fileobj %lx\n",
Irp,
requestInformation->SendLength,
requestInformation->SendFlags,
IrpSp->FileObject
));
}
status = ConvertMdlToNdisBuffer(Irp, Irp->MdlAddress, &pNdisBuffer);
if (status == TDI_SUCCESS) {
status = TdiSend(
&request,
(ushort) requestInformation->SendFlags,
requestInformation->SendLength,
pNdisBuffer
);
}
if (status == TDI_PENDING) {
IF_TCPDBG(TCP_DEBUG_SEND) {
TCPTRACE(("TCPSendData pending irp %lx\n", Irp));
}
return (status);
}
//
// The status is not pending. We reset the pending bit
//
IrpSp->Control &= ~SL_PENDING_RETURNED;
if (status == TDI_SUCCESS) {
ASSERT(requestInformation->SendLength == 0);
status = TCPDataRequestComplete(Irp, status, requestInformation->SendLength);
} else {
IF_TCPDBG(TCP_DEBUG_SEND) {
TCPTRACE((
"TCPSendData - irp %lx send failed, status %lx\n",
Irp,
status
));
}
status = TCPDataRequestComplete(Irp, status, 0);
}
} else {
//
// Irp was cancelled previously.
//
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
//Let cancel routine run
IoAcquireCancelSpinLock(&oldIrql);
IoReleaseCancelSpinLock(oldIrql);
CTEGetLock(&tcpContext->EndpointLock, &CancelHandle);
IoSetCancelRoutine(Irp, NULL);
IF_TCPDBG(TCP_DEBUG_SEND) {
TCPTRACE((
"TCPSendData: Irp %lx on fileobj %lx was cancelled\n",
Irp,
IrpSp->FileObject
));
}
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
status = STATUS_CANCELLED;
}
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPSendData \n")));
return (status);
} // TCPSendData
NTSTATUS
TCPReceiveData(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI Receive IRP into a call to TdiReceive.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
--*/
{
TDI_STATUS status;
TDI_REQUEST request;
PTCP_CONTEXT tcpContext;
PTDI_REQUEST_KERNEL_RECEIVE requestInformation;
KIRQL oldIrql;
CTELockHandle CancelHandle;
PNDIS_BUFFER pNdisBuffer;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPReceiveData \n")));
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;
ASSERT(Irp->CancelRoutine == NULL);
CTEGetLock(&tcpContext->EndpointLock, &CancelHandle);
IoSetCancelRoutine(Irp, TCPCancelRequest);
if (!Irp->Cancel) {
//
// Set up for cancellation
//
IoMarkIrpPending(Irp);
tcpContext->ReferenceCount++;
IF_TCPDBG(TCP_DEBUG_IRP) {
TCPTRACE((
"TCPReceiveData: irp %lx fileobj %lx refcnt inc to %u\n",
Irp,
IrpSp->FileObject,
tcpContext->ReferenceCount
));
}
#if DBG
IF_TCPDBG(TCP_DEBUG_CANCEL) {
PLIST_ENTRY entry;
PIRP item = NULL;
//
// Verify that the Irp has not already been submitted.
//
for (entry = tcpContext->PendingIrpList.Flink;
entry != &(tcpContext->PendingIrpList);
entry = entry->Flink
) {
item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
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
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
IF_TCPDBG(TCP_DEBUG_RECEIVE) {
TCPTRACE((
"TCPReceiveData irp %lx receiving %d bytes flags %lx filobj %lx\n",
Irp,
requestInformation->ReceiveLength,
requestInformation->ReceiveFlags,
IrpSp->FileObject
));
}
status = ConvertMdlToNdisBuffer(Irp, Irp->MdlAddress, &pNdisBuffer);
if (status == TDI_SUCCESS) {
status = TdiReceive(
&request,
(ushort *) & (requestInformation->ReceiveFlags),
&(requestInformation->ReceiveLength),
pNdisBuffer
);
}
if (status == TDI_PENDING) {
IF_TCPDBG(TCP_DEBUG_RECEIVE) {
TCPTRACE(("TCPReceiveData: pending irp %lx\n", Irp));
}
return (status);
}
//
// The status is not pending. We reset the pending bit
//
IrpSp->Control &= ~SL_PENDING_RETURNED;
// ASSERT(status != TDI_SUCCESS);
IF_TCPDBG(TCP_DEBUG_RECEIVE) {
TCPTRACE((
"TCPReceiveData - irp %lx failed, status %lx\n",
Irp,
status
));
}
status = TCPDataRequestComplete(Irp, status, 0);
} else {
//
// Irp was cancelled previously.
//
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
//Synchoronize with cancel routine by using both iocancelspinlocks
//and endpoint locks
IoAcquireCancelSpinLock(&oldIrql);
IoReleaseCancelSpinLock(oldIrql);
CTEGetLock(&tcpContext->EndpointLock, &CancelHandle);
IoSetCancelRoutine(Irp, NULL);
IF_TCPDBG(TCP_DEBUG_SEND) {
TCPTRACE((
"TCPReceiveData: Irp %lx on fileobj %lx was cancelled\n",
Irp,
IrpSp->FileObject
));
}
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
status = STATUS_CANCELLED;
}
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPReceiveData \n")));
return status;
} // TCPReceiveData
NTSTATUS
UDPSendData(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
TDI_STATUS status;
TDI_REQUEST request;
PTCP_CONTEXT tcpContext;
PTDI_REQUEST_KERNEL_SEND datagramInformation;
ULONG bytesSent = 0;
PNDIS_BUFFER pNdisBuffer;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+UDPSendData \n")));
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
datagramInformation = (PTDI_REQUEST_KERNEL_SEND) & (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) {
TCPTRACE((
"UDPSendData irp %lx sending %d bytes\n",
Irp,
datagramInformation->SendLength
));
}
status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
if (NT_SUCCESS(status)) {
AddrObj *AO = request.Handle.AddressHandle;
if (AO && (AO->ao_flags & AO_CONNUDP_FLAG)) {
status = ConvertMdlToNdisBuffer(Irp, Irp->MdlAddress, &pNdisBuffer);
if (status == TDI_SUCCESS) {
status = TdiSendDatagram(
&request,
NULL,
datagramInformation->SendLength,
&bytesSent,
pNdisBuffer
);
}
if (status == TDI_PENDING) {
return (status);
}
} else {
status = TDI_ADDR_INVALID;
}
ASSERT(status != TDI_SUCCESS);
ASSERT(bytesSent == 0);
TCPTRACE((
"UDPSendData - 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;
}
NTSTATUS
UDPSendDatagram(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
TDI_STATUS status;
TDI_REQUEST request;
PTCP_CONTEXT tcpContext;
PTDI_REQUEST_KERNEL_SENDDG datagramInformation;
ULONG bytesSent = 0;
PNDIS_BUFFER pNdisBuffer;
DEBUGMSG(DBG_TRACE && DBG_TDI && DBG_TX, (DTEXT("+UDPSendDatagram\n")));
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) {
TCPTRACE((
"UDPSendDatagram irp %lx sending %d bytes\n",
Irp,
datagramInformation->SendLength
));
}
status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
if (NT_SUCCESS(status)) {
status = ConvertMdlToNdisBuffer(Irp, Irp->MdlAddress, &pNdisBuffer);
if (status == TDI_SUCCESS) {
status = TdiSendDatagram(
&request,
datagramInformation->SendDatagramInformation,
datagramInformation->SendLength,
&bytesSent,
pNdisBuffer
);
}
if (status == TDI_PENDING) {
return (status);
}
ASSERT(status != TDI_SUCCESS);
ASSERT(bytesSent == 0);
TCPTRACE((
"UDPSendDatagram - irp %lx send failed, status %lx\n",
Irp,
status
));
TCPDataRequestComplete(Irp, status, bytesSent);
//
// return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
//
return (TDI_PENDING);
}
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-UDPSendDatagram \n")));
return status;
} // UDPSendDatagram
NTSTATUS
UDPReceiveDatagram(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI ReceiveDatagram IRP into a call to TdiReceiveDatagram.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
--*/
{
TDI_STATUS status;
TDI_REQUEST request;
PTCP_CONTEXT tcpContext;
PTDI_REQUEST_KERNEL_RECEIVEDG datagramInformation;
uint bytesReceived = 0;
PNDIS_BUFFER pNdisBuffer;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+UDPReceiveDatagram \n")));
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) {
TCPTRACE((
"UDPReceiveDatagram: irp %lx receiveing %d bytes\n",
Irp,
datagramInformation->ReceiveLength
));
}
status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
if (NT_SUCCESS(status)) {
status = ConvertMdlToNdisBuffer(Irp, Irp->MdlAddress, &pNdisBuffer);
if (status == TDI_SUCCESS) {
status = TdiReceiveDatagram(
&request,
datagramInformation->ReceiveDatagramInformation,
datagramInformation->ReturnDatagramInformation,
datagramInformation->ReceiveLength,
&bytesReceived,
pNdisBuffer
);
}
if (status == TDI_PENDING) {
return (status);
}
ASSERT(status != TDI_SUCCESS);
ASSERT(bytesReceived == 0);
TCPTRACE((
"UDPReceiveDatagram: irp %lx send failed, status %lx\n",
Irp,
status
));
TCPDataRequestComplete(Irp, status, bytesReceived);
//
// return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
//
return (TDI_PENDING);
}
return status;
} // UDPReceiveDatagram
NTSTATUS
TCPSetEventHandler(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI SetEventHandler IRP into a call to TdiSetEventHandler.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
Notes:
This routine does not pend.
--*/
{
NTSTATUS status;
PTDI_REQUEST_KERNEL_SET_EVENT event;
PTCP_CONTEXT tcpContext;
PAGED_CODE();
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPSetEventHandler \n")));
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
event = (PTDI_REQUEST_KERNEL_SET_EVENT) & (IrpSp->Parameters);
IF_TCPDBG(TCP_DEBUG_EVENT_HANDLER) {
TCPTRACE((
"TCPSetEventHandler: irp %lx event %lx handler %lx context %lx\n",
Irp,
event->EventType,
event->EventHandler,
event->EventContext
));
}
status = TdiSetEvent(
tcpContext->Handle.AddressHandle,
event->EventType,
event->EventHandler,
event->EventContext
);
ASSERT(status != TDI_PENDING);
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPSetEventHandler \n")));
return (status);
} // TCPSetEventHandler
NTSTATUS
TCPQueryInformation(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI QueryInformation IRP into a call to TdiQueryInformation.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
Notes:
--*/
{
TDI_REQUEST request;
TDI_STATUS status = STATUS_SUCCESS;
PTCP_CONTEXT tcpContext;
PTDI_REQUEST_KERNEL_QUERY_INFORMATION queryInformation;
uint isConn = FALSE;
uint dataSize = 0;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPQueryInformation \n")));
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( ((int) 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:
//ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) ==
// TDI_CONTROL_CHANNEL_FILE
// );
request.Handle.ControlChannel = tcpContext->Handle.ControlChannel;
break;
default:
status = STATUS_NOT_IMPLEMENTED;
break;
}
if (NT_SUCCESS(status)) {
PNDIS_BUFFER pNdisBuffer;
//
// 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 = ConvertMdlToNdisBuffer(Irp, Irp->MdlAddress, &pNdisBuffer);
if (status == TDI_SUCCESS) {
status = TdiQueryInformation(
&request,
queryInformation->QueryType,
pNdisBuffer,
&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);
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPQueryInformation \n")));
return (status);
} // TCPQueryInformation
NTSTATUS
TCPQueryInformationExComplete(
void *Context,
unsigned int Status,
unsigned int ByteCount
)
/*++
Routine Description:
Completes a TdiQueryInformationEx request.
Arguments:
Context - A pointer to the IRP for this request.
Status - The final TDI status of the request.
ByteCount - Bytes returned in output buffer.
Return Value:
None.
Notes:
--*/
{
PTCP_QUERY_CONTEXT queryContext = (PTCP_QUERY_CONTEXT) Context;
ULONG bytesCopied;
DEBUGMSG(DBG_TRACE && DBG_TDI,
(DTEXT("+TCPQueryInformationExComplete(%x, %x, %d)\n"),
Context, Status, ByteCount));
if (NT_SUCCESS(Status)) {
//
// Copy the returned context to the input buffer.
//
#if defined(_WIN64)
if (IoIs32bitProcess(queryContext->Irp)) {
TdiCopyBufferToMdl(
&queryContext->QueryInformation.Context,
0,
CONTEXT_SIZE,
queryContext->InputMdl,
FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX32, Context),
&bytesCopied
);
} else {
#endif // _WIN64
TdiCopyBufferToMdl(
&(queryContext->QueryInformation.Context),
0,
CONTEXT_SIZE,
queryContext->InputMdl,
FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX, Context),
&bytesCopied
);
#if defined(_WIN64)
}
#endif // _WIN64
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);
CTEFreeMem(queryContext);
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPQueryInformationExComplete \n")));
return Status;
}
NTSTATUS
TCPQueryInformationEx(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI QueryInformationEx IRP into a call to TdiQueryInformationEx.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
Notes:
--*/
{
TDI_REQUEST request;
TDI_STATUS status = STATUS_SUCCESS;
PTCP_CONTEXT tcpContext;
uint size;
PTCP_REQUEST_QUERY_INFORMATION_EX InputBuffer;
PVOID OutputBuffer;
PMDL inputMdl = NULL;
PMDL outputMdl = NULL;
ULONG InputBufferLength, OutputBufferLength;
PTCP_QUERY_CONTEXT queryContext;
BOOLEAN inputLocked = FALSE;
BOOLEAN outputLocked = FALSE;
#if defined(_WIN64)
BOOLEAN is32bitProcess = FALSE;
#endif // _WIN64
BOOLEAN inputBufferValid = FALSE;
ULONG AllocSize;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPQueryInformationEx \n")));
IF_TCPDBG(TCP_DEBUG_INFO) {
TCPTRACE((
"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 defined(_WIN64)
if (is32bitProcess = IoIs32bitProcess(Irp)) {
if (InputBufferLength >= sizeof(TCP_REQUEST_QUERY_INFORMATION_EX32) &&
InputBufferLength < MAXLONG) {
inputBufferValid = TRUE;
AllocSize =
FIELD_OFFSET(TCP_QUERY_CONTEXT, QueryInformation.Context) +
InputBufferLength -
FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX32, Context);
} else {
inputBufferValid = FALSE;
}
} else {
#endif // _WIN64
if (InputBufferLength >= sizeof(TCP_REQUEST_QUERY_INFORMATION_EX) &&
InputBufferLength < MAXLONG) {
inputBufferValid = TRUE;
AllocSize =
FIELD_OFFSET(TCP_QUERY_CONTEXT, QueryInformation) +
InputBufferLength;
} else {
inputBufferValid = FALSE;
}
#if defined(_WIN64)
}
#endif // _WIN64
if (inputBufferValid && OutputBufferLength != 0) {
OutputBuffer = Irp->UserBuffer;
InputBuffer =
(PTCP_REQUEST_QUERY_INFORMATION_EX)
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
queryContext = CTEAllocMem(AllocSize);
if (queryContext != NULL) {
status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL);
if (!NT_SUCCESS(status)) {
CTEFreeMem(queryContext);
return (status);
}
//
// Allocate Mdls to describe the input and output buffers.
// Probe and lock the buffers.
//
try {
inputMdl = IoAllocateMdl(
InputBuffer,
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.
//
#if defined(_WIN64)
if (is32bitProcess) {
RtlCopyMemory(
&queryContext->QueryInformation,
InputBuffer,
FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX32, Context)
);
RtlCopyMemory(
&queryContext->QueryInformation.Context,
(PUCHAR)InputBuffer +
FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX32, Context),
InputBufferLength -
FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX32, Context)
);
} else {
#endif // _WIN64
RtlCopyMemory(
&queryContext->QueryInformation,
InputBuffer,
InputBufferLength
);
#if defined(_WIN64)
}
#endif // _WIN64
} else {
IF_TCPDBG(TCP_DEBUG_INFO) {
TCPTRACE(("QueryInfoEx: Couldn't allocate MDL\n"));
}
IrpSp->Control &= ~SL_PENDING_RETURNED;
status = STATUS_INSUFFICIENT_RESOURCES;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
IF_TCPDBG(TCP_DEBUG_INFO) {
TCPTRACE((
"QueryInfoEx: exception copying input params %lx\n",
GetExceptionCode()
));
}
status = GetExceptionCode();
}
if (NT_SUCCESS(status)) {
PNDIS_BUFFER OutputNdisBuf;
//
// 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 = ConvertMdlToNdisBuffer(Irp, outputMdl, &OutputNdisBuf);
if (status == TDI_SUCCESS) {
status = TdiQueryInformationEx(
&request,
&(queryContext->QueryInformation.ID),
OutputNdisBuf,
&size,
&(queryContext->QueryInformation.Context)
);
}
if (status != TDI_PENDING) {
// Since status is not pending, clear the
// control flag to keep IO verifier happy.
IrpSp->Control &= ~SL_PENDING_RETURNED;
status = TCPQueryInformationExComplete(
queryContext,
status,
size
);
return (status);
}
IF_TCPDBG(TCP_DEBUG_INFO) {
TCPTRACE((
"QueryInformationEx - pending irp %lx fileobj %lx\n",
Irp,
IrpSp->FileObject
));
}
return (STATUS_PENDING);
}
//
// If we get here, something failed. Clean up.
//
if (inputMdl != NULL) {
if (inputLocked) {
MmUnlockPages(inputMdl);
}
IoFreeMdl(inputMdl);
}
if (outputMdl != NULL) {
if (outputLocked) {
MmUnlockPages(outputMdl);
}
IoFreeMdl(outputMdl);
}
CTEFreeMem(queryContext);
// Since status is not pending, clear the
// control flag to keep IO verifier happy.
IrpSp->Control &= ~SL_PENDING_RETURNED;
// This Irp may be in the process of cancellation
// get the real status used in irp completion
status = TCPDataRequestComplete(Irp, status, 0);
return (status);
} else {
IrpSp->Control &= ~SL_PENDING_RETURNED;
status = STATUS_INSUFFICIENT_RESOURCES;
IF_TCPDBG(TCP_DEBUG_INFO) {
TCPTRACE(("QueryInfoEx: Unable to allocate query context\n"));
}
}
} else {
status = STATUS_INVALID_PARAMETER;
IF_TCPDBG(TCP_DEBUG_INFO) {
TCPTRACE((
"QueryInfoEx: Bad buffer len, OBufLen %d, InBufLen %d\n",
OutputBufferLength, InputBufferLength
));
}
}
IF_TCPDBG(TCP_DEBUG_INFO) {
TCPTRACE((
"QueryInformationEx complete - irp %lx, status %lx\n",
Irp,
status
));
}
Irp->IoStatus.Status = (NTSTATUS) status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPQueryInformationEx \n")));
return (status);
}
NTSTATUS
TCPSetInformationEx(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Converts a TDI SetInformationEx IRP into a call to TdiSetInformationEx.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
Notes:
This routine does not pend.
--*/
{
TDI_REQUEST request;
TDI_STATUS status;
PTCP_CONTEXT tcpContext;
PTCP_REQUEST_SET_INFORMATION_EX setInformation;
PAGED_CODE();
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPSetInformationEx \n")));
IF_TCPDBG(TCP_DEBUG_INFO) {
TCPTRACE((
"SetInformationEx - irp %lx fileobj %lx\n",
Irp,
IrpSp->FileObject
));
}
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
setInformation = (PTCP_REQUEST_SET_INFORMATION_EX)
Irp->AssociatedIrp.SystemBuffer;
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);
}
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) {
DEBUGMSG(status != TDI_SUCCESS && DBG_ERROR && DBG_SETINFO,
(DTEXT("TCPSetInformationEx: TdiSetInformationEx failure %x\n"),
status));
IrpSp->Control &= ~SL_PENDING_RETURNED;
status = TCPDataRequestComplete(
Irp,
status,
0
);
return (status);
}
IF_TCPDBG(TCP_DEBUG_INFO) {
TCPTRACE((
"SetInformationEx - pending irp %lx fileobj %lx\n",
Irp,
IrpSp->FileObject
));
}
return (STATUS_PENDING);
}
IF_TCPDBG(TCP_DEBUG_INFO) {
TCPTRACE((
"SetInformationEx complete - irp %lx\n",
Irp
));
}
//
// The irp has already been completed.
//
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPSetInformationEx \n")));
return (status);
}
NTSTATUS
TCPControlSecurityFilter(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Processes a request to query or set the status of security filtering.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
Notes:
This routine does not pend.
--*/
{
PTCP_SECURITY_FILTER_STATUS request;
ULONG requestLength;
ULONG requestCode;
TDI_STATUS status = STATUS_SUCCESS;
PAGED_CODE();
Irp->IoStatus.Information = 0;
request = (PTCP_SECURITY_FILTER_STATUS) Irp->AssociatedIrp.SystemBuffer;
requestCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
if (requestCode == IOCTL_TCP_QUERY_SECURITY_FILTER_STATUS) {
requestLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
if (requestLength < sizeof(TCP_SECURITY_FILTER_STATUS)) {
status = STATUS_INVALID_PARAMETER;
} else {
request->FilteringEnabled = IsSecurityFilteringEnabled();
Irp->IoStatus.Information = sizeof(TCP_SECURITY_FILTER_STATUS);
}
} else {
ASSERT(requestCode == IOCTL_TCP_SET_SECURITY_FILTER_STATUS);
requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
if (requestLength < sizeof(TCP_SECURITY_FILTER_STATUS)) {
status = STATUS_INVALID_PARAMETER;
} else {
ControlSecurityFiltering(request->FilteringEnabled);
}
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return (status);
}
NTSTATUS
TCPProcessSecurityFilterRequest(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Processes a request to add or delete a transport security filter.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
Notes:
This routine does not pend.
--*/
{
TCPSecurityFilterEntry *request;
ULONG requestLength;
ULONG i;
ULONG requestCode;
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
Irp->IoStatus.Information = 0;
request = (TCPSecurityFilterEntry *) Irp->AssociatedIrp.SystemBuffer;
requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
requestCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
if (requestLength < sizeof(TCPSecurityFilterEntry)) {
status = STATUS_INVALID_PARAMETER;
} else {
if (requestCode == IOCTL_TCP_ADD_SECURITY_FILTER) {
status = AddValueSecurityFilter(
net_long(request->tsf_address),
request->tsf_protocol,
request->tsf_value
);
} else {
ASSERT(requestCode == IOCTL_TCP_DELETE_SECURITY_FILTER);
status = DeleteValueSecurityFilter(
net_long(request->tsf_address),
request->tsf_protocol,
request->tsf_value
);
}
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return (status);
}
NTSTATUS
TCPEnumerateSecurityFilter(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Processes a request to enumerate a transport security filter list.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
Notes:
This routine does not pend.
--*/
{
TCPSecurityFilterEntry *request;
TCPSecurityFilterEnum *response;
ULONG requestLength, responseLength;
NTSTATUS status;
PAGED_CODE();
request = (TCPSecurityFilterEntry *) Irp->AssociatedIrp.SystemBuffer;
response = (TCPSecurityFilterEnum *) request;
requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
responseLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
if (requestLength < sizeof(TCPSecurityFilterEntry)) {
status = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Information = 0;
} else if (responseLength < sizeof(TCPSecurityFilterEnum)) {
status = STATUS_BUFFER_TOO_SMALL;
Irp->IoStatus.Information = 0;
} else {
EnumerateSecurityFilters(
net_long(request->tsf_address),
request->tsf_protocol,
request->tsf_value,
(uchar *) (response + 1),
responseLength - sizeof(TCPSecurityFilterEnum),
&(response->tfe_entries_returned),
&(response->tfe_entries_available)
);
status = TDI_SUCCESS;
Irp->IoStatus.Information =
sizeof(TCPSecurityFilterEnum) +
(response->tfe_entries_returned * sizeof(TCPSecurityFilterEntry));
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return (status);
}
NTSTATUS
TCPReservePorts(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
{
ULONG requestLength;
ULONG requestCode;
TDI_STATUS status = STATUS_SUCCESS;
PTCP_RESERVE_PORT_RANGE request;
CTELockHandle Handle;
//PAGED_CODE();
Irp->IoStatus.Information = 0;
request = (PTCP_RESERVE_PORT_RANGE) Irp->AssociatedIrp.SystemBuffer;
requestCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
if (requestLength < sizeof(TCP_RESERVE_PORT_RANGE)) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return (STATUS_INVALID_PARAMETER);
}
if ((request->UpperRange >= request->LowerRange) &&
(request->LowerRange >= MIN_USER_PORT) &&
(request->UpperRange <= MaxUserPort)) {
if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_TCP_RESERVE_PORT_RANGE) {
ReservedPortListEntry *ListEntry;
ListEntry = CTEAllocMem(sizeof(ReservedPortListEntry));
if (ListEntry) {
ListEntry->UpperRange = request->UpperRange;
ListEntry->LowerRange = request->LowerRange;
CTEGetLock(&AddrObjTableLock.Lock, &Handle);
ListEntry->next = PortRangeList;
PortRangeList = ListEntry;
CTEFreeLock(&AddrObjTableLock.Lock, Handle);
} else
status = STATUS_INSUFFICIENT_RESOURCES;
} else if (PortRangeList) {
//UNRESERVE
ReservedPortListEntry *ListEntry, *PrevEntry;
CTEGetLock(&AddrObjTableLock.Lock, &Handle);
ListEntry = PortRangeList;
PrevEntry = ListEntry;
status = STATUS_INVALID_PARAMETER;
while (ListEntry) {
if ((request->LowerRange <= ListEntry->LowerRange) &&
(request->UpperRange >= ListEntry->UpperRange)) {
//This list should be deleted.
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Deleting port range %d to %d\n", request->LowerRange, request->UpperRange));
if (PrevEntry == PortRangeList) {
PortRangeList = ListEntry->next;
CTEFreeMem(ListEntry);
ListEntry = PortRangeList;
} else {
PrevEntry->next = ListEntry->next;
CTEFreeMem(ListEntry);
ListEntry = PrevEntry->next;
}
status = STATUS_SUCCESS;
break;
} else {
PrevEntry = ListEntry;
ListEntry = ListEntry->next;
}
}
CTEFreeLock(&AddrObjTableLock.Lock, Handle);
}
} else
status = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return (status);
}
NTSTATUS
BlockTCPPorts(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
{
TDI_STATUS status = STATUS_SUCCESS;
BOOLEAN ReservePorts;
ULONG NumberofPorts;
CTELockHandle Handle;
ULONG requestLength, responseLength;
PTCP_BLOCKPORTS_REQUEST request;
PULONG response;
Irp->IoStatus.Information = 0;
request = (PTCP_BLOCKPORTS_REQUEST) Irp->AssociatedIrp.SystemBuffer;
response = (PULONG) Irp->AssociatedIrp.SystemBuffer;
requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
responseLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
if (requestLength < sizeof(TCP_BLOCKPORTS_REQUEST)) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return (STATUS_INVALID_PARAMETER);
}
ReservePorts = (uchar) request->ReservePorts;
if (ReservePorts) {
ushort LowerRange = MaxUserPort + 1;
ushort UpperRange = 65534;
uint NumberofPorts = request->NumberofPorts;
ReservedPortListEntry *tmpEntry = BlockedPortList, *ListEntry, *prevEntry = NULL;
AddrObj *ExistingAO;
uint PortsRemaining;
ushort Start;
ushort netStart;
ushort LeftEdge;
if (responseLength < sizeof(ULONG)) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return (STATUS_INVALID_PARAMETER);
}
if ((UpperRange - LowerRange + 1) < (ushort) NumberofPorts) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return (STATUS_INVALID_PARAMETER);
}
if (!NumberofPorts) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return (STATUS_INVALID_PARAMETER);
}
CTEGetLock(&AddrObjTableLock.Lock, &Handle);
// its assumed that BlockedPortList is sorted in the order of port number range
Start = LowerRange;
PortsRemaining = NumberofPorts;
LeftEdge = Start;
while (Start < UpperRange) {
// check whether the current port lies in the reserved range
if ((tmpEntry) && ((Start >= tmpEntry->LowerRange) && (Start <= tmpEntry->UpperRange))) {
Start = tmpEntry->UpperRange + 1;
PortsRemaining = NumberofPorts;
LeftEdge = Start;
prevEntry = tmpEntry;
tmpEntry = tmpEntry->next;
} else {
// Start port doesn't lie in the current blocked range
// check whether somebody has done a bind to it
netStart = net_short(Start);
ExistingAO = FindAddrObjWithPort(netStart);
if (ExistingAO) {
Start++;
PortsRemaining = NumberofPorts;
LeftEdge = Start;
} else {
PortsRemaining--;
Start++;
if (!PortsRemaining) {
break;
}
}
}
}
// we have either found the range
// or we couldn't find a contiguous range
if (!PortsRemaining) {
// we found the range
// return the range
// LeftEdge <-> LeftEdge + NumberofPorts - 1
ListEntry = CTEAllocMem(sizeof(ReservedPortListEntry));
if (ListEntry) {
ListEntry->LowerRange = LeftEdge;
ListEntry->UpperRange = LeftEdge + NumberofPorts - 1;
// BlockedPortList is a sorted list
if (prevEntry) {
// insert it after prevEntry
ListEntry->next = prevEntry->next;
prevEntry->next = ListEntry;
} else {
// this has to be the first element in the list
ListEntry->next = BlockedPortList;
BlockedPortList = ListEntry;
}
Irp->IoStatus.Information = sizeof(ULONG);
*response = LeftEdge;
status = STATUS_SUCCESS;
} else {
// no resources
status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
// couldn't find the range
status = STATUS_INVALID_PARAMETER;
}
} else {
// unreserve the ports;
ReservedPortListEntry *CurrEntry = BlockedPortList;
ReservedPortListEntry *PrevEntry = NULL;
ULONG StartHandle;
StartHandle = request->StartHandle;
CTEGetLock(&AddrObjTableLock.Lock, &Handle);
status = STATUS_INVALID_PARAMETER;
while (CurrEntry) {
if (CurrEntry->LowerRange == StartHandle) {
// delete the entry
if (PrevEntry == NULL) {
// this is the first entry
BlockedPortList = CurrEntry->next;
} else {
// this is intermediate entry
PrevEntry->next = CurrEntry->next;
}
// free the current entry
CTEFreeMem(CurrEntry);
status = STATUS_SUCCESS;
break;
} else if (StartHandle > CurrEntry->UpperRange) {
PrevEntry = CurrEntry;
CurrEntry = CurrEntry->next;
} else {
// the list is sorted can't find the handle
break;
}
}
}
CTEFreeLock(&AddrObjTableLock.Lock, Handle);
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return (status);
}
NTSTATUS
TCPEnumerateConnectionList(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Processes a request to enumerate the workstation connection list.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successful.
Notes:
This routine does not pend.
--*/
{
TCPConnectionListEntry *request;
TCPConnectionListEnum *response;
ULONG requestLength, responseLength;
NTSTATUS status;
PAGED_CODE();
request = (TCPConnectionListEntry *) Irp->AssociatedIrp.SystemBuffer;
response = (TCPConnectionListEnum *) request;
requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
responseLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
if (responseLength < sizeof(TCPConnectionListEnum)) {
status = STATUS_BUFFER_TOO_SMALL;
Irp->IoStatus.Information = 0;
} else {
EnumerateConnectionList(
(uchar *) (response + 1),
responseLength - sizeof(TCPConnectionListEnum),
&(response->tce_entries_returned),
&(response->tce_entries_available)
);
status = TDI_SUCCESS;
Irp->IoStatus.Information =
sizeof(TCPConnectionListEnum) +
(response->tce_entries_returned * sizeof(TCPConnectionListEntry));
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return (status);
}
NTSTATUS
TCPCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Arguments:
DeviceObject - Pointer to the device object for this request.
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
TDI_REQUEST Request;
NTSTATUS status;
FILE_FULL_EA_INFORMATION *ea;
FILE_FULL_EA_INFORMATION UNALIGNED *targetEA;
PTCP_CONTEXT tcpContext;
uint protocol;
PAGED_CODE();
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPCreate \n")));
tcpContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(TCP_CONTEXT), 'cPCT');
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);
CTEInitLock(&(tcpContext->EndpointLock));
tcpContext->Cleanup = FALSE;
ea = (PFILE_FULL_EA_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
//
// See if this is a Control Channel open.
//
if (!ea) {
IF_TCPDBG(TCP_DEBUG_OPEN) {
TCPTRACE((
"TCPCreate: Opening control channel for file object %lx\n",
IrpSp->FileObject
));
}
tcpContext->Handle.ControlChannel = NULL;
IrpSp->FileObject->FsContext = tcpContext;
IrpSp->FileObject->FsContext2 = (PVOID) TDI_CONTROL_CHANNEL_FILE;
return (STATUS_SUCCESS);
}
//
// See if this is an Address Object open.
//
targetEA = FindEA(
ea,
TdiTransportAddress,
TDI_TRANSPORT_ADDRESS_LENGTH
);
if (targetEA != NULL) {
UCHAR optionsBuffer[3];
PUCHAR optionsPointer = optionsBuffer;
//Sanity check the address list. Should be bound by EaValueLength
{
TA_ADDRESS *tmpTA;
TRANSPORT_ADDRESS UNALIGNED *tmpTAList;
LONG Count = 1;
UINT tmpLen = 0;
UINT sizeof_TransportAddress = FIELD_OFFSET(TRANSPORT_ADDRESS, Address);
UINT sizeof_TAAddress = FIELD_OFFSET(TA_ADDRESS, Address);
if (ea->EaValueLength >= sizeof_TransportAddress + sizeof_TAAddress) {
tmpTAList = (TRANSPORT_ADDRESS UNALIGNED *)
& (targetEA->EaName[targetEA->EaNameLength + 1]);
Count = tmpTAList->TAAddressCount;
tmpLen = sizeof_TransportAddress;
tmpTA = (PTA_ADDRESS) tmpTAList->Address;
while (Count && ((tmpLen += sizeof_TAAddress) <= ea->EaValueLength)) {
tmpLen += tmpTA->AddressLength;
tmpTA = (PTA_ADDRESS) (tmpTA->Address + tmpTA->AddressLength);
Count--;
}
if (tmpLen > ea->EaValueLength) {
Count = 1;
}
}
if (Count) {
//Does not match what is stated in EA. Bail out
TCPTRACE(("TCPCreate: ea count and Ea Val length does not match for transport address's\n"));
status = STATUS_INVALID_EA_NAME;
ExFreePool(tcpContext);
ASSERT(status != TDI_PENDING);
return (status);
}
}
if (DeviceObject == TCPDeviceObject) {
protocol = PROTOCOL_TCP;
} else if (DeviceObject == UDPDeviceObject) {
protocol = PROTOCOL_UDP;
ASSERT(optionsPointer - optionsBuffer <= 3);
if (IsDHCPZeroAddress(
(TRANSPORT_ADDRESS UNALIGNED *)
& (targetEA->EaName[targetEA->EaNameLength + 1])
)) {
#if ACC
if (!IsAdminIoRequest(Irp, IrpSp)) {
ExFreePool(tcpContext);
return (STATUS_ACCESS_DENIED);
}
#endif
*optionsPointer = TDI_ADDRESS_OPTION_DHCP;
optionsPointer++;
}
ASSERT(optionsPointer - optionsBuffer <= 3);
} else {
//
// This is a raw ip open
//
#if ACC
//
// Only administrators can create raw addresses
// unless this is allowed through registry
//
if (!AllowUserRawAccess && !IsAdminIoRequest(Irp, IrpSp)) {
ExFreePool(tcpContext);
return (STATUS_ACCESS_DENIED);
}
#endif // ACC
protocol = RawExtractProtocolNumber(
&(IrpSp->FileObject->FileName)
);
if ((protocol == 0xFFFFFFFF) || (protocol == PROTOCOL_TCP)) {
ExFreePool(tcpContext);
return (STATUS_INVALID_PARAMETER);
}
}
if ((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) ||
(IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)
) {
*optionsPointer = TDI_ADDRESS_OPTION_REUSE;
optionsPointer++;
}
*optionsPointer = TDI_OPTION_EOL;
IF_TCPDBG(TCP_DEBUG_OPEN) {
TCPTRACE((
"TCPCreate: Opening address for file object %lx\n",
IrpSp->FileObject
));
}
#if ACC
Request.RequestContext = Irp;
#endif
status = TdiOpenAddress(
&Request,
(TRANSPORT_ADDRESS UNALIGNED *)
& (targetEA->EaName[targetEA->EaNameLength + 1]),
protocol,
optionsBuffer
);
if (NT_SUCCESS(status)) {
//
// Save off the handle to the AO passed back.
//
tcpContext->Handle.AddressHandle = Request.Handle.AddressHandle;
IrpSp->FileObject->FsContext = tcpContext;
IrpSp->FileObject->FsContext2 =
(PVOID) TDI_TRANSPORT_ADDRESS_FILE;
} else {
ExFreePool(tcpContext);
//TCPTRACE(("TdiOpenAddress failed, status %lx\n", status));
if (status == STATUS_ADDRESS_ALREADY_EXISTS) {
status = STATUS_SHARING_VIOLATION;
}
}
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) {
TCPTRACE((
"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;
tcpContext->Conn = (UINT_PTR) Request.RequestContext;
} else {
ExFreePool(tcpContext);
TCPTRACE((
"TdiOpenConnection failed, status %lx\n",
status
));
}
} else {
TCPTRACE((
"TCP: TdiOpenConnection issued on UDP device!\n"
));
status = STATUS_INVALID_DEVICE_REQUEST;
ExFreePool(tcpContext);
}
ASSERT(status != TDI_PENDING);
return (status);
}
TCPTRACE(("TCPCreate: didn't find any useful ea's\n"));
status = STATUS_INVALID_EA_NAME;
ExFreePool(tcpContext);
ASSERT(status != TDI_PENDING);
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPCreate \n")));
return (status);
} // TCPCreate
#if ACC
BOOLEAN
IsAdminIoRequest(
PIRP Irp,
PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
(Lifted from AFD - AfdPerformSecurityCheck)
Compares security context of the endpoint creator to that
of the administrator and local system.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
TRUE - the socket creator has admin or local system privilige
FALSE - the socket creator is just a plain user
--*/
{
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;
}
#endif
void
TCPCloseObjectComplete(
void *Context,
unsigned int Status,
unsigned int UnUsed
)
/*++
Routine Description:
Completes a TdiCloseConnectoin or TdiCloseAddress request.
Arguments:
Context - A pointer to the IRP for this request.
Status - The final status of the operation.
UnUsed - An unused parameter
Return Value:
None.
Notes:
--*/
{
KIRQL oldIrql;
PIRP irp;
PIO_STACK_LOCATION irpSp;
PTCP_CONTEXT tcpContext;
CTELockHandle CancelHandle;
UNREFERENCED_PARAMETER(UnUsed);
irp = (PIRP) Context;
irpSp = IoGetCurrentIrpStackLocation(irp);
tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext;
irp->IoStatus.Status = Status;
IF_TCPDBG(TCP_DEBUG_CLEANUP) {
TCPTRACE((
"TCPCloseObjectComplete on file object %lx\n",
irpSp->FileObject
));
}
CTEGetLock(&tcpContext->EndpointLock, &CancelHandle);
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) {
TCPTRACE((
"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)));
}
//
// Free the EndpointLock before setting CleanupEvent,
// as tcpContext can go away as soon as the event is signalled.
//
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE);
return;
}
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
return;
} // TCPCleanupComplete
NTSTATUS
TCPCleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Cancels all outstanding Irps on a TDI object by calling the close
routine for the object. It then waits for them to be completed
before returning.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
Notes:
This routine blocks, but does not pend.
--*/
{
KIRQL oldIrql;
PIRP cancelIrp = NULL;
PTCP_CONTEXT tcpContext;
NTSTATUS status;
TDI_REQUEST request;
CTELockHandle CancelHandle;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPCleanup \n")));
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
CTEGetLock(&tcpContext->EndpointLock, &CancelHandle);
tcpContext->CancelIrps = TRUE;
KeResetEvent(&(tcpContext->CleanupEvent));
if (tcpContext->Cleanup) {
#if DBG_VALIDITY_CHECK
//Double cleanup call!!!
DbgBreakPoint();
#endif
} else {
tcpContext->Cleanup = TRUE;
tcpContext->Irp = Irp;
}
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
//
// 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) {
TCPTRACE((
"TCPCleanup: Closing address object on file object %lx\n",
IrpSp->FileObject
));
}
request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
status = TdiCloseAddress(&request);
break;
case TDI_CONNECTION_FILE:
IF_TCPDBG(TCP_DEBUG_CLOSE) {
TCPTRACE((
"TCPCleanup: Closing Connection object on file object %lx\n",
IrpSp->FileObject
));
}
request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
status = TdiCloseConnection(&request);
break;
case TDI_CONTROL_CHANNEL_FILE:
IF_TCPDBG(TCP_DEBUG_CLOSE) {
TCPTRACE((
"TCPCleanup: Closing Control Channel object on file object %lx\n",
IrpSp->FileObject
));
}
status = STATUS_SUCCESS;
break;
default:
//
// This should never happen.
//
ASSERT(FALSE);
CTEGetLock(&tcpContext->EndpointLock, &CancelHandle);
tcpContext->CancelIrps = FALSE;
CTEFreeLock(&tcpContext->EndpointLock, CancelHandle);
return (STATUS_INVALID_PARAMETER);
}
if (status != TDI_PENDING) {
TCPCloseObjectComplete(Irp, status, 0);
}
IF_TCPDBG(TCP_DEBUG_CLEANUP) {
TCPTRACE((
"TCPCleanup: waiting for completion of Irps on file object %lx\n",
IrpSp->FileObject
));
}
status = KeWaitForSingleObject(
&(tcpContext->CleanupEvent),
UserRequest,
KernelMode,
FALSE,
NULL
);
ASSERT(NT_SUCCESS(status));
IF_TCPDBG(TCP_DEBUG_CLEANUP) {
TCPTRACE((
"TCPCleanup: Wait on file object %lx finished\n",
IrpSp->FileObject
));
}
//
// The cleanup Irp will be completed by the dispatch routine.
//
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPCleanup \n")));
return (Irp->IoStatus.Status);
} // TCPCleanup
NTSTATUS
TCPClose(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Dispatch routine for MJ_CLOSE IRPs. Performs final cleanup of the
open endpoint.
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
Notes:
This request does not pend.
--*/
{
PTCP_CONTEXT tcpContext;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPClose \n")));
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) {
TCPTRACE(("TCPClose on file object %lx\n", IrpSp->FileObject));
}
ExFreePool(tcpContext);
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPClose \n")));
return (STATUS_SUCCESS);
} // TCPClose
NTSTATUS
TCPDispatchDeviceControl(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Arguments:
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current stack location in the Irp.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
NTSTATUS status;
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("+TCPDispatchDeviceControl \n")));
//
// 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_RCVWND:
// Allow this IOCTL to set the window size only
// if there are no established connections
if (TStats.ts_currestab == 0) {
PULONG request = Irp->AssociatedIrp.SystemBuffer;
ULONG tmp = DefaultRcvWin;
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return STATUS_INVALID_PARAMETER;
}
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(ULONG)) {
if ((LONG)*request > 0) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"setting global rcv window %d\n", *request));
if (!(TcpHostOpts & TCP_FLAG_WS) && *request > 0xFFFF) {
DefaultRcvWin = 0xFFFF;
} else {
uint rcvwinscale=0;
//
// Check for valid window size
// taking care of window scaling option
//
while ((rcvwinscale < TCP_MAX_WINSHIFT) &&
((TCP_MAXWIN << rcvwinscale) < (LONG)*request)) {
rcvwinscale++;
}
if (rcvwinscale == TCP_MAX_WINSHIFT) {
DefaultRcvWin = TCP_MAXWIN << rcvwinscale;
} else {
DefaultRcvWin = *request;
}
}
}
}
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"returning global rcv window %d\n", *request));
*request = tmp;
status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof(ULONG);
break;
} else {
status = STATUS_CONNECTION_ACTIVE;
}
case IOCTL_TCP_FINDTCB:
{
IPAddr Src;
IPAddr Dest;
ushort DestPort;
ushort SrcPort;
PTCP_FINDTCB_REQUEST request;
PTCP_FINDTCB_RESPONSE TCBInfo;
ULONG InfoBufferLen;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(TCP_FINDTCB_REQUEST)
)
||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(TCP_FINDTCB_RESPONSE))
) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return STATUS_INVALID_PARAMETER;
}
request = Irp->AssociatedIrp.SystemBuffer;
Src = request->Src;
Dest = request->Dest;
DestPort = request->DestPort;
SrcPort = request->SrcPort;
InfoBufferLen = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
TCBInfo = Irp->AssociatedIrp.SystemBuffer;
NdisZeroMemory(TCBInfo, sizeof(TCP_FINDTCB_RESPONSE));
status = GetTCBInfo(TCBInfo, Dest, Src, DestPort, SrcPort);
if (status == STATUS_SUCCESS) {
Irp->IoStatus.Information = sizeof(TCP_FINDTCB_RESPONSE);
} else {
Irp->IoStatus.Information = 0;
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return status;
break;
}
case IOCTL_TCP_QUERY_INFORMATION_EX:
return (TCPQueryInformationEx(Irp, IrpSp));
break;
case IOCTL_TCP_WSH_SET_INFORMATION_EX:
case IOCTL_TCP_SET_INFORMATION_EX:
return (TCPSetInformationEx(Irp, IrpSp));
break;
case IOCTL_TCP_QUERY_SECURITY_FILTER_STATUS:
case IOCTL_TCP_SET_SECURITY_FILTER_STATUS:
return (TCPControlSecurityFilter(Irp, IrpSp));
break;
case IOCTL_TCP_ADD_SECURITY_FILTER:
case IOCTL_TCP_DELETE_SECURITY_FILTER:
return (TCPProcessSecurityFilterRequest(Irp, IrpSp));
break;
case IOCTL_TCP_ENUMERATE_SECURITY_FILTER:
return (TCPEnumerateSecurityFilter(Irp, IrpSp));
break;
case IOCTL_TCP_RESERVE_PORT_RANGE:
case IOCTL_TCP_UNRESERVE_PORT_RANGE:
return (TCPReservePorts(Irp, IrpSp));
break;
case IOCTL_TCP_BLOCK_PORTS:
return (BlockTCPPorts(Irp, IrpSp));
break;
default:
status = STATUS_NOT_IMPLEMENTED;
break;
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
DEBUGMSG(DBG_TRACE && DBG_TDI, (DTEXT("-TCPDispatchDeviceControl \n")));
return status;
} // TCPDispatchDeviceControl
#if TRACE_EVENT
NTSTATUS
TCPEventTraceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine handles any WMI requests for enabling/disabling Event
tracing.
Arguments:
DeviceObject - Context for the activity.
Irp - The device control argument block.
Return Value:
Status is returned.
--*/
{
NTSTATUS status;
ULONG retSize;
if (DeviceObject != IPDeviceObject) {
PIO_STACK_LOCATION irpSp;
ULONG bufferSize;
PVOID buffer;
irpSp = IoGetCurrentIrpStackLocation(Irp);
bufferSize = irpSp->Parameters.WMI.BufferSize;
buffer = irpSp->Parameters.WMI.Buffer;
switch (irpSp->MinorFunction) {
case IRP_MN_SET_TRACE_NOTIFY:
if (bufferSize < sizeof(PTDI_DATA_REQUEST_NOTIFY_ROUTINE)) {
status = STATUS_BUFFER_TOO_SMALL;
} else {
TCPCPHandlerRoutine = (PTDI_DATA_REQUEST_NOTIFY_ROUTINE)
* ((PVOID *) buffer);
status = STATUS_SUCCESS;
}
retSize = 0;
break;
case IRP_MN_REGINFO:
{
//
// Stub for now. TCP can register its Guids with WMI here
//
PWMIREGINFOW WmiRegInfo;
ULONG WmiRegInfoSize = sizeof(WMIREGINFOW);
status = STATUS_SUCCESS;
if (bufferSize >= WmiRegInfoSize) {
WmiRegInfo = (PWMIREGINFOW) buffer;
RtlZeroMemory(WmiRegInfo, WmiRegInfoSize);
WmiRegInfo->BufferSize = WmiRegInfoSize;
retSize = WmiRegInfoSize;
} else {
*(ULONG *) buffer = WmiRegInfoSize;
retSize = sizeof(ULONG);
}
break;
}
default:
TCPTRACE((
"TCPDispatch: Irp %lx unsupported minor function 0x%lx\n",
irpSp,
irpSp->MinorFunction
));
retSize = 0;
status = STATUS_INVALID_DEVICE_REQUEST;
}
} else {
status = STATUS_INVALID_DEVICE_REQUEST;
retSize = 0;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = retSize;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return status;
}
#endif
NTSTATUS
TCPDispatchInternalDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This is the dispatch routine for Internal Device Control IRPs.
This is the hot path for kernel-mode clients.
Arguments:
DeviceObject - Pointer to device object for target device
Irp - Pointer to I/O request packet
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
DEBUGMSG(DBG_TRACE && DBG_TDI && DBG_VERBOSE,
(DTEXT("+TCPDispatchInternalDeviceControl \n")));
#if IPMCAST
if (DeviceObject == IpMcastDeviceObject) {
return IpMcastDispatch(DeviceObject,
Irp);
}
#endif
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) {
return (UDPSendData(Irp, irpSp));
}
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);
}
if (irpSp->MinorFunction == TDI_CONNECT) {
return (TCPConnect(Irp, irpSp));
}
if (irpSp->MinorFunction == TDI_DISCONNECT) {
return (TCPDisconnect(Irp, irpSp));
}
//
// 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:
TCPTRACE((
"TCP: Call to unimplemented TDI function 0x%x\n",
irpSp->MinorFunction
));
status = STATUS_NOT_IMPLEMENTED;
break;
default:
TCPTRACE((
"TCP: call to invalid TDI function 0x%x\n",
irpSp->MinorFunction
));
status = STATUS_INVALID_DEVICE_REQUEST;
}
ASSERT(status != TDI_PENDING);
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return status;
}
DEBUGMSG(DBG_TRACE && DBG_TDI && DBG_VERBOSE, (DTEXT("-TCPDispatchInternalDeviceControl \n")));
return (IPDispatch(DeviceObject, Irp));
}
NTSTATUS
TCPDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This is the generic dispatch routine for TCP/UDP/RawIP.
Arguments:
DeviceObject - Pointer to device object for target device
Irp - Pointer to I/O request packet
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
DEBUGMSG(DBG_TRACE && DBG_TDI && DBG_VERBOSE, (DTEXT("+TCPDispatch(%x, %x) \n"), DeviceObject, Irp));
#if IPMCAST
if (DeviceObject == IpMcastDeviceObject) {
return IpMcastDispatch(DeviceObject,
Irp);
}
#endif
if (DeviceObject != IPDeviceObject) {
#if MILLEN
// Ensure that the driver context is zero'd for our use.
Irp->Tail.Overlay.DriverContext[0] = NULL;
#endif // MILLEN
Irp->IoStatus.Information = 0;
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) {
PULONG_PTR EntryPoint;
EntryPoint = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
try {
// Type3InputBuffer must be writeable by the caller.
if (Irp->RequestorMode != KernelMode) {
ProbeForWrite(EntryPoint,
sizeof(ULONG_PTR),
PROBE_ALIGNMENT(ULONG_PTR));
}
*EntryPoint = (ULONG_PTR) TCPSendData;
status = STATUS_SUCCESS;
}
except(EXCEPTION_EXECUTE_HANDLER) {
status = STATUS_INVALID_PARAMETER;
}
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_PNP:
status = TCPDispatchPnPPower(Irp, irpSp);
break;
#if TRACE_EVENT
case IRP_MJ_SYSTEM_CONTROL:
return TCPEventTraceControl(DeviceObject, Irp);
#endif
case IRP_MJ_WRITE:
case IRP_MJ_READ:
default:
TCPTRACE((
"TCPDispatch: Irp %lx unsupported major function 0x%lx\n",
irpSp,
irpSp->MajorFunction
));
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
ASSERT(status != TDI_PENDING);
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return status;
}
return (IPDispatch(DeviceObject, Irp));
} // TCPDispatch
//
// Private utility functions
//
FILE_FULL_EA_INFORMATION UNALIGNED *
FindEA(
PFILE_FULL_EA_INFORMATION StartEA,
CHAR * TargetName,
USHORT TargetNameLength
)
/*++
Routine Description:
Parses and extended attribute list for a given target attribute.
Arguments:
StartEA - the first extended attribute in the list.
TargetName - the name of the target attribute.
TargetNameLength - the length of the name of the target attribute.
Return Value:
A pointer to the requested attribute or NULL if the target wasn't found.
--*/
{
USHORT i;
BOOLEAN found;
FILE_FULL_EA_INFORMATION UNALIGNED *CurrentEA;
PAGED_CODE();
do {
found = TRUE;
CurrentEA = StartEA;
StartEA = (FILE_FULL_EA_INFORMATION *) ((PUCHAR) StartEA + CurrentEA->NextEntryOffset);
if (CurrentEA->EaNameLength != TargetNameLength) {
continue;
}
for (i = 0; i < CurrentEA->EaNameLength; i++) {
if (CurrentEA->EaName[i] == TargetName[i]) {
continue;
}
found = FALSE;
break;
}
if (found) {
return (CurrentEA);
}
} while (CurrentEA->NextEntryOffset != 0);
return (NULL);
}
BOOLEAN
IsDHCPZeroAddress(
TRANSPORT_ADDRESS UNALIGNED * AddrList
)
/*++
Routine Description:
Checks a TDI IP address list for an address from DHCP binding
to the IP address zero. Normally, binding to zero means wildcard.
For DHCP, it really means bind to an interface with an address of
zero. This semantic is flagged by a special value in an unused
portion of the address structure (ie. this is a kludge).
Arguments:
AddrList - The TDI transport address list passed in the create IRP.
Return Value:
TRUE if the first IP address found had the flag set. FALSE otherwise.
--*/
{
int i; // Index variable.
TA_ADDRESS *CurrentAddr; // Address we're examining and may use.
// First, verify that someplace in Address is an address we can use.
CurrentAddr = (PTA_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 = (PTA_ADDRESS)
(CurrentAddr->Address + CurrentAddr->AddressLength);
}
}
return FALSE; // Didn't find a match.
}
ULONG
TCPGetMdlChainByteCount(
PMDL Mdl
)
/*++
Routine Description:
Sums the byte counts of each MDL in a chain.
Arguments:
Mdl - Pointer to the MDL chain to sum.
Return Value:
The byte count of the MDL chain.
--*/
{
ULONG count = 0;
while (Mdl != NULL) {
count += MmGetMdlByteCount(Mdl);
Mdl = Mdl->Next;
}
return (count);
}
ULONG
TCPGetNdisBufferChainByteCount(
PNDIS_BUFFER pBuffer
)
{
ULONG cb = 0;
while (pBuffer != NULL) {
cb += NdisBufferLength(pBuffer);
pBuffer = NDIS_BUFFER_LINKAGE(pBuffer);
}
return cb;
}
ULONG
RawExtractProtocolNumber(
IN PUNICODE_STRING FileName
)
/*++
Routine Description:
Extracts the protocol number from the file object name.
Arguments:
FileName - The unicode file name.
Return Value:
The protocol number or 0xFFFFFFFF on error.
--*/
{
PWSTR name;
UNICODE_STRING unicodeString;
USHORT length;
ULONG protocol;
NTSTATUS status;
PAGED_CODE();
name = FileName->Buffer;
if (FileName->Length <
(sizeof(OBJ_NAME_PATH_SEPARATOR) + sizeof(WCHAR))
) {
return (0xFFFFFFFF);
}
//
// Step over separator
//
if (*name++ != OBJ_NAME_PATH_SEPARATOR) {
return (0xFFFFFFFF);
}
if (*name == UNICODE_NULL) {
return (0xFFFFFFFF);
}
//
// Convert the remaining name into a number.
//
RtlInitUnicodeString(&unicodeString, name);
status = RtlUnicodeStringToInteger(
&unicodeString,
10,
&protocol
);
if (!NT_SUCCESS(status)) {
return (0xFFFFFFFF);
}
if (protocol > 255) {
return (0xFFFFFFFF);
}
return (protocol);
}