2996 lines
88 KiB
C
2996 lines
88 KiB
C
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
|
|
//
|
|
// Copyright (c) 1985-2000 Microsoft Corporation
|
|
//
|
|
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
|
|
// You should have received a copy of the Microsoft End-User License Agreement
|
|
// for this software along with this release; see the file "license.txt".
|
|
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
|
|
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
|
|
//
|
|
// Abstract:
|
|
//
|
|
// NT specific routines for dispatching and handling IRPs.
|
|
//
|
|
|
|
|
|
#include <oscfg.h>
|
|
#include <ndis.h>
|
|
#include <tdikrnl.h>
|
|
#include <tdint.h>
|
|
#include <tdistat.h>
|
|
#include <tdiinfo.h>
|
|
#include <ip6imp.h>
|
|
#include <ip6def.h>
|
|
#include <ntddip6.h>
|
|
#include "queue.h"
|
|
#include "transprt.h"
|
|
#include "addr.h"
|
|
#include "tcp.h"
|
|
#include "udp.h"
|
|
#include "raw.h"
|
|
#include <ntddtcp.h>
|
|
#include "tcpcfg.h"
|
|
#include "tcpconn.h"
|
|
#include "tdilocal.h"
|
|
|
|
//
|
|
// Macros
|
|
//
|
|
|
|
//* Convert100nsToMillisconds
|
|
//
|
|
// Converts time expressed in hundreds of nanoseconds to milliseconds.
|
|
//
|
|
// REVIEW: replace RtlExtendedMagicDivide with 64 bit compiler support?
|
|
//
|
|
// LARGE_INTEGER // Returns: Time in milliseconds.
|
|
// Convert100nsToMilliseconds(
|
|
// IN LARGE_INTEGER HnsTime); // Time in hundreds of nanoseconds.
|
|
//
|
|
#define SHIFT10000 13
|
|
static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758};
|
|
|
|
#define Convert100nsToMilliseconds(HnsTime) \
|
|
RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000)
|
|
|
|
|
|
//
|
|
// Global variables
|
|
//
|
|
extern PSECURITY_DESCRIPTOR TcpAdminSecurityDescriptor;
|
|
extern PDEVICE_OBJECT TCPDeviceObject, UDPDeviceObject;
|
|
extern PDEVICE_OBJECT IPDeviceObject;
|
|
extern PDEVICE_OBJECT RawIPDeviceObject;
|
|
|
|
|
|
//
|
|
// Local types
|
|
//
|
|
typedef struct {
|
|
PIRP Irp;
|
|
PMDL InputMdl;
|
|
PMDL OutputMdl;
|
|
TCP_REQUEST_QUERY_INFORMATION_EX QueryInformation;
|
|
} TCP_QUERY_CONTEXT, *PTCP_QUERY_CONTEXT;
|
|
|
|
|
|
//
|
|
// General external function prototypes
|
|
//
|
|
extern
|
|
NTSTATUS
|
|
IPDispatch(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
|
|
//
|
|
// Other external functions
|
|
//
|
|
void
|
|
TCPAbortAndIndicateDisconnect(
|
|
CONNECTION_CONTEXT ConnnectionContext
|
|
);
|
|
|
|
//
|
|
// Local pageable function prototypes
|
|
//
|
|
NTSTATUS
|
|
TCPDispatchDeviceControl(
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
);
|
|
|
|
NTSTATUS
|
|
TCPCreate(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
);
|
|
|
|
NTSTATUS
|
|
TCPAssociateAddress(
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
);
|
|
|
|
NTSTATUS
|
|
TCPSetEventHandler(
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
);
|
|
|
|
NTSTATUS
|
|
TCPQueryInformation(
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
);
|
|
|
|
FILE_FULL_EA_INFORMATION UNALIGNED *
|
|
FindEA(
|
|
PFILE_FULL_EA_INFORMATION StartEA,
|
|
CHAR *TargetName,
|
|
USHORT TargetNameLength
|
|
);
|
|
|
|
BOOLEAN
|
|
IsAdminIoRequest(
|
|
PIRP Irp,
|
|
PIO_STACK_LOCATION IrpSp
|
|
);
|
|
|
|
BOOLEAN
|
|
IsDHCPZeroAddress(
|
|
TRANSPORT_ADDRESS UNALIGNED *AddrList
|
|
);
|
|
|
|
ULONG
|
|
RawExtractProtocolNumber(
|
|
IN PUNICODE_STRING FileName
|
|
);
|
|
|
|
NTSTATUS
|
|
TCPEnumerateConnectionList(
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
);
|
|
|
|
//
|
|
// Local helper routine prototypes.
|
|
//
|
|
ULONG
|
|
TCPGetMdlChainByteCount(
|
|
PMDL Mdl
|
|
);
|
|
|
|
|
|
//
|
|
// All of this code is pageable.
|
|
//
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(PAGE, TCPDispatchDeviceControl)
|
|
#pragma alloc_text(PAGE, TCPCreate)
|
|
#pragma alloc_text(PAGE, TCPAssociateAddress)
|
|
#pragma alloc_text(PAGE, TCPSetEventHandler)
|
|
#pragma alloc_text(PAGE, FindEA)
|
|
#pragma alloc_text(PAGE, IsDHCPZeroAddress)
|
|
#pragma alloc_text(PAGE, RawExtractProtocolNumber)
|
|
#pragma alloc_text(PAGE, IsAdminIoRequest)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
//
|
|
// Generic Irp completion and cancellation routines.
|
|
//
|
|
|
|
//* TCPDataRequestComplete - Completes a UDP/TCP send/receive request.
|
|
//
|
|
NTSTATUS // Returns: Nothing.
|
|
TCPDataRequestComplete(
|
|
void *Context, // A pointer to the IRP for this request.
|
|
unsigned int Status, // The final TDI status of the request.
|
|
unsigned int ByteCount) // Bytes sent/received information.
|
|
{
|
|
KIRQL oldIrql;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PTCP_CONTEXT tcpContext;
|
|
PIRP item = NULL;
|
|
|
|
irp = (PIRP) Context;
|
|
irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext;
|
|
|
|
if (IoSetCancelRoutine(irp, NULL) == NULL) {
|
|
IoAcquireCancelSpinLock(&oldIrql);
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
}
|
|
|
|
KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql);
|
|
|
|
#if DBG
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CANCEL) {
|
|
|
|
PLIST_ENTRY entry, listHead;
|
|
PIRP item = NULL;
|
|
|
|
if (irp->Cancel) {
|
|
ASSERT(irp->CancelRoutine == NULL);
|
|
listHead = &(tcpContext->CancelledIrpList);
|
|
} else {
|
|
listHead = &(tcpContext->PendingIrpList);
|
|
}
|
|
|
|
//
|
|
// Verify that the Irp is on the appropriate list.
|
|
//
|
|
for (entry = listHead->Flink; entry != listHead;
|
|
entry = entry->Flink) {
|
|
|
|
item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
if (item == irp) {
|
|
RemoveEntryList(&(irp->Tail.Overlay.ListEntry));
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(item == irp);
|
|
}
|
|
|
|
#endif
|
|
|
|
ASSERT(tcpContext->ReferenceCount > 0);
|
|
|
|
if (--(tcpContext->ReferenceCount) == 0) {
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CANCEL) {
|
|
ASSERT(IsListEmpty(&(tcpContext->CancelledIrpList)));
|
|
ASSERT(IsListEmpty(&(tcpContext->PendingIrpList)));
|
|
}
|
|
|
|
//
|
|
// Set the cleanup event.
|
|
//
|
|
KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE);
|
|
}
|
|
|
|
IF_TCPDBG(TCP_DEBUG_IRP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPDataRequestComplete: "
|
|
"Irp %lx fileobj %lx refcnt dec to %u\n",
|
|
irp, irpSp->FileObject, tcpContext->ReferenceCount));
|
|
}
|
|
|
|
if (irp->Cancel || tcpContext->CancelIrps) {
|
|
|
|
IF_TCPDBG(TCP_DEBUG_IRP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPDataRequestComplete: Irp %lx was cancelled\n", irp));
|
|
}
|
|
|
|
Status = (unsigned int) STATUS_CANCELLED;
|
|
ByteCount = 0;
|
|
}
|
|
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
|
|
IF_TCPDBG(TCP_DEBUG_IRP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPDataRequestComplete: completing irp %lx, status %lx,"
|
|
" byte count %lx\n", irp, Status, ByteCount));
|
|
}
|
|
|
|
irp->IoStatus.Status = (NTSTATUS) Status;
|
|
irp->IoStatus.Information = ByteCount;
|
|
|
|
IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
|
|
|
|
return Status;
|
|
|
|
} // TCPDataRequestComplete
|
|
|
|
|
|
//* TCPRequestComplete - Completes a TDI request.
|
|
//
|
|
// Completes a cancellable TDI request which returns no data by
|
|
// calling TCPDataRequestComplete with a ByteCount of zero.
|
|
//
|
|
void // Returns: Nothing.
|
|
TCPRequestComplete(
|
|
void *Context, // A pointer to the IRP for this request.
|
|
unsigned int Status, // The final TDI status of the request.
|
|
unsigned int UnUsed) // An unused parameter.
|
|
{
|
|
UNREFERENCED_PARAMETER(UnUsed);
|
|
|
|
TCPDataRequestComplete(Context, Status, 0);
|
|
|
|
} // TCPRequestComplete
|
|
|
|
|
|
//* TCPNonCancellableRequestComplete - Complete uncancellable TDI request.
|
|
//
|
|
// Completes a TDI request which cannot be cancelled.
|
|
//
|
|
void // Returns: Nothing.
|
|
TCPNonCancellableRequestComplete(
|
|
void *Context, // A pointer to the IRP for this request.
|
|
unsigned int Status, // The final TDI status of the request.
|
|
unsigned int UnUsed) // An unused parameter.
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
UNREFERENCED_PARAMETER(UnUsed);
|
|
|
|
irp = (PIRP) Context;
|
|
irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CLOSE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPNonCancellableRequestComplete: irp %lx status %lx\n",
|
|
irp, Status));
|
|
}
|
|
|
|
//
|
|
// Complete the IRP.
|
|
//
|
|
irp->IoStatus.Status = (NTSTATUS) Status;
|
|
irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
|
|
|
|
return;
|
|
|
|
} // TCPNonCancellableRequestComplete
|
|
|
|
|
|
//* TCPCancelComplete
|
|
//
|
|
void
|
|
TCPCancelComplete(
|
|
void *Context,
|
|
unsigned int Unused1,
|
|
unsigned int Unused2)
|
|
{
|
|
PFILE_OBJECT fileObject = (PFILE_OBJECT) Context;
|
|
PTCP_CONTEXT tcpContext = (PTCP_CONTEXT) fileObject->FsContext;
|
|
KIRQL oldIrql;
|
|
|
|
UNREFERENCED_PARAMETER(Unused1);
|
|
UNREFERENCED_PARAMETER(Unused2);
|
|
|
|
KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql);
|
|
|
|
//
|
|
// Remove the reference placed on the endpoint by the cancel routine.
|
|
// The cancelled Irp will be completed by the completion routine for the
|
|
// request.
|
|
//
|
|
if (--(tcpContext->ReferenceCount) == 0) {
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CANCEL) {
|
|
ASSERT(IsListEmpty(&(tcpContext->CancelledIrpList)));
|
|
ASSERT(IsListEmpty(&(tcpContext->PendingIrpList)));
|
|
}
|
|
|
|
//
|
|
// Set the cleanup event.
|
|
//
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE);
|
|
return;
|
|
}
|
|
|
|
IF_TCPDBG(TCP_DEBUG_IRP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCancelComplete: fileobj %lx refcnt dec to %u\n",
|
|
fileObject, tcpContext->ReferenceCount));
|
|
}
|
|
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
|
|
return;
|
|
|
|
} // TCPCancelComplete
|
|
|
|
|
|
//* TCPCancelRequest - Cancels an outstanding Irp.
|
|
//
|
|
// Cancel an outstanding Irp.
|
|
//
|
|
VOID // Returns: Nothing.
|
|
TCPCancelRequest(
|
|
PDEVICE_OBJECT Device, // Pointer to the device object for this request.
|
|
PIRP Irp) // Pointer to I/O request packet.
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PTCP_CONTEXT tcpContext;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PFILE_OBJECT fileObject;
|
|
UCHAR minorFunction;
|
|
TDI_REQUEST request;
|
|
KIRQL CancelIrql;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
fileObject = irpSp->FileObject;
|
|
tcpContext = (PTCP_CONTEXT) fileObject->FsContext;
|
|
minorFunction = irpSp->MinorFunction;
|
|
CancelIrql = Irp->CancelIrql;
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&tcpContext->EndpointLock);
|
|
|
|
ASSERT(Irp->Cancel);
|
|
IoReleaseCancelSpinLock(DISPATCH_LEVEL);
|
|
|
|
IF_TCPDBG(TCP_DEBUG_IRP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCancelRequest: cancelling irp %lx, file object %lx\n",
|
|
Irp, fileObject));
|
|
}
|
|
|
|
#if DBG
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CANCEL) {
|
|
//
|
|
// Verify that the Irp is on the pending list.
|
|
//
|
|
PLIST_ENTRY entry;
|
|
PIRP item = NULL;
|
|
|
|
for (entry = tcpContext->PendingIrpList.Flink;
|
|
entry != &(tcpContext->PendingIrpList); entry = entry->Flink) {
|
|
|
|
item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
if (item == Irp) {
|
|
RemoveEntryList( &(Irp->Tail.Overlay.ListEntry));
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(item == Irp);
|
|
|
|
InsertTailList(&(tcpContext->CancelledIrpList),
|
|
&(Irp->Tail.Overlay.ListEntry));
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
//
|
|
// Add a reference so the object can't be closed while the cancel routine
|
|
// is executing.
|
|
//
|
|
ASSERT(tcpContext->ReferenceCount > 0);
|
|
tcpContext->ReferenceCount++;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_IRP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCancelRequest: Irp %lx fileobj %lx refcnt inc to %u\n",
|
|
Irp, fileObject, tcpContext->ReferenceCount));
|
|
}
|
|
|
|
//
|
|
// Try to cancel the request.
|
|
//
|
|
switch(minorFunction) {
|
|
|
|
case TDI_SEND:
|
|
case TDI_RECEIVE:
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, CancelIrql);
|
|
|
|
ASSERT((PtrToUlong(fileObject->FsContext2)) == TDI_CONNECTION_FILE);
|
|
#ifndef UDP_ONLY
|
|
TCPAbortAndIndicateDisconnect(tcpContext->Handle.ConnectionContext);
|
|
#endif
|
|
break;
|
|
|
|
case TDI_SEND_DATAGRAM:
|
|
|
|
ASSERT(PtrToUlong(fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE);
|
|
|
|
TdiCancelSendDatagram(tcpContext->Handle.AddressHandle, Irp,
|
|
&tcpContext->EndpointLock, CancelIrql);
|
|
break;
|
|
|
|
case TDI_RECEIVE_DATAGRAM:
|
|
|
|
ASSERT(PtrToUlong(fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE);
|
|
|
|
TdiCancelReceiveDatagram(tcpContext->Handle.AddressHandle, Irp,
|
|
&tcpContext->EndpointLock, CancelIrql);
|
|
break;
|
|
|
|
case TDI_DISASSOCIATE_ADDRESS:
|
|
|
|
ASSERT(PtrToUlong(fileObject->FsContext2) == TDI_CONNECTION_FILE);
|
|
//
|
|
// This pends but is not cancellable. We put it thru the cancel code
|
|
// anyway so a reference is made for it and so it can be tracked in
|
|
// a debug build.
|
|
//
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, CancelIrql);
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Initiate a disconnect to cancel the request.
|
|
//
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, CancelIrql);
|
|
request.Handle.ConnectionContext =
|
|
tcpContext->Handle.ConnectionContext;
|
|
request.RequestNotifyObject = TCPCancelComplete;
|
|
request.RequestContext = fileObject;
|
|
|
|
status = TdiDisconnect(&request, NULL, TDI_DISCONNECT_ABORT, NULL,
|
|
NULL);
|
|
break;
|
|
}
|
|
|
|
if (status != TDI_PENDING) {
|
|
TCPCancelComplete(fileObject, 0, 0);
|
|
}
|
|
|
|
return;
|
|
|
|
} // TCPCancelRequest
|
|
|
|
|
|
//* TCPPrepareIrpForCancel
|
|
//
|
|
NTSTATUS
|
|
TCPPrepareIrpForCancel(
|
|
PTCP_CONTEXT TcpContext,
|
|
PIRP Irp,
|
|
PDRIVER_CANCEL CancelRoutine)
|
|
{
|
|
KIRQL oldIrql;
|
|
|
|
//
|
|
// Set up for cancellation.
|
|
//
|
|
KeAcquireSpinLock(&TcpContext->EndpointLock, &oldIrql);
|
|
|
|
ASSERT(Irp->CancelRoutine == NULL);
|
|
|
|
if (!Irp->Cancel) {
|
|
|
|
IoMarkIrpPending(Irp);
|
|
IoSetCancelRoutine(Irp, CancelRoutine);
|
|
TcpContext->ReferenceCount++;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_IRP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPPrepareIrpForCancel: irp %lx fileobj %lx refcnt inc"
|
|
" to %u\n", Irp,
|
|
(IoGetCurrentIrpStackLocation(Irp))->FileObject,
|
|
TcpContext->ReferenceCount));
|
|
}
|
|
|
|
#if DBG
|
|
IF_TCPDBG(TCP_DEBUG_CANCEL) {
|
|
PLIST_ENTRY entry;
|
|
PIRP item = NULL;
|
|
|
|
//
|
|
// Verify that the Irp has not already been submitted.
|
|
//
|
|
for (entry = TcpContext->PendingIrpList.Flink;
|
|
entry != &(TcpContext->PendingIrpList);
|
|
entry = entry->Flink) {
|
|
|
|
item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
ASSERT(item != Irp);
|
|
}
|
|
|
|
for (entry = TcpContext->CancelledIrpList.Flink;
|
|
entry != &(TcpContext->CancelledIrpList);
|
|
entry = entry->Flink) {
|
|
|
|
item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
ASSERT(item != Irp);
|
|
}
|
|
|
|
InsertTailList(&(TcpContext->PendingIrpList),
|
|
&(Irp->Tail.Overlay.ListEntry));
|
|
}
|
|
#endif // DBG
|
|
|
|
KeReleaseSpinLock(&TcpContext->EndpointLock, oldIrql);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// The IRP has already been cancelled. Complete it now.
|
|
//
|
|
|
|
IF_TCPDBG(TCP_DEBUG_IRP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCP: irp %lx already cancelled, completing.\n", Irp));
|
|
}
|
|
|
|
KeReleaseSpinLock(&TcpContext->EndpointLock, oldIrql);
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return(STATUS_CANCELLED);
|
|
|
|
} // TCPPrepareIrpForCancel
|
|
|
|
|
|
//
|
|
// TDI functions.
|
|
//
|
|
|
|
|
|
//* TCPAssociateAddress - Handle TDI Associate Address IRP.
|
|
//
|
|
// Converts a TDI Associate Address IRP into a call to TdiAssociateAddress.
|
|
//
|
|
// This routine does not pend.
|
|
//
|
|
NTSTATUS // Returns: indication of whether the request was successful.
|
|
TCPAssociateAddress(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
|
|
{
|
|
NTSTATUS status;
|
|
TDI_REQUEST request;
|
|
PTCP_CONTEXT tcpContext;
|
|
PTDI_REQUEST_KERNEL_ASSOCIATE associateInformation;
|
|
PFILE_OBJECT fileObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
|
|
associateInformation =
|
|
(PTDI_REQUEST_KERNEL_ASSOCIATE) &(IrpSp->Parameters);
|
|
|
|
//
|
|
// Get the file object for the address. Then extract the Address Handle
|
|
// from the TCP_CONTEXT associated with it.
|
|
//
|
|
status = ObReferenceObjectByHandle(associateInformation->AddressHandle,
|
|
0, *IoFileObjectType, Irp->RequestorMode,
|
|
&fileObject, NULL);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if ((fileObject->DeviceObject == TCPDeviceObject) &&
|
|
(PtrToUlong(fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE)) {
|
|
|
|
tcpContext = (PTCP_CONTEXT) fileObject->FsContext;
|
|
|
|
status = TdiAssociateAddress(&request,
|
|
tcpContext->Handle.AddressHandle);
|
|
|
|
ASSERT(status != STATUS_PENDING);
|
|
|
|
ObDereferenceObject(fileObject);
|
|
|
|
IF_TCPDBG(TCP_DEBUG_ASSOCIATE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPAssociateAddress complete on file object %lx\n",
|
|
IrpSp->FileObject));
|
|
}
|
|
} else {
|
|
ObDereferenceObject(fileObject);
|
|
status = STATUS_INVALID_HANDLE;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_ASSOCIATE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPAssociateAddress: ObReference failed on object"
|
|
" %lx, status %lx\n",
|
|
associateInformation->AddressHandle, status));
|
|
}
|
|
}
|
|
} else {
|
|
IF_TCPDBG(TCP_DEBUG_ASSOCIATE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPAssociateAddress: ObReference failed on object %lx,"
|
|
" status %lx\n", associateInformation->AddressHandle,
|
|
status));
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
//* TCPDisassociateAddress - Handle TDI Disassociate Address IRP.
|
|
//
|
|
// Converts a TDI Disassociate Address IRP into a call to
|
|
// TdiDisassociateAddress.
|
|
NTSTATUS // Returns: Indication of whether the request was successful.
|
|
TCPDisassociateAddress(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
NTSTATUS status;
|
|
TDI_REQUEST request;
|
|
PTCP_CONTEXT tcpContext;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_ASSOCIATE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCP disassociating address\n"));
|
|
}
|
|
|
|
ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE);
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
|
|
request.RequestNotifyObject = TCPRequestComplete;
|
|
request.RequestContext = Irp;
|
|
|
|
status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = TdiDisAssociateAddress(&request);
|
|
|
|
if (status != TDI_PENDING) {
|
|
TCPRequestComplete(Irp, status, 0);
|
|
}
|
|
//
|
|
// Return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING.
|
|
//
|
|
return(TDI_PENDING);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // TCPDisassociateAddress
|
|
|
|
|
|
//* TCPConnect - Handle TDI Connect IRP.
|
|
//
|
|
// Converts a TDI Connect IRP into a call to TdiConnect.
|
|
//
|
|
NTSTATUS // Returns: Whether the request was successfully queued.
|
|
TCPConnect(
|
|
IN PIRP Irp, // Pointer to I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
NTSTATUS status;
|
|
PTCP_CONTEXT tcpContext;
|
|
TDI_REQUEST request;
|
|
PTDI_CONNECTION_INFORMATION requestInformation, returnInformation;
|
|
PTDI_REQUEST_KERNEL_CONNECT connectRequest;
|
|
LARGE_INTEGER millisecondTimeout;
|
|
PLARGE_INTEGER requestTimeout;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CONNECT) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPConnect irp %lx, file object %lx\n", Irp,
|
|
IrpSp->FileObject));
|
|
}
|
|
|
|
ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE);
|
|
|
|
connectRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(IrpSp->Parameters);
|
|
requestInformation = connectRequest->RequestConnectionInformation;
|
|
returnInformation = connectRequest->ReturnConnectionInformation;
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
|
|
request.RequestNotifyObject = TCPRequestComplete;
|
|
request.RequestContext = Irp;
|
|
|
|
requestTimeout = (PLARGE_INTEGER) connectRequest->RequestSpecific;
|
|
|
|
if (requestTimeout != NULL) {
|
|
//
|
|
// NT relative timeouts are negative. Negate first to get a positive
|
|
// value to pass to the transport.
|
|
//
|
|
millisecondTimeout.QuadPart = -((*requestTimeout).QuadPart);
|
|
millisecondTimeout = Convert100nsToMilliseconds(millisecondTimeout);
|
|
} else {
|
|
millisecondTimeout.LowPart = 0;
|
|
millisecondTimeout.HighPart = 0;
|
|
}
|
|
|
|
|
|
ASSERT(millisecondTimeout.HighPart == 0);
|
|
|
|
status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = TdiConnect(&request, ((millisecondTimeout.LowPart != 0) ?
|
|
&(millisecondTimeout.LowPart) : NULL),
|
|
requestInformation, returnInformation);
|
|
|
|
if (status != STATUS_PENDING) {
|
|
TCPRequestComplete(Irp, status, 0);
|
|
}
|
|
//
|
|
// Return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING.
|
|
//
|
|
return(STATUS_PENDING);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // TCPConnect
|
|
|
|
|
|
//* TCPDisconnect - Handler for TDI Disconnect IRP
|
|
//
|
|
// Converts a TDI Disconnect IRP into a call to TdiDisconnect.
|
|
//
|
|
NTSTATUS // Returns: whether the request was successfully queued.
|
|
TCPDisconnect(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
NTSTATUS status;
|
|
PTCP_CONTEXT tcpContext;
|
|
TDI_REQUEST request;
|
|
PTDI_CONNECTION_INFORMATION requestInformation, returnInformation;
|
|
PTDI_REQUEST_KERNEL_DISCONNECT disconnectRequest;
|
|
LARGE_INTEGER millisecondTimeout;
|
|
PLARGE_INTEGER requestTimeout;
|
|
BOOLEAN abortive = FALSE;
|
|
|
|
ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE);
|
|
|
|
disconnectRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(IrpSp->Parameters);
|
|
requestInformation = disconnectRequest->RequestConnectionInformation;
|
|
returnInformation = disconnectRequest->ReturnConnectionInformation;
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
|
|
request.RequestContext = Irp;
|
|
|
|
//
|
|
// Set up the timeout value.
|
|
//
|
|
if (disconnectRequest->RequestSpecific != NULL) {
|
|
requestTimeout = (PLARGE_INTEGER) disconnectRequest->RequestSpecific;
|
|
|
|
if ((requestTimeout->LowPart == -1) &&
|
|
(requestTimeout->HighPart == -1)) {
|
|
|
|
millisecondTimeout.LowPart = requestTimeout->LowPart;
|
|
millisecondTimeout.HighPart = 0;
|
|
} else {
|
|
//
|
|
// NT relative timeouts are negative. Negate first to get a
|
|
// positive value to pass to the transport.
|
|
//
|
|
millisecondTimeout.QuadPart = -((*requestTimeout).QuadPart);
|
|
millisecondTimeout = Convert100nsToMilliseconds(
|
|
millisecondTimeout);
|
|
}
|
|
} else {
|
|
millisecondTimeout.LowPart = 0;
|
|
millisecondTimeout.HighPart = 0;
|
|
}
|
|
|
|
ASSERT(millisecondTimeout.HighPart == 0);
|
|
|
|
if (disconnectRequest->RequestFlags & TDI_DISCONNECT_ABORT) {
|
|
//
|
|
// Abortive disconnects cannot be cancelled and must use
|
|
// a specific completion routine.
|
|
//
|
|
abortive = TRUE;
|
|
IoMarkIrpPending(Irp);
|
|
request.RequestNotifyObject = TCPNonCancellableRequestComplete;
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
//
|
|
// Non-abortive disconnects can use the generic cancellation and
|
|
// completion routines.
|
|
//
|
|
status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
|
|
request.RequestNotifyObject = TCPRequestComplete;
|
|
}
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CLOSE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPDisconnect "
|
|
"irp %lx, flags %lx, fileobj %lx, abortive = %d\n",
|
|
Irp, disconnectRequest->RequestFlags, IrpSp->FileObject,
|
|
abortive));
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
status = TdiDisconnect(&request,((millisecondTimeout.LowPart != 0) ?
|
|
&(millisecondTimeout.LowPart) : NULL),
|
|
(ushort) disconnectRequest->RequestFlags,
|
|
requestInformation, returnInformation);
|
|
|
|
if (status != STATUS_PENDING) {
|
|
if (abortive) {
|
|
TCPNonCancellableRequestComplete(Irp, status, 0);
|
|
} else {
|
|
TCPRequestComplete(Irp, status, 0);
|
|
}
|
|
} else {
|
|
IF_TCPDBG(TCP_DEBUG_CLOSE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPDisconnect pending irp %lx\n", Irp));
|
|
}
|
|
}
|
|
//
|
|
// return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
|
|
//
|
|
return(STATUS_PENDING);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // TCPDisconnect
|
|
|
|
|
|
//* TCPListen - Handler for TDI Listen IRP.
|
|
//
|
|
// Converts a TDI Listen IRP into a call to TdiListen.
|
|
//
|
|
NTSTATUS // Returns: whether or not the request was successful.
|
|
TCPListen(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PTCP_CONTEXT tcpContext;
|
|
TDI_REQUEST request;
|
|
PTDI_CONNECTION_INFORMATION requestInformation, returnInformation;
|
|
PTDI_REQUEST_KERNEL_LISTEN listenRequest;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CONNECT) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPListen irp %lx on file object %lx\n",
|
|
Irp, IrpSp->FileObject));
|
|
}
|
|
|
|
ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE);
|
|
|
|
listenRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(IrpSp->Parameters);
|
|
requestInformation = listenRequest->RequestConnectionInformation;
|
|
returnInformation = listenRequest->ReturnConnectionInformation;
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
|
|
request.RequestNotifyObject = TCPRequestComplete;
|
|
request.RequestContext = Irp;
|
|
|
|
status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = TdiListen(&request, (ushort) listenRequest->RequestFlags,
|
|
requestInformation, returnInformation);
|
|
|
|
if (status != TDI_PENDING) {
|
|
TCPRequestComplete(Irp, status, 0);
|
|
}
|
|
//
|
|
// return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING
|
|
//
|
|
return(TDI_PENDING);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // TCPListen
|
|
|
|
|
|
//* TCPAccept - Handle a TDI Accept IRP.
|
|
//
|
|
// Converts a TDI Accept IRP into a call to TdiAccept.
|
|
//
|
|
NTSTATUS // Returns: whether the request was successfully queued.
|
|
TCPAccept(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
NTSTATUS status;
|
|
PTCP_CONTEXT tcpContext;
|
|
TDI_REQUEST request;
|
|
PTDI_CONNECTION_INFORMATION requestInformation, returnInformation;
|
|
PTDI_REQUEST_KERNEL_ACCEPT acceptRequest;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CONNECT) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPAccept irp %lx on file object %lx\n", Irp,
|
|
IrpSp->FileObject));
|
|
}
|
|
|
|
ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE);
|
|
|
|
acceptRequest = (PTDI_REQUEST_KERNEL_ACCEPT) &(IrpSp->Parameters);
|
|
requestInformation = acceptRequest->RequestConnectionInformation;
|
|
returnInformation = acceptRequest->ReturnConnectionInformation;
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
|
|
request.RequestNotifyObject = TCPRequestComplete;
|
|
request.RequestContext = Irp;
|
|
|
|
status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = TdiAccept(&request, requestInformation, returnInformation);
|
|
|
|
if (status != TDI_PENDING) {
|
|
TCPRequestComplete(Irp, status, 0);
|
|
}
|
|
//
|
|
// Return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING.
|
|
//
|
|
return(TDI_PENDING);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // TCPAccept
|
|
|
|
|
|
//* TCPSendData - Handle TDI Send IRP.
|
|
//
|
|
// Converts a TDI Send IRP into a call to TdiSend.
|
|
//
|
|
NTSTATUS
|
|
TCPSendData(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
TDI_STATUS status;
|
|
TDI_REQUEST request;
|
|
PTCP_CONTEXT tcpContext;
|
|
PTDI_REQUEST_KERNEL_SEND requestInformation;
|
|
KIRQL oldIrql;
|
|
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE);
|
|
requestInformation = (PTDI_REQUEST_KERNEL_SEND) &(IrpSp->Parameters);
|
|
|
|
request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
|
|
request.RequestNotifyObject = TCPDataRequestComplete;
|
|
request.RequestContext = Irp;
|
|
|
|
KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql);
|
|
IoSetCancelRoutine(Irp, TCPCancelRequest);
|
|
|
|
if (!Irp->Cancel) {
|
|
//
|
|
// Set up for cancellation.
|
|
//
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
tcpContext->ReferenceCount++;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_IRP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPSendData: irp %lx fileobj %lx refcnt inc to %u\n",
|
|
Irp, IrpSp, tcpContext->ReferenceCount));
|
|
}
|
|
|
|
#if DBG
|
|
IF_TCPDBG(TCP_DEBUG_CANCEL) {
|
|
PLIST_ENTRY entry;
|
|
PIRP item = NULL;
|
|
|
|
//
|
|
// Verify that the Irp has not already been submitted.
|
|
//
|
|
for (entry = tcpContext->PendingIrpList.Flink;
|
|
entry != &(tcpContext->PendingIrpList);
|
|
entry = entry->Flink) {
|
|
|
|
item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
ASSERT(item != Irp);
|
|
}
|
|
|
|
for (entry = tcpContext->CancelledIrpList.Flink;
|
|
entry != &(tcpContext->CancelledIrpList);
|
|
entry = entry->Flink) {
|
|
|
|
item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
ASSERT(item != Irp);
|
|
}
|
|
|
|
InsertTailList(&(tcpContext->PendingIrpList),
|
|
&(Irp->Tail.Overlay.ListEntry));
|
|
}
|
|
#endif // DBG
|
|
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
|
|
IF_TCPDBG(TCP_DEBUG_SEND) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPSendData irp %lx sending %d bytes, flags %lx,"
|
|
" fileobj %lx\n", Irp, requestInformation->SendLength,
|
|
requestInformation->SendFlags, IrpSp->FileObject));
|
|
}
|
|
|
|
status = TdiSend(&request, (ushort) requestInformation->SendFlags,
|
|
requestInformation->SendLength,
|
|
(PNDIS_BUFFER) Irp->MdlAddress);
|
|
|
|
if (status == TDI_PENDING) {
|
|
IF_TCPDBG(TCP_DEBUG_SEND) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPSendData pending irp %lx\n", Irp));
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
//
|
|
// The status is not pending. We reset the pending bit
|
|
//
|
|
IrpSp->Control &= ~SL_PENDING_RETURNED;
|
|
|
|
if (status == TDI_SUCCESS) {
|
|
ASSERT(requestInformation->SendLength == 0);
|
|
|
|
status = TCPDataRequestComplete(Irp, status,
|
|
requestInformation->SendLength);
|
|
} else {
|
|
|
|
IF_TCPDBG(TCP_DEBUG_SEND) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPSendData - irp %lx send failed, status %lx\n",
|
|
Irp, status));
|
|
}
|
|
|
|
status = TCPDataRequestComplete(Irp, status, 0);
|
|
}
|
|
} else {
|
|
//
|
|
// Irp was cancelled previously.
|
|
//
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
|
|
//
|
|
// Ensure that the cancel-routine has executed.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&oldIrql);
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
|
|
KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
|
|
IF_TCPDBG(TCP_DEBUG_SEND) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPSendData: Irp %lx on fileobj %lx was cancelled\n",
|
|
Irp, IrpSp->FileObject));
|
|
}
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // TCPSendData
|
|
|
|
|
|
//* TCPReceiveData - Handler for TDI Receive IRP.
|
|
//
|
|
// Converts a TDI Receive IRP into a call to TdiReceive.
|
|
//
|
|
NTSTATUS // Returns: whether the request was successful.
|
|
TCPReceiveData(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
TDI_STATUS status;
|
|
TDI_REQUEST request;
|
|
PTCP_CONTEXT tcpContext;
|
|
PTDI_REQUEST_KERNEL_RECEIVE requestInformation;
|
|
KIRQL oldIrql;
|
|
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE);
|
|
requestInformation = (PTDI_REQUEST_KERNEL_RECEIVE) &(IrpSp->Parameters);
|
|
|
|
request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
|
|
request.RequestNotifyObject = TCPDataRequestComplete;
|
|
request.RequestContext = Irp;
|
|
|
|
KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql);
|
|
IoSetCancelRoutine(Irp, TCPCancelRequest);
|
|
|
|
if (!Irp->Cancel) {
|
|
//
|
|
// Set up for cancellation.
|
|
//
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
tcpContext->ReferenceCount++;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_IRP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPReceiveData: irp %lx fileobj %lx refcnt inc to %u\n",
|
|
Irp, IrpSp->FileObject, tcpContext->ReferenceCount));
|
|
}
|
|
|
|
#if DBG
|
|
IF_TCPDBG(TCP_DEBUG_CANCEL) {
|
|
PLIST_ENTRY entry;
|
|
PIRP item = NULL;
|
|
|
|
//
|
|
// Verify that the Irp has not already been submitted.
|
|
//
|
|
for (entry = tcpContext->PendingIrpList.Flink;
|
|
entry != &(tcpContext->PendingIrpList);
|
|
entry = entry->Flink) {
|
|
|
|
item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
ASSERT(item != Irp);
|
|
}
|
|
|
|
for (entry = tcpContext->CancelledIrpList.Flink;
|
|
entry != &(tcpContext->CancelledIrpList);
|
|
entry = entry->Flink) {
|
|
|
|
item = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
ASSERT(item != Irp);
|
|
}
|
|
|
|
InsertTailList(&(tcpContext->PendingIrpList),
|
|
&(Irp->Tail.Overlay.ListEntry));
|
|
}
|
|
#endif // DBG
|
|
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
|
|
IF_TCPDBG(TCP_DEBUG_RECEIVE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPReceiveData irp %lx receiving %d bytes flags %lx"
|
|
" filobj %lx\n", Irp, requestInformation->ReceiveLength,
|
|
requestInformation->ReceiveFlags, IrpSp->FileObject));
|
|
}
|
|
|
|
status = TdiReceive(&request,
|
|
(ushort *) &(requestInformation->ReceiveFlags),
|
|
&(requestInformation->ReceiveLength),
|
|
(PNDIS_BUFFER) Irp->MdlAddress);
|
|
|
|
if (status == TDI_PENDING) {
|
|
IF_TCPDBG(TCP_DEBUG_RECEIVE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPReceiveData: pending irp %lx\n", Irp));
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
//
|
|
// The status is not pending. We reset the pending bit
|
|
//
|
|
IrpSp->Control &= ~SL_PENDING_RETURNED;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_RECEIVE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPReceiveData - irp %lx failed, status %lx\n",
|
|
Irp, status));
|
|
}
|
|
|
|
status = TCPDataRequestComplete(Irp, status, 0);
|
|
} else {
|
|
//
|
|
// Irp was cancelled previously.
|
|
//
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
|
|
//
|
|
// Ensure that the cancel-routine has executed.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&oldIrql);
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
|
|
KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
|
|
IF_TCPDBG(TCP_DEBUG_SEND) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPReceiveData: Irp %lx on fileobj %lx was cancelled\n",
|
|
Irp, IrpSp->FileObject));
|
|
}
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // TCPReceiveData
|
|
|
|
|
|
//* UDPSendDatagram -
|
|
//
|
|
NTSTATUS // Returns: whether the request was successfully queued.
|
|
UDPSendDatagram(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
TDI_STATUS status;
|
|
TDI_REQUEST request;
|
|
PTCP_CONTEXT tcpContext;
|
|
PTDI_REQUEST_KERNEL_SENDDG datagramInformation;
|
|
ULONG bytesSent = 0;
|
|
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
datagramInformation = (PTDI_REQUEST_KERNEL_SENDDG) &(IrpSp->Parameters);
|
|
ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) ==
|
|
TDI_TRANSPORT_ADDRESS_FILE);
|
|
|
|
request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
|
|
request.RequestNotifyObject = TCPDataRequestComplete;
|
|
request.RequestContext = Irp;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_SEND_DGRAM) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"UDPSendDatagram irp %lx sending %d bytes\n", Irp,
|
|
datagramInformation->SendLength));
|
|
}
|
|
|
|
status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = TdiSendDatagram(&request,
|
|
datagramInformation->SendDatagramInformation,
|
|
datagramInformation->SendLength, &bytesSent,
|
|
(PNDIS_BUFFER) Irp->MdlAddress);
|
|
|
|
if (status == TDI_PENDING) {
|
|
return(status);
|
|
}
|
|
|
|
ASSERT(status != TDI_SUCCESS);
|
|
ASSERT(bytesSent == 0);
|
|
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
|
|
"UDPSendDatagram - irp %lx send failed, status %lx\n",
|
|
Irp, status));
|
|
|
|
TCPDataRequestComplete(Irp, status, bytesSent);
|
|
//
|
|
// Return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING.
|
|
//
|
|
return(TDI_PENDING);
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UDPSendDatagram
|
|
|
|
|
|
//* UDPReceiveDatagram - Handle TDI ReceiveDatagram IRP.
|
|
//
|
|
// Converts a TDI ReceiveDatagram IRP into a call to TdiReceiveDatagram.
|
|
//
|
|
NTSTATUS // Returns: whether the request was successful.
|
|
UDPReceiveDatagram(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
TDI_STATUS status;
|
|
TDI_REQUEST request;
|
|
PTCP_CONTEXT tcpContext;
|
|
PTDI_REQUEST_KERNEL_RECEIVEDG datagramInformation;
|
|
uint bytesReceived = 0;
|
|
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
datagramInformation = (PTDI_REQUEST_KERNEL_RECEIVEDG) &(IrpSp->Parameters);
|
|
ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) ==
|
|
TDI_TRANSPORT_ADDRESS_FILE);
|
|
|
|
request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
|
|
request.RequestNotifyObject = TCPDataRequestComplete;
|
|
request.RequestContext = Irp;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_RECEIVE_DGRAM) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"UDPReceiveDatagram: irp %lx receiveing %d bytes\n", Irp,
|
|
datagramInformation->ReceiveLength));
|
|
}
|
|
|
|
status = TCPPrepareIrpForCancel(tcpContext, Irp, TCPCancelRequest);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = TdiReceiveDatagram(&request,
|
|
datagramInformation->ReceiveDatagramInformation,
|
|
datagramInformation->ReturnDatagramInformation,
|
|
datagramInformation->ReceiveLength, &bytesReceived,
|
|
Irp->MdlAddress);
|
|
|
|
if (status == TDI_PENDING) {
|
|
return(status);
|
|
}
|
|
|
|
ASSERT(status != TDI_SUCCESS);
|
|
ASSERT(bytesReceived == 0);
|
|
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
|
|
"UDPReceiveDatagram: irp %lx send failed, status %lx\n",
|
|
Irp, status));
|
|
|
|
TCPDataRequestComplete(Irp, status, bytesReceived);
|
|
//
|
|
// Return PENDING because TCPPrepareIrpForCancel marks Irp as PENDING.
|
|
//
|
|
return(TDI_PENDING);
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UDPReceiveDatagram
|
|
|
|
|
|
//* TCPSetEventHandler - Handle TDI SetEventHandler IRP.
|
|
//
|
|
// Converts a TDI SetEventHandler IRP into a call to TdiSetEventHandler.
|
|
//
|
|
NTSTATUS // Returns: whether the request was successful.
|
|
TCPSetEventHandler(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
NTSTATUS status;
|
|
PTDI_REQUEST_KERNEL_SET_EVENT event;
|
|
PTCP_CONTEXT tcpContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
event = (PTDI_REQUEST_KERNEL_SET_EVENT) &(IrpSp->Parameters);
|
|
|
|
IF_TCPDBG(TCP_DEBUG_EVENT_HANDLER) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPSetEventHandler: "
|
|
"irp %lx event %lx handler %lx context %lx\n", Irp,
|
|
event->EventType, event->EventHandler, event->EventContext));
|
|
}
|
|
|
|
status = TdiSetEvent(tcpContext->Handle.AddressHandle, event->EventType,
|
|
event->EventHandler, event->EventContext);
|
|
|
|
ASSERT(status != TDI_PENDING);
|
|
|
|
return(status);
|
|
|
|
} // TCPSetEventHandler
|
|
|
|
|
|
//* TCPQueryInformation - Handle a TDI QueryInformation IRP.
|
|
//
|
|
// Converts a TDI QueryInformation IRP into a call to TdiQueryInformation.
|
|
//
|
|
NTSTATUS // Returns: whether request was successful.
|
|
TCPQueryInformation(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
TDI_REQUEST request;
|
|
TDI_STATUS status = STATUS_SUCCESS;
|
|
PTCP_CONTEXT tcpContext;
|
|
PTDI_REQUEST_KERNEL_QUERY_INFORMATION queryInformation;
|
|
uint isConn = FALSE;
|
|
uint dataSize = 0;
|
|
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
queryInformation = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)
|
|
&(IrpSp->Parameters);
|
|
|
|
request.RequestNotifyObject = TCPDataRequestComplete;
|
|
request.RequestContext = Irp;
|
|
|
|
switch(queryInformation->QueryType) {
|
|
|
|
case TDI_QUERY_BROADCAST_ADDRESS:
|
|
ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) ==
|
|
TDI_CONTROL_CHANNEL_FILE);
|
|
request.Handle.ControlChannel = tcpContext->Handle.ControlChannel;
|
|
break;
|
|
|
|
case TDI_QUERY_PROVIDER_INFO:
|
|
//
|
|
// NetBT does this. Reinstate the ASSERT when it is fixed.
|
|
//
|
|
// ASSERT(PtrToUlong(IrpSp->FileObject->FsContext2) ==
|
|
// TDI_CONTROL_CHANNEL_FILE);
|
|
request.Handle.ControlChannel = tcpContext->Handle.ControlChannel;
|
|
break;
|
|
|
|
case TDI_QUERY_ADDRESS_INFO:
|
|
if (PtrToUlong(IrpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) {
|
|
//
|
|
// This is a TCP connection object.
|
|
//
|
|
isConn = TRUE;
|
|
request.Handle.ConnectionContext =
|
|
tcpContext->Handle.ConnectionContext;
|
|
} else {
|
|
//
|
|
// This is an address object.
|
|
//
|
|
request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
|
|
}
|
|
break;
|
|
|
|
case TDI_QUERY_CONNECTION_INFO:
|
|
|
|
if (PtrToUlong(IrpSp->FileObject->FsContext2) != TDI_CONNECTION_FILE){
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
isConn = TRUE;
|
|
request.Handle.ConnectionContext = tcpContext->Handle.ConnectionContext;
|
|
}
|
|
break;
|
|
|
|
case TDI_QUERY_PROVIDER_STATISTICS:
|
|
request.Handle.ControlChannel = tcpContext->Handle.ControlChannel;
|
|
break;
|
|
|
|
default:
|
|
status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// This request isn't cancellable, but we put it through
|
|
// the cancel path because it handles some checks for us
|
|
// and tracks the irp.
|
|
//
|
|
status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
dataSize = TCPGetMdlChainByteCount(Irp->MdlAddress);
|
|
|
|
status = TdiQueryInformation(&request, queryInformation->QueryType,
|
|
Irp->MdlAddress, &dataSize, isConn);
|
|
|
|
if (status != TDI_PENDING) {
|
|
IrpSp->Control &= ~SL_PENDING_RETURNED;
|
|
status = TCPDataRequestComplete(Irp, status, dataSize);
|
|
return(status);
|
|
}
|
|
|
|
return(STATUS_PENDING);
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
Irp->IoStatus.Status = (NTSTATUS) status;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return(status);
|
|
|
|
} // TCPQueryInformation
|
|
|
|
|
|
//* TCPQueryInformationExComplete - Complete a TdiQueryInformationEx request.
|
|
//
|
|
NTSTATUS
|
|
TCPQueryInformationExComplete(
|
|
void *Context, // A pointer to the IRP for this request.
|
|
unsigned int Status, // Final TDI status of the request.
|
|
unsigned int ByteCount) // Bytes returned in output buffer.
|
|
{
|
|
PTCP_QUERY_CONTEXT queryContext = (PTCP_QUERY_CONTEXT) Context;
|
|
ULONG bytesCopied;
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// Copy the returned context to the input buffer.
|
|
//
|
|
TdiCopyBufferToMdl(&(queryContext->QueryInformation.Context), 0,
|
|
CONTEXT_SIZE, queryContext->InputMdl,
|
|
FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX,
|
|
Context),
|
|
&bytesCopied);
|
|
|
|
if (bytesCopied != CONTEXT_SIZE) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ByteCount = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Unlock the user's buffers and free the MDLs describing them.
|
|
//
|
|
MmUnlockPages(queryContext->InputMdl);
|
|
IoFreeMdl(queryContext->InputMdl);
|
|
MmUnlockPages(queryContext->OutputMdl);
|
|
IoFreeMdl(queryContext->OutputMdl);
|
|
|
|
//
|
|
// Complete the request.
|
|
//
|
|
Status = TCPDataRequestComplete(queryContext->Irp, Status, ByteCount);
|
|
|
|
ExFreePool(queryContext);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//* TCPQueryInformationEx - Handle a TDI QueryInformationEx IRP.
|
|
//
|
|
// Converts a TDI QueryInformationEx IRP into a call to TdiQueryInformationEx.
|
|
//
|
|
NTSTATUS // Returns: whether the request was successful.
|
|
TCPQueryInformationEx(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
TDI_REQUEST request;
|
|
TDI_STATUS status = STATUS_SUCCESS;
|
|
PTCP_CONTEXT tcpContext;
|
|
uint size;
|
|
PTCP_REQUEST_QUERY_INFORMATION_EX InputBuffer;
|
|
PVOID OutputBuffer;
|
|
PMDL inputMdl = NULL;
|
|
PMDL outputMdl = NULL;
|
|
ULONG InputBufferLength, OutputBufferLength;
|
|
PTCP_QUERY_CONTEXT queryContext;
|
|
BOOLEAN inputLocked = FALSE;
|
|
BOOLEAN outputLocked = FALSE;
|
|
BOOLEAN inputBufferValid = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
IF_TCPDBG(TCP_DEBUG_INFO) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"QueryInformationEx starting - irp %lx fileobj %lx\n",
|
|
Irp, IrpSp->FileObject));
|
|
}
|
|
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
|
|
switch (PtrToUlong(IrpSp->FileObject->FsContext2)) {
|
|
|
|
case TDI_TRANSPORT_ADDRESS_FILE:
|
|
request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
|
|
break;
|
|
|
|
case TDI_CONNECTION_FILE:
|
|
request.Handle.ConnectionContext =
|
|
tcpContext->Handle.ConnectionContext;
|
|
break;
|
|
|
|
case TDI_CONTROL_CHANNEL_FILE:
|
|
request.Handle.ControlChannel = tcpContext->Handle.ControlChannel;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// Validate the input parameters.
|
|
//
|
|
if (InputBufferLength >= sizeof(TCP_REQUEST_QUERY_INFORMATION_EX) &&
|
|
InputBufferLength < MAXLONG) {
|
|
inputBufferValid = TRUE;
|
|
} else {
|
|
inputBufferValid = FALSE;
|
|
}
|
|
if (inputBufferValid && (OutputBufferLength != 0)) {
|
|
|
|
OutputBuffer = Irp->UserBuffer;
|
|
InputBuffer = (PTCP_REQUEST_QUERY_INFORMATION_EX)
|
|
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
queryContext = ExAllocatePool(NonPagedPool, InputBufferLength
|
|
+ FIELD_OFFSET(TCP_QUERY_CONTEXT, QueryInformation));
|
|
|
|
if (queryContext != NULL) {
|
|
status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(queryContext);
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Allocate Mdls to describe the input and output buffers.
|
|
// Probe and lock the buffers.
|
|
//
|
|
try {
|
|
inputMdl = IoAllocateMdl(InputBuffer, InputBufferLength,
|
|
FALSE, TRUE, NULL);
|
|
|
|
outputMdl = IoAllocateMdl(OutputBuffer, OutputBufferLength,
|
|
FALSE, TRUE, NULL);
|
|
|
|
if ((inputMdl != NULL) && (outputMdl != NULL)) {
|
|
|
|
MmProbeAndLockPages(inputMdl, Irp->RequestorMode,
|
|
IoModifyAccess);
|
|
|
|
inputLocked = TRUE;
|
|
|
|
MmProbeAndLockPages(outputMdl, Irp->RequestorMode,
|
|
IoWriteAccess);
|
|
|
|
outputLocked = TRUE;
|
|
|
|
//
|
|
// Copy the input parameter to our pool block so
|
|
// TdiQueryInformationEx can manipulate it directly.
|
|
//
|
|
RtlCopyMemory(&(queryContext->QueryInformation),
|
|
InputBuffer,
|
|
InputBufferLength);
|
|
} else {
|
|
|
|
IF_TCPDBG(TCP_DEBUG_INFO) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"QueryInfoEx: Couldn't allocate MDL\n"));
|
|
}
|
|
|
|
IrpSp->Control &= ~SL_PENDING_RETURNED;
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
IF_TCPDBG(TCP_DEBUG_INFO) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"QueryInfoEx: "
|
|
"exception copying input param %lx\n",
|
|
GetExceptionCode()));
|
|
}
|
|
|
|
status = GetExceptionCode();
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// It's finally time to do this thing.
|
|
//
|
|
size = TCPGetMdlChainByteCount(outputMdl);
|
|
|
|
queryContext->Irp = Irp;
|
|
queryContext->InputMdl = inputMdl;
|
|
queryContext->OutputMdl = outputMdl;
|
|
|
|
request.RequestNotifyObject = TCPQueryInformationExComplete;
|
|
request.RequestContext = queryContext;
|
|
|
|
status = TdiQueryInformationEx(&request,
|
|
&(queryContext->QueryInformation.ID),
|
|
outputMdl, &size,
|
|
&(queryContext->QueryInformation.Context),
|
|
InputBufferLength - FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX, Context));
|
|
|
|
if (status != TDI_PENDING) {
|
|
|
|
//
|
|
// Since status is not pending, clear the
|
|
// control flag to keep IO verifier happy.
|
|
//
|
|
IrpSp->Control &= ~SL_PENDING_RETURNED;
|
|
|
|
TCPQueryInformationExComplete(queryContext, status, size);
|
|
return(status);
|
|
}
|
|
|
|
IF_TCPDBG(TCP_DEBUG_INFO) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"QueryInformationEx - "
|
|
"pending irp %lx fileobj %lx\n",
|
|
Irp, IrpSp->FileObject));
|
|
}
|
|
|
|
return(STATUS_PENDING);
|
|
}
|
|
|
|
//
|
|
// If we get here, something failed. Clean up.
|
|
//
|
|
if (inputMdl != NULL) {
|
|
if (inputLocked) {
|
|
MmUnlockPages(inputMdl);
|
|
}
|
|
|
|
IoFreeMdl(inputMdl);
|
|
}
|
|
|
|
if (outputMdl != NULL) {
|
|
if (outputLocked) {
|
|
MmUnlockPages(outputMdl);
|
|
}
|
|
|
|
IoFreeMdl(outputMdl);
|
|
}
|
|
|
|
ExFreePool(queryContext);
|
|
|
|
// Since status is not pending, clear the
|
|
// control flag to keep IO verifier happy.
|
|
|
|
IrpSp->Control &= ~SL_PENDING_RETURNED;
|
|
|
|
status = TCPDataRequestComplete(Irp, status, 0);
|
|
|
|
return(status);
|
|
|
|
} else {
|
|
IrpSp->Control &= ~SL_PENDING_RETURNED;
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_INFO) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"QueryInfoEx: Unable to allocate query context\n"));
|
|
}
|
|
}
|
|
} else {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_INFO) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"QueryInfoEx: Bad buffer len, OBufLen %d, InBufLen %d\n",
|
|
OutputBufferLength, InputBufferLength));
|
|
}
|
|
}
|
|
|
|
IF_TCPDBG(TCP_DEBUG_INFO) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"QueryInformationEx complete - irp %lx, status %lx\n",
|
|
Irp, status));
|
|
}
|
|
|
|
Irp->IoStatus.Status = (NTSTATUS) status;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
//* TCPSetInformationEx - Handle TDI SetInformationEx IRP.
|
|
//
|
|
// Converts a TDI SetInformationEx IRP into a call to TdiSetInformationEx.
|
|
//
|
|
// This routine does not pend.
|
|
//
|
|
NTSTATUS // Returns: whether the request was successful.
|
|
TCPSetInformationEx(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
TDI_REQUEST request;
|
|
TDI_STATUS status;
|
|
PTCP_CONTEXT tcpContext;
|
|
PTCP_REQUEST_SET_INFORMATION_EX setInformation;
|
|
|
|
PAGED_CODE();
|
|
|
|
IF_TCPDBG(TCP_DEBUG_INFO) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"SetInformationEx - irp %lx fileobj %lx\n", Irp,
|
|
IrpSp->FileObject));
|
|
}
|
|
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
setInformation = (PTCP_REQUEST_SET_INFORMATION_EX)
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
FIELD_OFFSET(TCP_REQUEST_SET_INFORMATION_EX, Buffer) ||
|
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength -
|
|
FIELD_OFFSET(TCP_REQUEST_SET_INFORMATION_EX, Buffer) < setInformation->BufferSize) {
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return (STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
switch (PtrToUlong(IrpSp->FileObject->FsContext2)) {
|
|
|
|
case TDI_TRANSPORT_ADDRESS_FILE:
|
|
request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
|
|
break;
|
|
|
|
case TDI_CONNECTION_FILE:
|
|
request.Handle.ConnectionContext =
|
|
tcpContext->Handle.ConnectionContext;
|
|
break;
|
|
|
|
case TDI_CONTROL_CHANNEL_FILE:
|
|
request.Handle.ControlChannel = tcpContext->Handle.ControlChannel;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Prevent non-privleged access (i.e. IOCTL_TCP_WSH_SET_INFORMATION_EX
|
|
// calls) from making changes outside of the transport layer.
|
|
// Privleged calls should be using IOCTL_TCP_SET_INFORMATION_EX instead.
|
|
//
|
|
if (IrpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_TCP_WSH_SET_INFORMATION_EX) {
|
|
uint Entity;
|
|
|
|
Entity = setInformation->ID.toi_entity.tei_entity;
|
|
|
|
if ((Entity != CO_TL_ENTITY) && (Entity != CL_TL_ENTITY) ) {
|
|
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
return (STATUS_ACCESS_DENIED);
|
|
}
|
|
}
|
|
|
|
status = TCPPrepareIrpForCancel(tcpContext, Irp, NULL);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
request.RequestNotifyObject = TCPDataRequestComplete;
|
|
request.RequestContext = Irp;
|
|
|
|
status = TdiSetInformationEx(&request, &(setInformation->ID),
|
|
&(setInformation->Buffer[0]),
|
|
setInformation->BufferSize);
|
|
|
|
if (status != TDI_PENDING) {
|
|
IrpSp->Control &= ~SL_PENDING_RETURNED;
|
|
|
|
status = TCPDataRequestComplete(Irp, status, 0);
|
|
|
|
return(status);
|
|
}
|
|
|
|
IF_TCPDBG(TCP_DEBUG_INFO) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"SetInformationEx - pending irp %lx fileobj %lx\n",
|
|
Irp, IrpSp->FileObject));
|
|
}
|
|
|
|
return(STATUS_PENDING);
|
|
}
|
|
|
|
IF_TCPDBG(TCP_DEBUG_INFO) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"SetInformationEx complete - irp %lx\n", Irp));
|
|
}
|
|
|
|
//
|
|
// The irp has already been completed.
|
|
//
|
|
return(status);
|
|
}
|
|
|
|
|
|
#if 0
|
|
//* TCPEnumerateConnectionList -
|
|
//
|
|
// Processes a request to enumerate the workstation connection list.
|
|
//
|
|
// This routine does not pend.
|
|
//
|
|
NTSTATUS // Return: whether the request was successful.
|
|
TCPEnumerateConnectionList(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
|
|
TCPConnectionListEntry *request;
|
|
TCPConnectionListEnum *response;
|
|
ULONG requestLength, responseLength;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
request = (TCPConnectionListEntry *) Irp->AssociatedIrp.SystemBuffer;
|
|
response = (TCPConnectionListEnum *) request;
|
|
requestLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
responseLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
if (responseLength < sizeof(TCPConnectionListEnum)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
Irp->IoStatus.Information = 0;
|
|
} else {
|
|
EnumerateConnectionList((uchar *) (response + 1),
|
|
responseLength - sizeof(TCPConnectionListEnum),
|
|
&(response->tce_entries_returned),
|
|
&(response->tce_entries_available));
|
|
|
|
status = TDI_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(TCPConnectionListEnum) +
|
|
(response->tce_entries_returned * sizeof(TCPConnectionListEntry));
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return(status);
|
|
}
|
|
#endif
|
|
|
|
|
|
//* TCPCreate -
|
|
//
|
|
NTSTATUS // Returns: whether the request was successfully queued.
|
|
TCPCreate(
|
|
IN PDEVICE_OBJECT DeviceObject, // Device object for this request.
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
TDI_REQUEST Request;
|
|
NTSTATUS status;
|
|
FILE_FULL_EA_INFORMATION *ea;
|
|
FILE_FULL_EA_INFORMATION UNALIGNED *targetEA;
|
|
PTCP_CONTEXT tcpContext;
|
|
uint protocol;
|
|
|
|
PAGED_CODE();
|
|
|
|
tcpContext = ExAllocatePool(NonPagedPool, sizeof(TCP_CONTEXT));
|
|
|
|
if (tcpContext == NULL) {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
#if DBG
|
|
InitializeListHead(&(tcpContext->PendingIrpList));
|
|
InitializeListHead(&(tcpContext->CancelledIrpList));
|
|
#endif
|
|
|
|
tcpContext->ReferenceCount = 1; // Put initial reference on open object.
|
|
tcpContext->CancelIrps = FALSE;
|
|
KeInitializeEvent(&(tcpContext->CleanupEvent), SynchronizationEvent,
|
|
FALSE);
|
|
KeInitializeSpinLock(&tcpContext->EndpointLock);
|
|
|
|
ea = (PFILE_FULL_EA_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// See if this is a Control Channel open.
|
|
//
|
|
if (!ea) {
|
|
IF_TCPDBG(TCP_DEBUG_OPEN) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCreate: "
|
|
"Opening control channel for file object %lx\n",
|
|
IrpSp->FileObject));
|
|
}
|
|
|
|
tcpContext->Handle.ControlChannel = NULL;
|
|
IrpSp->FileObject->FsContext = tcpContext;
|
|
IrpSp->FileObject->FsContext2 = (PVOID) TDI_CONTROL_CHANNEL_FILE;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// See if this is an Address Object open.
|
|
//
|
|
targetEA = FindEA(ea, TdiTransportAddress, TDI_TRANSPORT_ADDRESS_LENGTH);
|
|
|
|
if (targetEA != NULL) {
|
|
UCHAR optionsBuffer[3];
|
|
PUCHAR optionsPointer = optionsBuffer;
|
|
|
|
if (DeviceObject == TCPDeviceObject) {
|
|
protocol = IP_PROTOCOL_TCP;
|
|
}
|
|
else if (DeviceObject == UDPDeviceObject) {
|
|
protocol = IP_PROTOCOL_UDP;
|
|
|
|
ASSERT(optionsPointer - optionsBuffer <= 3);
|
|
|
|
if (IsDHCPZeroAddress((TRANSPORT_ADDRESS UNALIGNED *)
|
|
&(targetEA->EaName[targetEA->EaNameLength + 1]))) {
|
|
|
|
if (!IsAdminIoRequest(Irp, IrpSp)) {
|
|
ExFreePool(tcpContext);
|
|
return(STATUS_ACCESS_DENIED);
|
|
}
|
|
|
|
*optionsPointer = TDI_ADDRESS_OPTION_DHCP;
|
|
optionsPointer++;
|
|
}
|
|
|
|
ASSERT(optionsPointer - optionsBuffer <= 3);
|
|
} else {
|
|
//
|
|
// This is a raw ip open.
|
|
//
|
|
|
|
//
|
|
// Only administrators can create raw addresses
|
|
// unless this is allowed through registry.
|
|
//
|
|
if (!AllowUserRawAccess && !IsAdminIoRequest(Irp, IrpSp)) {
|
|
ExFreePool(tcpContext);
|
|
return(STATUS_ACCESS_DENIED);
|
|
}
|
|
|
|
protocol = RawExtractProtocolNumber(
|
|
&(IrpSp->FileObject->FileName));
|
|
|
|
//
|
|
// We need to protect the IPv6Send routine's packet rewriting
|
|
// code (for fragmentation, header inclusion, etc) from getting
|
|
// confused by malformed extension headers coming from user-land.
|
|
// So we disallow these types.
|
|
//
|
|
if ((protocol == 0xFFFFFFFF) ||
|
|
IsExtensionHeader((uchar)protocol)) {
|
|
ExFreePool(tcpContext);
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
*optionsPointer = TDI_ADDRESS_OPTION_RAW;
|
|
optionsPointer++;
|
|
}
|
|
|
|
if ((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) ||
|
|
(IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) {
|
|
|
|
*optionsPointer = TDI_ADDRESS_OPTION_REUSE;
|
|
optionsPointer++;
|
|
}
|
|
|
|
*optionsPointer = TDI_OPTION_EOL;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_OPEN) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCreate: Opening address for file object %lx\n",
|
|
IrpSp->FileObject));
|
|
}
|
|
|
|
status = TdiOpenAddress(&Request, (TRANSPORT_ADDRESS UNALIGNED *)
|
|
&(targetEA->EaName[targetEA->EaNameLength + 1]),
|
|
protocol, optionsBuffer);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Save off the handle to the AO passed back.
|
|
//
|
|
tcpContext->Handle.AddressHandle = Request.Handle.AddressHandle;
|
|
IrpSp->FileObject->FsContext = tcpContext;
|
|
IrpSp->FileObject->FsContext2 = (PVOID) TDI_TRANSPORT_ADDRESS_FILE;
|
|
} else {
|
|
ExFreePool(tcpContext);
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
|
|
"TdiOpenAddress failed, status %lx\n", status));
|
|
if (status == STATUS_ADDRESS_ALREADY_EXISTS) {
|
|
status = STATUS_SHARING_VIOLATION;
|
|
}
|
|
}
|
|
|
|
ASSERT(status != TDI_PENDING);
|
|
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// See if this is a Connection Object open.
|
|
//
|
|
targetEA = FindEA(ea, TdiConnectionContext, TDI_CONNECTION_CONTEXT_LENGTH);
|
|
|
|
if (targetEA != NULL) {
|
|
//
|
|
// This is an open of a Connection Object.
|
|
//
|
|
|
|
if (DeviceObject == TCPDeviceObject) {
|
|
|
|
IF_TCPDBG(TCP_DEBUG_OPEN) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCreate: Opening connection for file object %lx\n",
|
|
IrpSp->FileObject));
|
|
}
|
|
|
|
if (targetEA->EaValueLength < sizeof(CONNECTION_CONTEXT)) {
|
|
status = STATUS_EA_LIST_INCONSISTENT;
|
|
} else {
|
|
status = TdiOpenConnection(&Request,
|
|
*((CONNECTION_CONTEXT UNALIGNED *)
|
|
&(targetEA->EaName[targetEA->EaNameLength + 1])));
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Save off the Connection Context passed back.
|
|
//
|
|
tcpContext->Handle.ConnectionContext =
|
|
Request.Handle.ConnectionContext;
|
|
IrpSp->FileObject->FsContext = tcpContext;
|
|
IrpSp->FileObject->FsContext2 = (PVOID) TDI_CONNECTION_FILE;
|
|
} else {
|
|
ExFreePool(tcpContext);
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
|
|
"TdiOpenConnection failed, status %lx\n", status));
|
|
}
|
|
} else {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
|
|
"TCP: TdiOpenConnection issued on UDP device!\n"));
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
ExFreePool(tcpContext);
|
|
}
|
|
|
|
ASSERT(status != TDI_PENDING);
|
|
|
|
return(status);
|
|
}
|
|
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
|
|
"TCPCreate: didn't find any useful ea's\n"));
|
|
status = STATUS_INVALID_EA_NAME;
|
|
ExFreePool(tcpContext);
|
|
|
|
ASSERT(status != TDI_PENDING);
|
|
|
|
return(status);
|
|
|
|
} // TCPCreate
|
|
|
|
|
|
//* IsAdminIoRequest -
|
|
//
|
|
// (Lifted from AFD - AfdPerformSecurityCheck)
|
|
// Compares security context of the endpoint creator to that
|
|
// of the administrator and local system.
|
|
//
|
|
BOOLEAN // return TRUE if socket creator has admin or local system privilege
|
|
IsAdminIoRequest(
|
|
PIRP Irp, // Pointer to I/O request packet.
|
|
PIO_STACK_LOCATION IrpSp) // Pointer to the I/O stack location to use for this request.
|
|
{
|
|
BOOLEAN accessGranted;
|
|
PACCESS_STATE accessState;
|
|
PIO_SECURITY_CONTEXT securityContext;
|
|
PPRIVILEGE_SET privileges = NULL;
|
|
ACCESS_MASK grantedAccess;
|
|
PGENERIC_MAPPING GenericMapping;
|
|
ACCESS_MASK AccessMask = GENERIC_ALL;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Enable access to all the globally defined SIDs
|
|
//
|
|
|
|
GenericMapping = IoGetFileObjectGenericMapping();
|
|
|
|
RtlMapGenericMask(&AccessMask, GenericMapping);
|
|
|
|
securityContext = IrpSp->Parameters.Create.SecurityContext;
|
|
accessState = securityContext->AccessState;
|
|
|
|
SeLockSubjectContext(&accessState->SubjectSecurityContext);
|
|
|
|
accessGranted = SeAccessCheck(TcpAdminSecurityDescriptor,
|
|
&accessState->SubjectSecurityContext,
|
|
TRUE,
|
|
AccessMask,
|
|
0,
|
|
&privileges,
|
|
IoGetFileObjectGenericMapping(),
|
|
(KPROCESSOR_MODE) ((IrpSp->Flags & SL_FORCE_ACCESS_CHECK)
|
|
? UserMode
|
|
: Irp->RequestorMode),
|
|
&grantedAccess,
|
|
&Status);
|
|
|
|
if (privileges) {
|
|
(VOID) SeAppendPrivileges(accessState,
|
|
privileges);
|
|
SeFreePrivileges(privileges);
|
|
}
|
|
if (accessGranted) {
|
|
accessState->PreviouslyGrantedAccess |= grantedAccess;
|
|
accessState->RemainingDesiredAccess &= ~(grantedAccess | MAXIMUM_ALLOWED);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
} else {
|
|
ASSERT(!NT_SUCCESS(Status));
|
|
}
|
|
SeUnlockSubjectContext(&accessState->SubjectSecurityContext);
|
|
|
|
return accessGranted;
|
|
} // IsAdminIoRequest
|
|
|
|
|
|
//* TCPCloseObjectComplete -
|
|
//
|
|
// Completes a TdiCloseConnectoin or TdiCloseAddress request.
|
|
//
|
|
void // Returns: Nothing.
|
|
TCPCloseObjectComplete(
|
|
void *Context, // Pointer to the IRP for this request.
|
|
unsigned int Status, // Final status of the operation.
|
|
unsigned int UnUsed) // Unused parameter.
|
|
{
|
|
KIRQL oldIrql;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PTCP_CONTEXT tcpContext;
|
|
|
|
UNREFERENCED_PARAMETER(UnUsed);
|
|
|
|
irp = (PIRP) Context;
|
|
irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
tcpContext = (PTCP_CONTEXT) irpSp->FileObject->FsContext;
|
|
irp->IoStatus.Status = Status;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CLEANUP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCloseObjectComplete on file object %lx\n",
|
|
irpSp->FileObject));
|
|
}
|
|
|
|
KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql);
|
|
|
|
ASSERT(tcpContext->ReferenceCount > 0);
|
|
ASSERT(tcpContext->CancelIrps);
|
|
|
|
//
|
|
// Remove the initial reference that was put on by TCPCreate.
|
|
//
|
|
ASSERT(tcpContext->ReferenceCount > 0);
|
|
|
|
IF_TCPDBG(TCP_DEBUG_IRP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCloseObjectComplete: "
|
|
"irp %lx fileobj %lx refcnt dec to %u\n",
|
|
irp, irpSp, tcpContext->ReferenceCount - 1));
|
|
}
|
|
|
|
if (--(tcpContext->ReferenceCount) == 0) {
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CANCEL) {
|
|
ASSERT(IsListEmpty(&(tcpContext->CancelledIrpList)));
|
|
ASSERT(IsListEmpty(&(tcpContext->PendingIrpList)));
|
|
}
|
|
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
KeSetEvent(&(tcpContext->CleanupEvent), 0, FALSE);
|
|
return;
|
|
}
|
|
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
|
|
return;
|
|
|
|
} // TCPCloseObjectComplete
|
|
|
|
|
|
|
|
//* TCPCleanup -
|
|
//
|
|
// Cancels all outstanding Irps on a TDI object by calling the close
|
|
// routine for the object. It then waits for them to be completed
|
|
// before returning.
|
|
//
|
|
// This routine blocks, but does not pend.
|
|
//
|
|
NTSTATUS // Returns: whether the request was successfully queued.
|
|
TCPCleanup(
|
|
IN PDEVICE_OBJECT DeviceObject, // Device object for this request.
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
KIRQL oldIrql;
|
|
PIRP cancelIrp = NULL;
|
|
PTCP_CONTEXT tcpContext;
|
|
NTSTATUS status;
|
|
TDI_REQUEST request;
|
|
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
|
|
KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql);
|
|
|
|
tcpContext->CancelIrps = TRUE;
|
|
KeResetEvent(&(tcpContext->CleanupEvent));
|
|
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
|
|
//
|
|
// Now call the TDI close routine for this object to force all of its Irps
|
|
// to complete.
|
|
//
|
|
request.RequestNotifyObject = TCPCloseObjectComplete;
|
|
request.RequestContext = Irp;
|
|
|
|
switch (PtrToUlong(IrpSp->FileObject->FsContext2)) {
|
|
|
|
case TDI_TRANSPORT_ADDRESS_FILE:
|
|
IF_TCPDBG(TCP_DEBUG_CLOSE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCleanup: Closing address object on file object %lx\n",
|
|
IrpSp->FileObject));
|
|
}
|
|
request.Handle.AddressHandle = tcpContext->Handle.AddressHandle;
|
|
status = TdiCloseAddress(&request);
|
|
break;
|
|
|
|
case TDI_CONNECTION_FILE:
|
|
IF_TCPDBG(TCP_DEBUG_CLOSE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCleanup: "
|
|
"Closing Connection object on file object %lx\n",
|
|
IrpSp->FileObject));
|
|
}
|
|
request.Handle.ConnectionContext =
|
|
tcpContext->Handle.ConnectionContext;
|
|
status = TdiCloseConnection(&request);
|
|
break;
|
|
|
|
case TDI_CONTROL_CHANNEL_FILE:
|
|
IF_TCPDBG(TCP_DEBUG_CLOSE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCleanup: Closing Control Channel object on"
|
|
" file object %lx\n", IrpSp->FileObject));
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// This should never happen.
|
|
//
|
|
ASSERT(FALSE);
|
|
|
|
KeAcquireSpinLock(&tcpContext->EndpointLock, &oldIrql);
|
|
tcpContext->CancelIrps = FALSE;
|
|
KeReleaseSpinLock(&tcpContext->EndpointLock, oldIrql);
|
|
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (status != TDI_PENDING) {
|
|
TCPCloseObjectComplete(Irp, status, 0);
|
|
}
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CLEANUP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCleanup: waiting for completion of Irps on"
|
|
" file object %lx\n", IrpSp->FileObject));
|
|
}
|
|
|
|
status = KeWaitForSingleObject(&(tcpContext->CleanupEvent), UserRequest,
|
|
KernelMode, FALSE, NULL);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CLEANUP) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPCleanup: Wait on file object %lx finished\n",
|
|
IrpSp->FileObject));
|
|
}
|
|
|
|
//
|
|
// The cleanup Irp will be completed by the dispatch routine.
|
|
//
|
|
|
|
return(Irp->IoStatus.Status);
|
|
|
|
} // TCPCleanup
|
|
|
|
|
|
//* TCPClose -
|
|
//
|
|
// Dispatch routine for MJ_CLOSE IRPs. Performs final cleanup of the
|
|
// open endpoint.
|
|
//
|
|
// This request does not pend.
|
|
//
|
|
NTSTATUS // Returns: whether the request was successfully queued.
|
|
TCPClose(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
PTCP_CONTEXT tcpContext;
|
|
|
|
tcpContext = (PTCP_CONTEXT) IrpSp->FileObject->FsContext;
|
|
|
|
#if DBG
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CANCEL) {
|
|
|
|
KIRQL oldIrql;
|
|
|
|
IoAcquireCancelSpinLock(&oldIrql);
|
|
|
|
ASSERT(tcpContext->ReferenceCount == 0);
|
|
ASSERT(IsListEmpty(&(tcpContext->PendingIrpList)));
|
|
ASSERT(IsListEmpty(&(tcpContext->CancelledIrpList)));
|
|
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
}
|
|
#endif // DBG
|
|
|
|
IF_TCPDBG(TCP_DEBUG_CLOSE) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"TCPClose on file object %lx\n", IrpSp->FileObject));
|
|
}
|
|
|
|
ExFreePool(tcpContext);
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // TCPClose
|
|
|
|
|
|
//* TCPDispatchDeviceControl -
|
|
//
|
|
NTSTATUS // Returns: whether the request was successfully queued.
|
|
TCPDispatchDeviceControl(
|
|
IN PIRP Irp, // I/O request packet.
|
|
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Set this in advance. Any IOCTL dispatch routine that cares about it
|
|
// will modify it itself.
|
|
//
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
switch(IrpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_TCP_QUERY_INFORMATION_EX:
|
|
return(TCPQueryInformationEx(Irp, IrpSp));
|
|
break;
|
|
|
|
case IOCTL_TCP_SET_INFORMATION_EX:
|
|
case IOCTL_TCP_WSH_SET_INFORMATION_EX:
|
|
return(TCPSetInformationEx(Irp, IrpSp));
|
|
break;
|
|
|
|
default:
|
|
status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return status;
|
|
|
|
} // TCPDispatchDeviceControl
|
|
|
|
|
|
//* TCPDispatchInternalDeviceControl -
|
|
//
|
|
// This is the dispatch routine for Internal Device Control IRPs.
|
|
// This is the hot path for kernel-mode clients.
|
|
//
|
|
NTSTATUS // Returns: whether the request was successfully queued.
|
|
TCPDispatchInternalDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject, // Device object for target device.
|
|
IN PIRP Irp) // I/O request packet.
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
|
|
if (DeviceObject != IPDeviceObject) {
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
if (PtrToUlong(irpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE) {
|
|
//
|
|
// Send and receive are the performance path, so check for them
|
|
// right away.
|
|
//
|
|
if (irpSp->MinorFunction == TDI_SEND) {
|
|
return(TCPSendData(Irp, irpSp));
|
|
}
|
|
|
|
if (irpSp->MinorFunction == TDI_RECEIVE) {
|
|
return(TCPReceiveData(Irp, irpSp));
|
|
}
|
|
|
|
switch(irpSp->MinorFunction) {
|
|
|
|
case TDI_ASSOCIATE_ADDRESS:
|
|
status = TCPAssociateAddress(Irp, irpSp);
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return(status);
|
|
|
|
case TDI_DISASSOCIATE_ADDRESS:
|
|
return(TCPDisassociateAddress(Irp, irpSp));
|
|
|
|
case TDI_CONNECT:
|
|
return(TCPConnect(Irp, irpSp));
|
|
|
|
case TDI_DISCONNECT:
|
|
return(TCPDisconnect(Irp, irpSp));
|
|
|
|
case TDI_LISTEN:
|
|
return(TCPListen(Irp, irpSp));
|
|
|
|
case TDI_ACCEPT:
|
|
return(TCPAccept(Irp, irpSp));
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Fall through.
|
|
//
|
|
}
|
|
else if (PtrToUlong(irpSp->FileObject->FsContext2) ==
|
|
TDI_TRANSPORT_ADDRESS_FILE) {
|
|
|
|
if (irpSp->MinorFunction == TDI_SEND_DATAGRAM) {
|
|
return(UDPSendDatagram(Irp, irpSp));
|
|
}
|
|
|
|
if (irpSp->MinorFunction == TDI_RECEIVE_DATAGRAM) {
|
|
return(UDPReceiveDatagram(Irp, irpSp));
|
|
}
|
|
|
|
if (irpSp->MinorFunction == TDI_SET_EVENT_HANDLER) {
|
|
status = TCPSetEventHandler(Irp, irpSp);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Fall through.
|
|
//
|
|
}
|
|
|
|
ASSERT(
|
|
(PtrToUlong(irpSp->FileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE)
|
|
||
|
|
(PtrToUlong(irpSp->FileObject->FsContext2) == TDI_CONNECTION_FILE)
|
|
||
|
|
(PtrToUlong(irpSp->FileObject->FsContext2) == TDI_CONTROL_CHANNEL_FILE));
|
|
|
|
//
|
|
// These functions are common to all endpoint types.
|
|
//
|
|
switch(irpSp->MinorFunction) {
|
|
|
|
case TDI_QUERY_INFORMATION:
|
|
return(TCPQueryInformation(Irp, irpSp));
|
|
|
|
case TDI_SET_INFORMATION:
|
|
case TDI_ACTION:
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
|
|
"TCP: Call to unimplemented TDI function 0x%p\n",
|
|
irpSp->MinorFunction));
|
|
status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
default:
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
|
|
"TCP: call to invalid TDI function 0x%p\n",
|
|
irpSp->MinorFunction));
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
ASSERT(status != TDI_PENDING);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
|
|
return(IPDispatch(DeviceObject, Irp));
|
|
}
|
|
|
|
|
|
//* TCPDispatch -
|
|
//
|
|
// This is the generic dispatch routine for TCP/UDP/RawIP.
|
|
//
|
|
NTSTATUS // Returns: whether the request was successfully queued.
|
|
TCPDispatch(
|
|
IN PDEVICE_OBJECT DeviceObject, // Device object for target device.
|
|
IN PIRP Irp) // I/O request packet.
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
|
|
if (DeviceObject != IPDeviceObject) {
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ASSERT(irpSp->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL);
|
|
|
|
switch (irpSp->MajorFunction) {
|
|
|
|
case IRP_MJ_CREATE:
|
|
status = TCPCreate(DeviceObject, Irp, irpSp);
|
|
break;
|
|
|
|
case IRP_MJ_CLEANUP:
|
|
status = TCPCleanup(DeviceObject, Irp, irpSp);
|
|
break;
|
|
|
|
case IRP_MJ_CLOSE:
|
|
status = TCPClose(Irp, irpSp);
|
|
break;
|
|
|
|
case IRP_MJ_DEVICE_CONTROL:
|
|
status = TdiMapUserRequest(DeviceObject, Irp, irpSp);
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
return(TCPDispatchInternalDeviceControl(DeviceObject, Irp));
|
|
}
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER) {
|
|
if (Irp->RequestorMode == KernelMode) {
|
|
*(PULONG_PTR)irpSp->Parameters.DeviceIoControl.Type3InputBuffer = (ULONG_PTR)TCPSendData;
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
status = STATUS_ACCESS_DENIED;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return(TCPDispatchDeviceControl(Irp,
|
|
IoGetCurrentIrpStackLocation(Irp)));
|
|
break;
|
|
|
|
case IRP_MJ_QUERY_SECURITY:
|
|
//
|
|
// This is generated on Raw endpoints. We don't do anything
|
|
// for it.
|
|
//
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IRP_MJ_WRITE:
|
|
case IRP_MJ_READ:
|
|
|
|
default:
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
|
|
"TCPDispatch: Irp %lx unsupported major function 0x%lx\n",
|
|
irpSp, irpSp->MajorFunction));
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
ASSERT(status != TDI_PENDING);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
|
|
return(IPDispatch(DeviceObject, Irp));
|
|
|
|
} // TCPDispatch
|
|
|
|
|
|
//
|
|
// Private utility functions
|
|
//
|
|
|
|
//* FindEA -
|
|
//
|
|
// Parses and extended attribute list for a given target attribute.
|
|
//
|
|
FILE_FULL_EA_INFORMATION UNALIGNED * // Returns: requested attribute or NULL.
|
|
FindEA(
|
|
PFILE_FULL_EA_INFORMATION StartEA, // First extended attribute in list.
|
|
CHAR *TargetName, // Name of target attribute.
|
|
USHORT TargetNameLength) // Length of above.
|
|
{
|
|
USHORT i;
|
|
BOOLEAN found;
|
|
FILE_FULL_EA_INFORMATION UNALIGNED *CurrentEA;
|
|
|
|
PAGED_CODE();
|
|
|
|
do {
|
|
found = TRUE;
|
|
|
|
CurrentEA = StartEA;
|
|
StartEA += CurrentEA->NextEntryOffset;
|
|
|
|
if (CurrentEA->EaNameLength != TargetNameLength) {
|
|
continue;
|
|
}
|
|
|
|
for (i=0; i < CurrentEA->EaNameLength; i++) {
|
|
if (CurrentEA->EaName[i] == TargetName[i]) {
|
|
continue;
|
|
}
|
|
found = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (found) {
|
|
return(CurrentEA);
|
|
}
|
|
|
|
} while(CurrentEA->NextEntryOffset != 0);
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
//* IsDHCPZeroAddress -
|
|
//
|
|
// Checks a TDI IP address list for an address from DHCP binding
|
|
// to the IP address zero. Normally, binding to zero means wildcard.
|
|
// For DHCP, it really means bind to an interface with an address of
|
|
// zero. This semantic is flagged by a special value in an unused
|
|
// portion of the address structure (ie. this is a kludge).
|
|
//
|
|
BOOLEAN // Returns: TRUE iff first IP address found had the flag set.
|
|
IsDHCPZeroAddress(
|
|
TRANSPORT_ADDRESS UNALIGNED *AddrList) // TDI transport address list
|
|
// passed in the create IRP.
|
|
{
|
|
int i; // Index variable.
|
|
TA_ADDRESS *CurrentAddr; // Address we're examining and may use.
|
|
|
|
// First, verify that someplace in Address is an address we can use.
|
|
CurrentAddr = (TA_ADDRESS *)AddrList->Address;
|
|
|
|
for (i = 0; i < AddrList->TAAddressCount; i++) {
|
|
if (CurrentAddr->AddressType == TDI_ADDRESS_TYPE_IP) {
|
|
if (CurrentAddr->AddressLength == TDI_ADDRESS_LENGTH_IP) {
|
|
TDI_ADDRESS_IP UNALIGNED *ValidAddr;
|
|
|
|
ValidAddr = (TDI_ADDRESS_IP UNALIGNED *)CurrentAddr->Address;
|
|
|
|
if (*((ULONG UNALIGNED *) ValidAddr->sin_zero) == 0x12345678) {
|
|
return TRUE;
|
|
}
|
|
|
|
} else {
|
|
return FALSE; // Wrong length for address.
|
|
}
|
|
} else {
|
|
CurrentAddr = (TA_ADDRESS *)
|
|
(CurrentAddr->Address + CurrentAddr->AddressLength);
|
|
}
|
|
}
|
|
|
|
return FALSE; // Didn't find a match.
|
|
}
|
|
|
|
|
|
//* TCPGetMdlChainByteCount -
|
|
//
|
|
// Sums the byte counts of each MDL in a chain.
|
|
//
|
|
ULONG // Returns: byte count of the MDL chain.
|
|
TCPGetMdlChainByteCount(
|
|
PMDL Mdl) // MDL chain to sum.
|
|
{
|
|
ULONG count = 0;
|
|
|
|
while (Mdl != NULL) {
|
|
count += MmGetMdlByteCount(Mdl);
|
|
Mdl = Mdl->Next;
|
|
}
|
|
|
|
return(count);
|
|
}
|
|
|
|
|
|
//* RawExtractProtocolNumber -
|
|
//
|
|
// Extracts the protocol number from the file object name.
|
|
//
|
|
ULONG // Returns: the protocol number or 0xFFFFFFFF on error.
|
|
RawExtractProtocolNumber(
|
|
IN PUNICODE_STRING FileName) // File name (Unicode).
|
|
{
|
|
PWSTR name;
|
|
UNICODE_STRING unicodeString;
|
|
USHORT length;
|
|
ULONG protocol;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
name = FileName->Buffer;
|
|
|
|
if (FileName->Length < (sizeof(OBJ_NAME_PATH_SEPARATOR) + sizeof(WCHAR))) {
|
|
return(0xFFFFFFFF);
|
|
}
|
|
|
|
//
|
|
// Step over separator.
|
|
//
|
|
if (*name++ != OBJ_NAME_PATH_SEPARATOR) {
|
|
return(0xFFFFFFFF);
|
|
}
|
|
|
|
if (*name == UNICODE_NULL) {
|
|
return(0xFFFFFFFF);
|
|
}
|
|
|
|
//
|
|
// Convert the remaining name into a number.
|
|
//
|
|
RtlInitUnicodeString(&unicodeString, name);
|
|
|
|
status = RtlUnicodeStringToInteger(&unicodeString, 10, &protocol);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return(0xFFFFFFFF);
|
|
}
|
|
|
|
if (protocol > 255) {
|
|
return(0xFFFFFFFF);
|
|
}
|
|
|
|
return(protocol);
|
|
}
|