You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3192 lines
91 KiB
3192 lines
91 KiB
#include "gpcpre.h"
|
|
|
|
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ioctl.c
|
|
|
|
Abstract:
|
|
|
|
Creates symbolic link to receive ioctls from user mode and contains the
|
|
ioctl case statement.
|
|
|
|
Author:
|
|
|
|
Yoram Bernet (yoramb) May, 7th. 1997
|
|
|
|
Environment:
|
|
|
|
Kernel Mode
|
|
|
|
Revision History:
|
|
|
|
Ofer Bar (oferbar) Oct 1, 1997 - revision II
|
|
|
|
--*/
|
|
|
|
#pragma hdrstop
|
|
|
|
VOID
|
|
IoctlCleanup(
|
|
ULONG ShutdownMask
|
|
);
|
|
|
|
NTSTATUS
|
|
GPCIoctl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
CancelPendingIrpCfInfo(
|
|
IN PDEVICE_OBJECT Device,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
CancelPendingIrpNotify(
|
|
IN PDEVICE_OBJECT Device,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
ProxyGpcRegisterClient(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PFILE_OBJECT FileObject,
|
|
BOOLEAN fNewInterface
|
|
);
|
|
|
|
NTSTATUS
|
|
ProxyGpcDeregisterClient(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PFILE_OBJECT FileObject,
|
|
BOOLEAN fNewInterface
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
ProxyGpcAddCfInfo(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PIRP Irp,
|
|
PFILE_OBJECT FileObject
|
|
);
|
|
|
|
NTSTATUS
|
|
ProxyGpcAddCfInfoEx(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PIRP Irp,
|
|
PFILE_OBJECT FileObject
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
ProxyGpcModifyCfInfo(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PIRP Irp,
|
|
PFILE_OBJECT FileObject
|
|
);
|
|
|
|
NTSTATUS
|
|
ProxyGpcRemoveCfInfo(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PIRP Irp,
|
|
PFILE_OBJECT FileObject,
|
|
BOOLEAN fNewInterface
|
|
);
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ProxyGpcAddPattern(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PFILE_OBJECT FileObject,
|
|
BOOLEAN fNewInterface
|
|
);
|
|
|
|
NTSTATUS
|
|
ProxyGpcRemovePattern(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PFILE_OBJECT FileObject,
|
|
BOOLEAN fNewInterface
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
ProxyGpcEnumCfInfo(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PFILE_OBJECT FileObject
|
|
);
|
|
|
|
NTSTATUS
|
|
GpcValidateClientOwner (
|
|
IN GPC_HANDLE GpcClientHandle,
|
|
IN PFILE_OBJECT pFile
|
|
);
|
|
|
|
NTSTATUS
|
|
GpcValidatePatternOwner (
|
|
IN GPC_HANDLE GpcClientHandle,
|
|
IN GPC_HANDLE GpcPatternHandle
|
|
);
|
|
|
|
NTSTATUS
|
|
GpcValidateCfinfoOwner (
|
|
IN GPC_HANDLE GpcClientHandle,
|
|
IN GPC_HANDLE GpcCfInfoHandle
|
|
);
|
|
|
|
|
|
GPC_CLIENT_FUNC_LIST CallBackProxyList;
|
|
PDEVICE_OBJECT GPCDeviceObject;
|
|
LIST_ENTRY PendingIrpCfInfoList;
|
|
LIST_ENTRY PendingIrpNotifyList;
|
|
LIST_ENTRY QueuedNotificationList;
|
|
LIST_ENTRY QueuedCompletionList;
|
|
|
|
/* End Forward */
|
|
|
|
#pragma NDIS_PAGEABLE_FUNCTION(GPCIoctl)
|
|
|
|
UNICODE_STRING GpcDriverName = {sizeof(DD_GPC_DEVICE_NAME)-2,
|
|
sizeof(DD_GPC_DEVICE_NAME),
|
|
DD_GPC_DEVICE_NAME};
|
|
NTSTATUS
|
|
IoctlInitialize(
|
|
PDRIVER_OBJECT DriverObject,
|
|
PULONG InitShutdownMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform initialization
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to DriverObject from DriverEntry
|
|
InitShutdownMask - pointer to mask used to indicate which events have been
|
|
successfully init'ed
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if everything worked ok
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
UINT FuncIndex;
|
|
|
|
InitializeListHead(&PendingIrpCfInfoList);
|
|
InitializeListHead(&PendingIrpNotifyList);
|
|
InitializeListHead(&QueuedNotificationList);
|
|
InitializeListHead(&QueuedCompletionList);
|
|
|
|
//
|
|
// Initialize the driver object's entry points
|
|
//
|
|
|
|
DriverObject->FastIoDispatch = NULL;
|
|
|
|
for (FuncIndex = 0; FuncIndex <= IRP_MJ_MAXIMUM_FUNCTION; FuncIndex++) {
|
|
DriverObject->MajorFunction[FuncIndex] = GPCIoctl;
|
|
}
|
|
|
|
Status = IoCreateDevice(DriverObject,
|
|
0,
|
|
&GpcDriverName,
|
|
FILE_DEVICE_NETWORK,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&GPCDeviceObject);
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
*InitShutdownMask |= SHUTDOWN_DELETE_DEVICE;
|
|
|
|
GPCDeviceObject->Flags |= DO_BUFFERED_IO;
|
|
|
|
/* yoramb - don't need a symbolic link for now...
|
|
|
|
Status = IoCreateSymbolicLink( &GPCSymbolicName, &GPCDriverName );
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
*InitShutdownMask |= SHUTDOWN_DELETE_SYMLINK;
|
|
} else {
|
|
DBGPRINT(IOCTL, ("IoCreateSymbolicLink Failed (%08X): %ls -> %ls\n",
|
|
Status, GPCSymbolicName.Buffer, PSDriverName.Buffer));
|
|
}
|
|
*/
|
|
} else {
|
|
DbgPrint("IoCreateDevice failed. Status = %x\n", Status);
|
|
GPCDeviceObject = NULL;
|
|
}
|
|
|
|
//
|
|
// Initialize the callback functions. These are called by the
|
|
// kernel Gpc and turned into async notifications to the user.
|
|
// For now, the user does not get callbacks, so they are NULL.
|
|
//
|
|
|
|
CallBackProxyList.GpcVersion = GpcMajorVersion;
|
|
CallBackProxyList.ClAddCfInfoCompleteHandler = NULL;
|
|
CallBackProxyList.ClAddCfInfoNotifyHandler = NULL;
|
|
CallBackProxyList.ClModifyCfInfoCompleteHandler = NULL;
|
|
CallBackProxyList.ClModifyCfInfoNotifyHandler = NULL;
|
|
CallBackProxyList.ClRemoveCfInfoCompleteHandler = NULL;
|
|
CallBackProxyList.ClRemoveCfInfoNotifyHandler = NULL;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
GPCIoctl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the IRPs sent to this device.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to a device object
|
|
Irp - pointer to an I/O Request Packet
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
PVOID ioBuffer;
|
|
ULONG inputBufferLength;
|
|
ULONG outputBufferLength;
|
|
ULONG ioControlCode;
|
|
UCHAR saveControlFlags;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
#if DBG
|
|
KIRQL irql = KeGetCurrentIrql();
|
|
KIRQL irql2;
|
|
HANDLE thrd = PsGetCurrentThreadId();
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Init to default settings- we only expect 1 type of
|
|
// IOCTL to roll through here, all others an error.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Get a pointer to the current location in the Irp. This is where
|
|
// the function codes and parameters are located.
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Get the pointer to the input/output buffer and it's length
|
|
//
|
|
|
|
ioBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
|
|
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
|
|
saveControlFlags = irpStack->Control;
|
|
|
|
TRACE(LOCKS, thrd, irql, "GPCIoctl");
|
|
|
|
switch (irpStack->MajorFunction) {
|
|
|
|
case IRP_MJ_CREATE:
|
|
DBGPRINT(IOCTL, ("IRP Create\n"));
|
|
break;
|
|
|
|
case IRP_MJ_READ:
|
|
DBGPRINT(IOCTL, ("IRP Read\n"));
|
|
break;
|
|
|
|
case IRP_MJ_CLOSE:
|
|
DBGPRINT(IOCTL, ("IRP Close\n"));
|
|
TRACE(IOCTL, irpStack->FileObject, 0, "IRP Close");
|
|
|
|
//
|
|
// make sure we clean all the objects for this particular
|
|
// file object, since it's closing right now.
|
|
//
|
|
|
|
CloseAllObjects(irpStack->FileObject, Irp);
|
|
|
|
break;
|
|
|
|
case IRP_MJ_CLEANUP:
|
|
DBGPRINT(IOCTL, ("IRP Cleanup\n"));
|
|
break;
|
|
|
|
case IRP_MJ_SHUTDOWN:
|
|
DBGPRINT(IOCTL, ("IRP Shutdown\n"));
|
|
break;
|
|
|
|
case IRP_MJ_DEVICE_CONTROL:
|
|
|
|
DBGPRINT(IOCTL, ("GPCIoctl: ioctl=0x%X, IRP=0x%X\n",
|
|
ioControlCode, (ULONG_PTR)Irp));
|
|
|
|
TRACE(IOCTL, ioControlCode, Irp, "GPCIoctl.irp:");
|
|
|
|
//
|
|
// Mark the IRP as Pending BEFORE calling any dispatch routine.
|
|
// If Status is actually set to STATUS_PENDING, we assume the IRP
|
|
// is ready to be returned.
|
|
// It is possible the IoCompleteRequest has been called async for the
|
|
// IRP, but this should be taken care of by the IO subsystem.
|
|
//
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
switch (ioControlCode) {
|
|
|
|
case IOCTL_GPC_REGISTER_CLIENT:
|
|
|
|
|
|
Status = ProxyGpcRegisterClient(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
irpStack->FileObject,
|
|
FALSE); //Admin Only
|
|
|
|
|
|
break;
|
|
|
|
case IOCTL_GPC_REGISTER_CLIENT_EX:
|
|
Status = ProxyGpcRegisterClient(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
irpStack->FileObject,
|
|
TRUE); // All users
|
|
break;
|
|
|
|
case IOCTL_GPC_DEREGISTER_CLIENT:
|
|
|
|
|
|
Status = ProxyGpcDeregisterClient(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
irpStack->FileObject,
|
|
FALSE
|
|
);// Admin Only Call
|
|
|
|
break;
|
|
|
|
case IOCTL_GPC_DEREGISTER_CLIENT_EX:
|
|
|
|
Status = ProxyGpcDeregisterClient(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
irpStack->FileObject,
|
|
TRUE);// All users
|
|
|
|
break;
|
|
|
|
case IOCTL_GPC_ADD_CF_INFO:
|
|
|
|
Status = ProxyGpcAddCfInfo(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
Irp,
|
|
irpStack->FileObject
|
|
);
|
|
|
|
break;
|
|
// RSVP Removal Change
|
|
// New IOCTL added
|
|
case IOCTL_GPC_ADD_CF_INFO_EX:
|
|
|
|
Status = ProxyGpcAddCfInfoEx(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
Irp,
|
|
irpStack->FileObject
|
|
);
|
|
break;
|
|
|
|
|
|
case IOCTL_GPC_MODIFY_CF_INFO:
|
|
|
|
Status = ProxyGpcModifyCfInfo(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
Irp,
|
|
irpStack->FileObject
|
|
);
|
|
|
|
break;
|
|
|
|
case IOCTL_GPC_REMOVE_CF_INFO:
|
|
|
|
Status = ProxyGpcRemoveCfInfo(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
Irp,
|
|
irpStack->FileObject,
|
|
FALSE);//Admin Only
|
|
|
|
break;
|
|
|
|
case IOCTL_GPC_REMOVE_CF_INFO_EX:
|
|
|
|
Status = ProxyGpcRemoveCfInfo(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
Irp,
|
|
irpStack->FileObject,
|
|
TRUE);//All User
|
|
break;
|
|
|
|
|
|
case IOCTL_GPC_ADD_PATTERN:
|
|
|
|
Status = ProxyGpcAddPattern(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
irpStack->FileObject,
|
|
FALSE); //Admin only IOCTL
|
|
|
|
break;
|
|
|
|
case IOCTL_GPC_ADD_PATTERN_EX:
|
|
|
|
Status = ProxyGpcAddPattern(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
irpStack->FileObject,
|
|
TRUE); // All user IOCTL
|
|
|
|
break;
|
|
|
|
|
|
case IOCTL_GPC_REMOVE_PATTERN:
|
|
|
|
Status = ProxyGpcRemovePattern(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
irpStack->FileObject,
|
|
FALSE);// Admin Only IOCTL
|
|
|
|
break;
|
|
|
|
case IOCTL_GPC_REMOVE_PATTERN_EX:
|
|
|
|
Status = ProxyGpcRemovePattern(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
irpStack->FileObject,
|
|
TRUE);// All user IOCTL
|
|
|
|
break;
|
|
|
|
|
|
case IOCTL_GPC_ENUM_CFINFO:
|
|
|
|
Status = ProxyGpcEnumCfInfo(ioBuffer,
|
|
inputBufferLength,
|
|
&outputBufferLength,
|
|
irpStack->FileObject);
|
|
|
|
break;
|
|
|
|
case IOCTL_GPC_NOTIFY_REQUEST:
|
|
|
|
//
|
|
// request to pend an IRP
|
|
//
|
|
|
|
Status = CheckQueuedNotification(Irp, &outputBufferLength);
|
|
|
|
break;
|
|
|
|
|
|
case IOCTL_GPC_GET_ENTRIES:
|
|
|
|
// Locking Down Kmode Only IOCTLs
|
|
if (ExGetPreviousMode() != KernelMode)
|
|
{
|
|
Status = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
#ifdef STANDALONE_DRIVER
|
|
|
|
//
|
|
// Return the exported calls in the buffer
|
|
//
|
|
|
|
if (outputBufferLength >= sizeof(glGpcExportedCalls)) {
|
|
|
|
NdisMoveMemory(ioBuffer,
|
|
&glGpcExportedCalls,
|
|
sizeof(glGpcExportedCalls));
|
|
|
|
|
|
outputBufferLength = sizeof(glGpcExportedCalls);
|
|
|
|
} else {
|
|
|
|
outputBufferLength = sizeof(glGpcExportedCalls);
|
|
Status = GPC_STATUS_INSUFFICIENT_BUFFER;
|
|
}
|
|
#else
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
DBGPRINT(IOCTL, ("GPCIoctl: Unknown IRP_MJ_DEVICE_CONTROL\n = %X\n",
|
|
ioControlCode));
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
} // switch (ioControlCode)
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
DBGPRINT(IOCTL, ("GPCIoctl: Unknown IRP major function = %08X\n",
|
|
irpStack->MajorFunction));
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
DBGPRINT(IOCTL, ("GPCIoctl: Status=0x%X, IRP=0x%X, outSize=%d\n",
|
|
Status, (ULONG_PTR)Irp, outputBufferLength));
|
|
|
|
TRACE(IOCTL, Irp, Status, "GPCIoctl.Complete:");
|
|
|
|
if (Status != STATUS_PENDING) {
|
|
|
|
//
|
|
// IRP completed and it's not Pending, we need to restore the Control flags,
|
|
// since it might have been marked as Pending before...
|
|
//
|
|
|
|
irpStack->Control = saveControlFlags;
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
Irp->IoStatus.Information = outputBufferLength;
|
|
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
}
|
|
|
|
#if DBG
|
|
irql2 = KeGetCurrentIrql();
|
|
ASSERT(irql == irql2);
|
|
#endif
|
|
|
|
TRACE(LOCKS, thrd, irql2, "GPCIoctl (end)");
|
|
|
|
return Status;
|
|
|
|
} // GPCIoctl
|
|
|
|
|
|
|
|
|
|
VOID
|
|
IoctlCleanup(
|
|
ULONG ShutdownMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleanup code for Initialize
|
|
|
|
Arguments:
|
|
|
|
ShutdownMask - mask indicating which functions need to be cleaned up
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
/*
|
|
if ( ShutdownMask & SHUTDOWN_DELETE_SYMLINK ) {
|
|
|
|
IoDeleteSymbolicLink( &PSSymbolicName );
|
|
}
|
|
*/
|
|
|
|
if ( ShutdownMask & SHUTDOWN_DELETE_DEVICE ) {
|
|
|
|
IoDeleteDevice( GPCDeviceObject );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
TcpQueryInfoComplete(PDEVICE_OBJECT pDeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context)
|
|
{
|
|
PTDI_ROUTING_INFO RoutingInfo;
|
|
PTRANSPORT_ADDRESS TransportAddress;
|
|
PTA_ADDRESS TaAddress;
|
|
PTDI_ADDRESS_IP IpAddress;
|
|
int i;
|
|
PGPC_IP_PATTERN pTcpPattern;
|
|
PGPC_TCP_QUERY_CONTEXT pGpcTcpContext;
|
|
NTSTATUS Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
RoutingInfo = (PTDI_ROUTING_INFO)Context;
|
|
//
|
|
// Get a pointer to the Original Context. This allows us to access the GPC_IP_PATTERN
|
|
pGpcTcpContext = CONTAINING_RECORD(Context,GPC_TCP_QUERY_CONTEXT,RouteInfo);
|
|
//
|
|
// Retreive the pointer to the GPC_IP_PATTERN
|
|
pTcpPattern = pGpcTcpContext->pTcpPattern;
|
|
|
|
// Check status .
|
|
if (Irp->IoStatus.Status != STATUS_SUCCESS){
|
|
goto exit;
|
|
}
|
|
DBGPRINT(IOCTL,("\n"));
|
|
DBGPRINT(IOCTL,("Protocol: %d\n", RoutingInfo->Protocol));
|
|
DBGPRINT(IOCTL,("InterfaceId: %d\n", RoutingInfo->InterfaceId));
|
|
DBGPRINT(IOCTL,("LinkId: %d\n", RoutingInfo->LinkId));
|
|
|
|
// Fill up the values of the Interface
|
|
pTcpPattern->InterfaceId.InterfaceId = RoutingInfo->InterfaceId;
|
|
pTcpPattern->InterfaceId.LinkId = RoutingInfo->LinkId;
|
|
|
|
TransportAddress = (PTRANSPORT_ADDRESS)&RoutingInfo->Address;
|
|
DBGPRINT(IOCTL,("Address Count: %d\n",TransportAddress->TAAddressCount));
|
|
|
|
TaAddress = (PTA_ADDRESS)&TransportAddress->Address;
|
|
|
|
|
|
if ((2 < TransportAddress->TAAddressCount) || (1 > TransportAddress->TAAddressCount)){
|
|
// Release Memory
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
ASSERT(FALSE);
|
|
goto exit;
|
|
}
|
|
|
|
IpAddress = (PTDI_ADDRESS_IP)&TaAddress->Address;
|
|
pTcpPattern->SrcAddr = IpAddress->in_addr;
|
|
pTcpPattern->gpcSrcPort = IpAddress->sin_port;
|
|
DBGPRINT(IOCTL,("AddressLength = %d\n",TaAddress->AddressLength));
|
|
DBGPRINT(IOCTL,("AddressType = %d\n",TaAddress->AddressType));
|
|
DBGPRINT(IOCTL,("Port = %d\n",IpAddress->sin_port));
|
|
DBGPRINT(IOCTL,("Address = %d.%d.%d.%d\n",
|
|
(IpAddress->in_addr & 0xff000000) >> 24,
|
|
(IpAddress->in_addr & 0x00ff0000) >> 16,
|
|
(IpAddress->in_addr & 0x0000ff00) >> 8,
|
|
IpAddress->in_addr & 0x000000ff));
|
|
|
|
if ( 2 == TransportAddress->TAAddressCount)
|
|
{
|
|
TaAddress = (PTA_ADDRESS)((PUCHAR)TaAddress +
|
|
FIELD_OFFSET(TA_ADDRESS, Address) +
|
|
TaAddress->AddressLength);
|
|
IpAddress = (PTDI_ADDRESS_IP)&TaAddress->Address;
|
|
pTcpPattern->DstAddr = IpAddress->in_addr;
|
|
pTcpPattern->gpcDstPort = IpAddress->sin_port;
|
|
pTcpPattern->ProtocolId = IPPROTO_TCP;
|
|
DBGPRINT(IOCTL,("AddressLength = %d\n",TaAddress->AddressLength));
|
|
DBGPRINT(IOCTL,("AddressType = %d\n",TaAddress->AddressType));
|
|
DBGPRINT(IOCTL,("Port = %d\n",IpAddress->sin_port));
|
|
DBGPRINT(IOCTL,("Address = %d.%d.%d.%d\n",
|
|
(IpAddress->in_addr & 0xff000000) >> 24,
|
|
(IpAddress->in_addr & 0x00ff0000) >> 16,
|
|
(IpAddress->in_addr & 0x0000ff00) >> 8,
|
|
IpAddress->in_addr & 0x000000ff));
|
|
}
|
|
else{
|
|
pTcpPattern->ProtocolId = IPPROTO_UDP;
|
|
}
|
|
//
|
|
// Need to free the Memory allocated, MDL and the IRP here.
|
|
|
|
exit:
|
|
IoFreeMdl(pGpcTcpContext->pMdl);
|
|
GpcFreeMem(pGpcTcpContext,TcpQueryContextTag);
|
|
IoFreeIrp(Irp);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
void
|
|
BuildTDIAddress(uchar * Buffer, IPAddr Addr, ushort Port)
|
|
{
|
|
PTRANSPORT_ADDRESS XportAddr;
|
|
PTA_ADDRESS TAAddr;
|
|
|
|
XportAddr = (PTRANSPORT_ADDRESS) Buffer;
|
|
XportAddr->TAAddressCount = 1;
|
|
TAAddr = XportAddr->Address;
|
|
TAAddr->AddressType = TDI_ADDRESS_TYPE_IP;
|
|
TAAddr->AddressLength = sizeof(TDI_ADDRESS_IP);
|
|
((PTDI_ADDRESS_IP) TAAddr->Address)->sin_port = Port;
|
|
((PTDI_ADDRESS_IP) TAAddr->Address)->in_addr = Addr;
|
|
memset(((PTDI_ADDRESS_IP) TAAddr->Address)->sin_zero,
|
|
0,
|
|
sizeof(((PTDI_ADDRESS_IP) TAAddr->Address)->sin_zero));
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
TcpQueryInfo(PFILE_OBJECT FileObject, PGPC_IP_PATTERN pTcpPattern, ULONG RemoteAddress, USHORT RemotePort)
|
|
{
|
|
PIRP pIrp = NULL;
|
|
PMDL pMdl = NULL;
|
|
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
|
|
PVOID pBuffer = NULL;
|
|
PVOID pInputBuf = NULL;
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
|
|
TDI_CONNECTION_INFORMATION ConnInfo;
|
|
UCHAR InputBuf[FIELD_OFFSET(TRANSPORT_ADDRESS, Address) +
|
|
FIELD_OFFSET(TA_ADDRESS, Address) + sizeof(TDI_ADDRESS_IP)];
|
|
PTRANSPORT_ADDRESS Address1;
|
|
PTA_ADDRESS Address2;
|
|
PGPC_IP_PATTERN * ppPattern;
|
|
PGPC_TCP_QUERY_CONTEXT pContext= NULL;
|
|
|
|
|
|
// IRP for querying TCPIP
|
|
//Charge quota for outstanding IRPs
|
|
try{
|
|
pIrp = IoAllocateIrp((CCHAR)(FileObject->DeviceObject->StackSize), TRUE);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER){
|
|
pIrp = NULL;
|
|
}
|
|
//Check for allocation failure
|
|
if (pIrp == NULL){
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Build output buf
|
|
// Allocate Space for GPC_TCP_QUERY_CONTEXT
|
|
//
|
|
|
|
GpcAllocMemWithQuota(&pBuffer, ROUTING_INFO_ADDR_2_SIZE, TcpQueryContextTag);
|
|
// Check for allocation failure
|
|
if (pBuffer==NULL) {
|
|
Status = GPC_STATUS_RESOURCES ;
|
|
goto exit;
|
|
}
|
|
// Will use pContext to index into the Data Structure
|
|
pContext = (PGPC_TCP_QUERY_CONTEXT)pBuffer;
|
|
|
|
// Store our pointer to Pattern at the beginning of the Buffer
|
|
(pContext->pTcpPattern) = pTcpPattern;
|
|
|
|
|
|
// Initialize with Remote Ports
|
|
// Will disregard these values if we get back 2 addresses from TCP
|
|
pTcpPattern->DstAddr = RemoteAddress;
|
|
pTcpPattern->gpcDstPort = RemotePort;
|
|
|
|
// Jump over the pointer stored
|
|
pBuffer = &(pContext->RouteInfo);
|
|
|
|
// Charge Quota for the MDL allocation
|
|
try{
|
|
pMdl = IoAllocateMdl(pBuffer, ROUTING_INFO_ADDR_2_SIZE-
|
|
FIELD_OFFSET(GPC_TCP_QUERY_CONTEXT ,RouteInfo), FALSE, TRUE, NULL);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER){
|
|
pMdl = NULL;
|
|
}
|
|
|
|
//Check for allocation failure
|
|
if (pMdl==NULL) {
|
|
Status = GPC_STATUS_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
|
|
// Should not Fail
|
|
MmBuildMdlForNonPagedPool(pMdl);
|
|
|
|
// Store our pointer to MDL in the context
|
|
pContext->pMdl = pMdl;
|
|
|
|
//
|
|
// Build the input buf
|
|
// Not Expected to Fail
|
|
BuildTDIAddress(InputBuf, RemoteAddress, RemotePort);
|
|
ConnInfo.RemoteAddress = InputBuf;
|
|
ConnInfo.RemoteAddressLength = 100;
|
|
|
|
// Should not Fail
|
|
TdiBuildQueryInformationEx(pIrp,
|
|
FileObject->DeviceObject,
|
|
FileObject,
|
|
(PIO_COMPLETION_ROUTINE)TcpQueryInfoComplete,
|
|
pBuffer, // Context in completion call
|
|
TDI_QUERY_ROUTING_INFO,
|
|
pMdl,
|
|
&ConnInfo);
|
|
|
|
// Completion handler always called, don't care on return value.
|
|
Status=IoCallDriver(FileObject->DeviceObject, pIrp);
|
|
|
|
// Per SanjayKa the call should always completely syncrhonously
|
|
// If we made the call memory would be released in our
|
|
// completion function
|
|
return Status;
|
|
exit:
|
|
// release all memory
|
|
//allocated before returning
|
|
// if we failed at some
|
|
// stage
|
|
if (pMdl){
|
|
IoFreeMdl(pMdl);
|
|
}
|
|
if (pContext){
|
|
GpcFreeMem(pContext,TcpQueryContextTag);
|
|
}
|
|
if (pIrp){
|
|
IoFreeIrp(pIrp);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Function: ProxyGpcRegisterClient
|
|
//
|
|
// Parameters:
|
|
// PVOID ioBuffer: The buffer containing request and result
|
|
// ULONG inputBufferLength: Size of Request
|
|
// ULONG * outputBufferLength: Size of Result
|
|
// PFILE_OBJECT: The client's file object
|
|
//
|
|
// Result: The Status of the Call. an fail the IRP or return failure in the
|
|
// GPC result structure
|
|
// Environment:
|
|
// For use with the new REGISTER_CLIENT and REGISTER_CLIENT_EX
|
|
// Ioctls
|
|
NTSTATUS
|
|
ProxyGpcRegisterClient(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PFILE_OBJECT FileObject,
|
|
BOOLEAN fNewInterface
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PCLIENT_BLOCK pClient;
|
|
PGPC_REGISTER_CLIENT_REQ GpcReq;
|
|
PGPC_REGISTER_CLIENT_RES GpcRes;
|
|
|
|
if (inputBufferLength < sizeof(GPC_REGISTER_CLIENT_REQ)
|
|
||
|
|
*outputBufferLength < sizeof(GPC_REGISTER_CLIENT_RES)
|
|
) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
|
|
if (fNewInterface){
|
|
// Do not allow HTTP to use this IOCTL interface
|
|
//
|
|
if ( KernelMode == ExGetPreviousMode() ){
|
|
return GPC_STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
GpcReq = (PGPC_REGISTER_CLIENT_REQ)ioBuffer;
|
|
GpcRes = (PGPC_REGISTER_CLIENT_RES)ioBuffer;
|
|
|
|
|
|
// Old IOCTL interface locked down to QOS only
|
|
if (GpcReq->CfId != GPC_CF_QOS) return GPC_STATUS_INVALID_PARAMETER;
|
|
|
|
|
|
// Check the flags the user is allowed to pass in
|
|
if (!((GpcReq->Flags == 0) ||(GpcReq->Flags == GPC_FLAGS_FRAGMENT))){
|
|
return GPC_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
// Check the number of priorities requested
|
|
if (GpcReq->MaxPriorities > GPC_PRIORITY_MAX) {
|
|
return GPC_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// ClientContext not validated since we dont do anything
|
|
// with it except return it to the client
|
|
|
|
if (fNewInterface){
|
|
Status = GpcRegisterClient(GpcReq->CfId,
|
|
GpcReq->Flags | GPC_FLAGS_USERMODE_CLIENT
|
|
|GPC_FLAGS_USERMODE_CLIENT_EX,
|
|
GpcReq->MaxPriorities,
|
|
&CallBackProxyList,
|
|
GpcReq->ClientContext,
|
|
(PGPC_HANDLE)&pClient);
|
|
}
|
|
else{
|
|
Status = GpcRegisterClient(GpcReq->CfId,
|
|
GpcReq->Flags | GPC_FLAGS_USERMODE_CLIENT,
|
|
GpcReq->MaxPriorities,
|
|
&CallBackProxyList,
|
|
GpcReq->ClientContext,
|
|
(PGPC_HANDLE)&pClient);
|
|
}
|
|
|
|
|
|
ASSERT(Status != GPC_STATUS_PENDING);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
ASSERT(pClient);
|
|
|
|
pClient->pFileObject = FileObject;
|
|
|
|
GpcRes->ClientHandle = AllocateHandle(&pClient->ClHandle, (PVOID)pClient);
|
|
|
|
if (GpcRes->ClientHandle == NULL)
|
|
{
|
|
//Got a NULL Handle release the CLIENT_BLOCK
|
|
GpcDeregisterClient(pClient);
|
|
Status = GPC_STATUS_RESOURCES;
|
|
}
|
|
}
|
|
|
|
GpcRes->Status = Status;
|
|
|
|
*outputBufferLength = sizeof(GPC_REGISTER_CLIENT_RES);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Function: ProxyGpcDeregisterClientEx
|
|
//
|
|
// Parameters:
|
|
// PVOID ioBuffer: The buffer containing request and result
|
|
// ULONG inputBufferLength: Size of Request
|
|
// ULONG * outputBufferLength: Size of Result
|
|
// PFILE_OBJECT: The client's file object
|
|
//
|
|
// Result: The Status of the Call. can fail the IRP or return failure in the
|
|
// GPC result structure
|
|
// Environment:
|
|
// For use with the new DEREGISTER_CLIENT_EX and DEREGISTER_CLIENT
|
|
// ioctls
|
|
|
|
NTSTATUS
|
|
ProxyGpcDeregisterClient(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PFILE_OBJECT FileObject,
|
|
BOOLEAN fNewInterface
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PGPC_DEREGISTER_CLIENT_REQ GpcReq;
|
|
PGPC_DEREGISTER_CLIENT_RES GpcRes;
|
|
GPC_HANDLE GpcClientHandle;
|
|
|
|
if (inputBufferLength < sizeof(GPC_DEREGISTER_CLIENT_REQ)
|
|
||
|
|
*outputBufferLength < sizeof(GPC_DEREGISTER_CLIENT_RES)
|
|
) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (fNewInterface){
|
|
// Do not allow HTTP to use this IOCTL interface
|
|
//
|
|
if ( KernelMode == ExGetPreviousMode() ){
|
|
return GPC_STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
GpcReq = (PGPC_DEREGISTER_CLIENT_REQ)ioBuffer;
|
|
GpcRes = (PGPC_DEREGISTER_CLIENT_RES)ioBuffer;
|
|
|
|
//Validate that the client handle maps on to a CLIENT_BLOCK
|
|
GpcClientHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->ClientHandle,
|
|
GPC_ENUM_CLIENT_TYPE,
|
|
'PGDC');
|
|
|
|
if (!GpcClientHandle) {
|
|
Status = GPC_STATUS_INVALID_HANDLE;
|
|
goto exit;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate that the client handle is owned by the
|
|
// calling user mode app.
|
|
Status = GpcValidateClientOwner(GpcClientHandle, FileObject);
|
|
if (STATUS_SUCCESS != Status){
|
|
goto exit;
|
|
}
|
|
|
|
Status = GpcDeregisterClient(GpcClientHandle);
|
|
|
|
exit:
|
|
|
|
if (GpcClientHandle) {
|
|
REFDEL(&((PCLIENT_BLOCK)GpcClientHandle)->RefCount, 'PGDC');
|
|
}
|
|
|
|
|
|
ASSERT(Status != GPC_STATUS_PENDING);
|
|
|
|
GpcRes->Status = Status;
|
|
|
|
*outputBufferLength = sizeof(GPC_DEREGISTER_CLIENT_RES);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Function for Handling New IOCTL
|
|
// for adding cfinfo now open
|
|
// to all users.
|
|
//
|
|
// Function: ProxyGpcAddCfInfoEx
|
|
//
|
|
// Parameters:
|
|
// PVOID ioBuffer: The buffer containing request and result
|
|
// ULONG inputBufferLength: Size of Request
|
|
// ULONG * outputBufferLength: Size of Result
|
|
// PIRP Irp: The Irp
|
|
//
|
|
// Result: The Status of the Call. One of GPC_STATUS_VALUES
|
|
// documented in gpcifc.h
|
|
// Environment:
|
|
// To be used in response tos calls from winsock helper dll
|
|
// for installing QOS flows for TCP sockets.
|
|
NTSTATUS
|
|
ProxyGpcAddCfInfoEx(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PIRP Irp,
|
|
PFILE_OBJECT FileObject
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
GPC_HANDLE GpcClientHandle = NULL;
|
|
PGPC_ADD_CF_INFO_EX_REQ GpcReq = NULL;
|
|
PGPC_ADD_CF_INFO_RES GpcRes = NULL;
|
|
PBLOB_BLOCK pBlob = NULL;
|
|
QUEUED_COMPLETION QItem;
|
|
UNICODE_STRING CfInfoName;
|
|
USHORT NameLen = 0;
|
|
PGPC_IP_PATTERN pTcpPattern = NULL;
|
|
|
|
|
|
if (inputBufferLength < sizeof(GPC_ADD_CF_INFO_EX_REQ)
|
|
||
|
|
*outputBufferLength < sizeof(GPC_ADD_CF_INFO_RES)
|
|
) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
|
|
// Do not allow HTTP to use this IOCTL interface
|
|
//
|
|
if ( KernelMode == ExGetPreviousMode() )
|
|
{
|
|
return GPC_STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
GpcReq = (PGPC_ADD_CF_INFO_EX_REQ)ioBuffer;
|
|
GpcRes = (PGPC_ADD_CF_INFO_RES)ioBuffer;
|
|
|
|
|
|
// Validating CfInfo and CfInfoSize
|
|
if (GpcReq->CfInfoSize >
|
|
inputBufferLength - FIELD_OFFSET(GPC_ADD_CF_INFO_EX_REQ, CfInfo)) {
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
|
|
// Should be atleast 1 byte : minimum size of variable length array
|
|
if (GpcReq->CfInfoSize < sizeof(CHAR)) return GPC_STATUS_INVALID_PARAMETER;
|
|
|
|
// NULL Handle
|
|
if (NULL == GpcReq->FileHandle) return GPC_STATUS_INVALID_HANDLE;
|
|
|
|
// Validate that the client handle maps on to a CLIENT_BLOCK
|
|
GpcClientHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->ClientHandle,
|
|
GPC_ENUM_CLIENT_TYPE,
|
|
'PGAC');
|
|
//Failed to get the client handle
|
|
if (!GpcClientHandle){
|
|
Status = GPC_STATUS_INVALID_HANDLE;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Validate that the client handle is owned by the
|
|
// calling user mode app.
|
|
//
|
|
Status = GpcValidateClientOwner(GpcClientHandle, FileObject);
|
|
if (Status != GPC_STATUS_SUCCESS) {
|
|
// Need to release reference to CLIENT_BLOCK acquired earlier
|
|
//
|
|
goto exit;
|
|
}
|
|
|
|
// This is the new functionality
|
|
// Validate who owns the socket.
|
|
// Get the fully specified pattern from TCP
|
|
// Let's validate the request: file-object must be present
|
|
//
|
|
|
|
|
|
// 1. Get the file object for the file handle
|
|
Status = ObReferenceObjectByHandle(GpcReq->FileHandle,
|
|
0,
|
|
NULL,
|
|
KernelMode,
|
|
&FileObject,
|
|
NULL);
|
|
|
|
if (Status != GPC_STATUS_SUCCESS) {
|
|
goto exit;
|
|
}
|
|
|
|
GpcAllocMemWithQuota(&pTcpPattern , sizeof(GPC_IP_PATTERN), TcpPatternTag);
|
|
|
|
|
|
if (pTcpPattern == NULL) {
|
|
goto exit;
|
|
}
|
|
|
|
|
|
// The IOCTL sent by this function completes synchronously
|
|
// Expect the GPC_IP_PATTERN in pTcpPattern
|
|
//
|
|
Status = TcpQueryInfo(FileObject, pTcpPattern,GpcReq->RemoteAddress,GpcReq->RemotePort);
|
|
|
|
if (STATUS_SUCCESS != Status)
|
|
goto exit;
|
|
|
|
Status = privateGpcAddCfInfo(GpcClientHandle,
|
|
GpcReq->CfInfoSize,
|
|
&GpcReq->CfInfo,
|
|
GpcReq->ClientCfInfoContext,
|
|
FileObject,
|
|
pTcpPattern,
|
|
(PGPC_HANDLE)&pBlob);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// including PENDING
|
|
//
|
|
if (Status == GPC_STATUS_PENDING) {
|
|
|
|
QItem.OpCode = OP_ADD_CFINFO;
|
|
QItem.ClientHandle = GpcClientHandle;
|
|
QItem.CfInfoHandle = (GPC_HANDLE)pBlob;
|
|
|
|
Status = CheckQueuedCompletion(&QItem, Irp);
|
|
|
|
}
|
|
|
|
if (Status == GPC_STATUS_SUCCESS) {
|
|
GPC_STATUS st = GPC_STATUS_FAILURE;
|
|
GPC_CLIENT_HANDLE NotifiedClientCtx = pBlob->NotifiedClientCtx;
|
|
PCLIENT_BLOCK pNotifiedClient = pBlob->pNotifiedClient;
|
|
|
|
GpcRes->GpcCfInfoHandle = (GPC_HANDLE)AllocateHandle(&pBlob->ClHandle, (PVOID)pBlob);
|
|
|
|
// what if we cant allocate a handle? fail the add!
|
|
if (!GpcRes->GpcCfInfoHandle) {
|
|
|
|
GpcRemoveCfInfo(GpcClientHandle,
|
|
pBlob);
|
|
|
|
Status = GPC_STATUS_RESOURCES;
|
|
// This memory is freed in
|
|
// GpcRemoveCfInfo
|
|
// due to dereferencing that blob
|
|
pTcpPattern = NULL;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
if (pNotifiedClient) {
|
|
|
|
if (pNotifiedClient->FuncList.ClGetCfInfoName &&
|
|
NotifiedClientCtx) {
|
|
|
|
st = pNotifiedClient->FuncList.ClGetCfInfoName(
|
|
pNotifiedClient->ClientCtx,
|
|
NotifiedClientCtx,
|
|
&CfInfoName
|
|
);
|
|
if (CfInfoName.Length >= MAX_STRING_LENGTH * sizeof(WCHAR))
|
|
CfInfoName.Length = (MAX_STRING_LENGTH-1) * sizeof(WCHAR);
|
|
//
|
|
// RajeshSu claims this can never happen.
|
|
//
|
|
ASSERT(NT_SUCCESS(st));
|
|
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(st)) {
|
|
|
|
//
|
|
// copy the instance name
|
|
//
|
|
|
|
GpcRes->InstanceNameLength = NameLen = CfInfoName.Length;
|
|
RtlMoveMemory(GpcRes->InstanceName,
|
|
CfInfoName.Buffer,
|
|
CfInfoName.Length
|
|
);
|
|
} else {
|
|
|
|
//
|
|
// generate a default name
|
|
//
|
|
|
|
if (NotifiedClientCtx)
|
|
swprintf(GpcRes->InstanceName, L"Flow %08X", NotifiedClientCtx);
|
|
else
|
|
swprintf(GpcRes->InstanceName, L"Flow <unkonwn name>");
|
|
GpcRes->InstanceNameLength = NameLen = wcslen(GpcRes->InstanceName)*sizeof(WCHAR);
|
|
|
|
}
|
|
|
|
GpcRes->InstanceName[GpcRes->InstanceNameLength/sizeof(WCHAR)] = L'\0';
|
|
|
|
} else {
|
|
|
|
pBlob = NULL;
|
|
// The pattern is freed along with the blob
|
|
// We have a guarantee that it was at least
|
|
// associated with the blob synchronously
|
|
pTcpPattern = NULL;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// This memory is freed in
|
|
// PrivateGpcAddCfInfoEx
|
|
// if that function fails
|
|
pTcpPattern = NULL;
|
|
}
|
|
|
|
//
|
|
// release the ref count we got earlier
|
|
//
|
|
exit:
|
|
|
|
if (GpcClientHandle) {
|
|
//
|
|
// release the ref count we got earlier
|
|
//
|
|
REFDEL(&((PCLIENT_BLOCK)GpcClientHandle)->RefCount, 'PGAC');
|
|
}
|
|
if (!NT_SUCCESS(Status) && (pTcpPattern)){
|
|
GpcFreeMem(pTcpPattern, TcpPatternTag);
|
|
}
|
|
|
|
|
|
GpcRes->InstanceNameLength = NameLen;
|
|
GpcRes->Status = Status;
|
|
*outputBufferLength = sizeof(GPC_ADD_CF_INFO_RES);
|
|
|
|
return (Status == GPC_STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
// Function: ProxyGpcAddCfInfo
|
|
//
|
|
// Parameters:
|
|
// PVOID ioBuffer: The buffer containing request and result
|
|
// ULONG inputBufferLength: Size of Request
|
|
// ULONG * outputBufferLength: Size of Result
|
|
// PIRP Irp: The Irp
|
|
//
|
|
// Result: The Status of the Call. One of GPC_STATUS_VALUES
|
|
// documented in gpcifc.h
|
|
// Environment:
|
|
// To be used in response to calls from HTTP.sys and traffic.dll
|
|
// for installing QOS flows for TCP sockets.
|
|
NTSTATUS
|
|
ProxyGpcAddCfInfo(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PIRP Irp,
|
|
PFILE_OBJECT FileObject
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
GPC_HANDLE GpcClientHandle;
|
|
PGPC_ADD_CF_INFO_REQ GpcReq;
|
|
PGPC_ADD_CF_INFO_RES GpcRes;
|
|
PBLOB_BLOCK pBlob = NULL;
|
|
QUEUED_COMPLETION QItem;
|
|
UNICODE_STRING CfInfoName;
|
|
USHORT NameLen = 0;
|
|
|
|
if (inputBufferLength < sizeof(GPC_ADD_CF_INFO_REQ)
|
|
||
|
|
*outputBufferLength < sizeof(GPC_ADD_CF_INFO_RES)
|
|
) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
GpcReq = (PGPC_ADD_CF_INFO_REQ)ioBuffer;
|
|
GpcRes = (PGPC_ADD_CF_INFO_RES)ioBuffer;
|
|
|
|
|
|
//Validate CfInfo and CfInfoSize
|
|
if (GpcReq->CfInfoSize >
|
|
inputBufferLength - FIELD_OFFSET(GPC_ADD_CF_INFO_REQ, CfInfo)) {
|
|
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
|
|
if (GpcReq->CfInfoSize < 1) return GPC_STATUS_INVALID_PARAMETER;
|
|
|
|
//Validate that the client handle maps on to a CLIENT_BLOCK
|
|
GpcClientHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->ClientHandle,
|
|
GPC_ENUM_CLIENT_TYPE,
|
|
'PGAC');
|
|
if (GpcClientHandle) {
|
|
|
|
//
|
|
// Validate that the client handle is owned by the
|
|
// calling user mode app.
|
|
//
|
|
Status = GpcValidateClientOwner(GpcClientHandle, FileObject);
|
|
if (Status == GPC_STATUS_SUCCESS)
|
|
{
|
|
Status = privateGpcAddCfInfo(GpcClientHandle,
|
|
GpcReq->CfInfoSize,
|
|
&GpcReq->CfInfo,
|
|
GpcReq->ClientCfInfoContext,
|
|
NULL,
|
|
NULL,
|
|
(PGPC_HANDLE)&pBlob);
|
|
}
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// including PENDING
|
|
//
|
|
|
|
if (Status == GPC_STATUS_PENDING) {
|
|
|
|
QItem.OpCode = OP_ADD_CFINFO;
|
|
QItem.ClientHandle = GpcClientHandle;
|
|
QItem.CfInfoHandle = (GPC_HANDLE)pBlob;
|
|
|
|
Status = CheckQueuedCompletion(&QItem, Irp);
|
|
|
|
}
|
|
|
|
if (Status == GPC_STATUS_SUCCESS) {
|
|
|
|
GPC_STATUS st = GPC_STATUS_FAILURE;
|
|
GPC_CLIENT_HANDLE NotifiedClientCtx = pBlob->NotifiedClientCtx;
|
|
PCLIENT_BLOCK pNotifiedClient = pBlob->pNotifiedClient;
|
|
|
|
GpcRes->GpcCfInfoHandle = (GPC_HANDLE)AllocateHandle(&pBlob->ClHandle, (PVOID)pBlob);
|
|
|
|
// what if we cant allocate a handle? fail the add!
|
|
if (!GpcRes->GpcCfInfoHandle) {
|
|
|
|
GpcRemoveCfInfo(GpcClientHandle,
|
|
pBlob);
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
if (pNotifiedClient) {
|
|
|
|
if (pNotifiedClient->FuncList.ClGetCfInfoName &&
|
|
NotifiedClientCtx) {
|
|
|
|
st = pNotifiedClient->FuncList.ClGetCfInfoName(
|
|
pNotifiedClient->ClientCtx,
|
|
NotifiedClientCtx,
|
|
&CfInfoName
|
|
);
|
|
if (CfInfoName.Length >= MAX_STRING_LENGTH * sizeof(WCHAR))
|
|
CfInfoName.Length = (MAX_STRING_LENGTH-1) * sizeof(WCHAR);
|
|
//
|
|
// RajeshSu claims this can never happen.
|
|
//
|
|
ASSERT(NT_SUCCESS(st));
|
|
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(st)) {
|
|
|
|
//
|
|
// copy the instance name
|
|
//
|
|
|
|
GpcRes->InstanceNameLength = NameLen = CfInfoName.Length;
|
|
RtlMoveMemory(GpcRes->InstanceName,
|
|
CfInfoName.Buffer,
|
|
CfInfoName.Length
|
|
);
|
|
} else {
|
|
|
|
//
|
|
// generate a default name
|
|
//
|
|
|
|
if (NotifiedClientCtx)
|
|
swprintf(GpcRes->InstanceName, L"Flow %08X", NotifiedClientCtx);
|
|
else
|
|
swprintf(GpcRes->InstanceName, L"Flow <unkonwn name>");
|
|
GpcRes->InstanceNameLength = NameLen = wcslen(GpcRes->InstanceName)*sizeof(WCHAR);
|
|
|
|
}
|
|
|
|
GpcRes->InstanceName[GpcRes->InstanceNameLength/sizeof(WCHAR)] = L'\0';
|
|
|
|
} else {
|
|
|
|
pBlob = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// release the ref count we got earlier
|
|
//
|
|
exit:
|
|
|
|
REFDEL(&((PCLIENT_BLOCK)GpcClientHandle)->RefCount, 'PGAC');
|
|
|
|
} else {
|
|
|
|
ASSERT(pBlob == NULL);
|
|
Status = GPC_STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
GpcRes->InstanceNameLength = NameLen;
|
|
GpcRes->Status = Status;
|
|
*outputBufferLength = sizeof(GPC_ADD_CF_INFO_RES);
|
|
|
|
return (Status == GPC_STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ProxyGpcAddPattern(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PFILE_OBJECT FileObject,
|
|
BOOLEAN fNewInterface
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
GPC_HANDLE GpcClientHandle=NULL;
|
|
GPC_HANDLE GpcCfInfoHandle=NULL;
|
|
CLASSIFICATION_HANDLE ClassificationHandle = 0;
|
|
PGPC_ADD_PATTERN_REQ GpcReq;
|
|
PGPC_ADD_PATTERN_RES GpcRes;
|
|
PVOID Pattern;
|
|
PVOID Mask;
|
|
PPATTERN_BLOCK pPattern;
|
|
PBLOB_BLOCK pBlob = NULL ;
|
|
|
|
if (inputBufferLength < sizeof(GPC_ADD_PATTERN_REQ)
|
|
||
|
|
*outputBufferLength < sizeof(GPC_ADD_PATTERN_RES)
|
|
) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (fNewInterface){
|
|
// Do not allow HTTP to use this IOCTL interface
|
|
//
|
|
if ( KernelMode == ExGetPreviousMode() ){
|
|
return GPC_STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
GpcReq = (PGPC_ADD_PATTERN_REQ)ioBuffer;
|
|
GpcRes = (PGPC_ADD_PATTERN_RES)ioBuffer;
|
|
|
|
|
|
//Validate PatternSize and Size of PatternAndMask array
|
|
if (GpcReq->PatternSize > MAX_PATTERN_SIZE) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (inputBufferLength - FIELD_OFFSET(GPC_ADD_PATTERN_REQ, PatternAndMask)
|
|
< 2 * GpcReq->PatternSize) {
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
//Validate that the client handle maps on to a CLIENT_BLOCK
|
|
GpcClientHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->ClientHandle,
|
|
GPC_ENUM_CLIENT_TYPE,
|
|
'PGAP');
|
|
//
|
|
// Validate that the cfInfo handle maps on to a BLOB_BLOCK
|
|
GpcCfInfoHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->GpcCfInfoHandle,
|
|
GPC_ENUM_CFINFO_TYPE,
|
|
'PGAP');
|
|
|
|
if (!(GpcClientHandle && GpcCfInfoHandle)){
|
|
Status = GPC_STATUS_INVALID_HANDLE;
|
|
goto exit;
|
|
}
|
|
|
|
Pattern = (PVOID)&GpcReq->PatternAndMask;
|
|
Mask = (PVOID)((PCHAR)(&GpcReq->PatternAndMask) + GpcReq->PatternSize);
|
|
|
|
//
|
|
// Validate that the client handle is owned by the
|
|
// calling user mode app.
|
|
//
|
|
Status = GpcValidateClientOwner(GpcClientHandle, FileObject);
|
|
if ( GPC_STATUS_SUCCESS != Status ){
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Validate that the client owns the cfinfo
|
|
// to which he wants to link the pattern to
|
|
//
|
|
Status = GpcValidateCfinfoOwner(GpcClientHandle,GpcCfInfoHandle);
|
|
if (GPC_STATUS_SUCCESS != Status) {
|
|
goto exit;
|
|
}
|
|
|
|
pBlob = (PBLOB_BLOCK)GpcCfInfoHandle;
|
|
|
|
// Priority and Protocol Template Validation
|
|
if (GpcReq->Priority >=
|
|
((PCLIENT_BLOCK)(GpcClientHandle))->pCfBlock->MaxPriorities ||
|
|
GpcReq->ProtocolTemplate >= GPC_PROTOCOL_TEMPLATE_MAX ) {
|
|
Status = GPC_STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
// EX IOCTL users should already have their
|
|
// GPC_IP_PATTERN hanging off the
|
|
// BLOB_BLOCK
|
|
if ((fNewInterface) && (!pBlob->Pattern)) {
|
|
Status = GPC_STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
//EX IOCTL users only
|
|
if (fNewInterface){
|
|
//Extra code for the new IOCTL.
|
|
// The cfinfo should contain a valid pattern pointer
|
|
// receiverd during the previous call to ProxyGpcAddCfinfoEx
|
|
RtlCopyMemory(Pattern, pBlob->Pattern, sizeof(GPC_IP_PATTERN));
|
|
RtlFillMemory(Mask, sizeof(GPC_IP_PATTERN), 0xff);
|
|
}
|
|
|
|
|
|
Status = GpcAddPattern(GpcClientHandle,
|
|
GpcReq->ProtocolTemplate,
|
|
Pattern,
|
|
Mask,
|
|
GpcReq->Priority,
|
|
GpcCfInfoHandle,
|
|
(PGPC_HANDLE)&pPattern,
|
|
&ClassificationHandle);
|
|
|
|
|
|
|
|
if (Status == GPC_STATUS_SUCCESS) {
|
|
ASSERT(Pattern);
|
|
|
|
GpcRes->GpcPatternHandle = AllocateHandle(&pPattern->ClHandle, (PVOID)pPattern);
|
|
|
|
//
|
|
// In certain circs, alloc_HF_handle could return 0.
|
|
// check for that and clean up the mess.
|
|
//
|
|
if (!GpcRes->GpcPatternHandle) {
|
|
//
|
|
// remove the pattern that was just added.
|
|
//
|
|
Status = GpcRemovePattern(GpcClientHandle,
|
|
pPattern);
|
|
|
|
//
|
|
// This was really the problem why we got a NULL handle
|
|
//
|
|
Status = GPC_STATUS_RESOURCES;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
exit:
|
|
if (GpcCfInfoHandle) {
|
|
|
|
REFDEL(&((PBLOB_BLOCK)GpcCfInfoHandle)->RefCount, 'PGAP');
|
|
}
|
|
|
|
if (GpcClientHandle) {
|
|
|
|
//
|
|
// release the ref count we got earlier
|
|
//
|
|
|
|
REFDEL(&((PCLIENT_BLOCK)GpcClientHandle)->RefCount, 'PGAP');
|
|
}
|
|
|
|
ASSERT(Status != GPC_STATUS_PENDING);
|
|
|
|
GpcRes->Status = Status;
|
|
GpcRes->ClassificationHandle = ClassificationHandle;
|
|
|
|
*outputBufferLength = sizeof(GPC_ADD_PATTERN_RES);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ProxyGpcModifyCfInfo(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PIRP Irp,
|
|
PFILE_OBJECT FileObject
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
GPC_HANDLE GpcClientHandle;
|
|
GPC_HANDLE GpcCfInfoHandle;
|
|
PGPC_MODIFY_CF_INFO_REQ GpcReq;
|
|
PGPC_MODIFY_CF_INFO_RES GpcRes;
|
|
QUEUED_COMPLETION QItem;
|
|
|
|
if (inputBufferLength < sizeof(GPC_MODIFY_CF_INFO_REQ)
|
|
||
|
|
*outputBufferLength < sizeof(GPC_MODIFY_CF_INFO_RES)
|
|
) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
GpcReq = (PGPC_MODIFY_CF_INFO_REQ)ioBuffer;
|
|
GpcRes = (PGPC_MODIFY_CF_INFO_RES)ioBuffer;
|
|
|
|
if (GpcReq->CfInfoSize >
|
|
inputBufferLength - FIELD_OFFSET(GPC_MODIFY_CF_INFO_REQ, CfInfo)) {
|
|
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
// Validate that the client handle maps on to a valid CLIENT_BLOCK
|
|
GpcClientHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->ClientHandle,
|
|
GPC_ENUM_CLIENT_TYPE,
|
|
'PGMP');
|
|
// Validate that the cfInfo handle maps on to a valid BLOB_BLOCK
|
|
GpcCfInfoHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->GpcCfInfoHandle,
|
|
GPC_ENUM_CFINFO_TYPE,
|
|
'PGMP');
|
|
|
|
if (GpcClientHandle && GpcCfInfoHandle) {
|
|
|
|
//
|
|
// Validate that the client handle is owned by the
|
|
// calling user mode app.
|
|
//
|
|
Status = GpcValidateClientOwner(GpcClientHandle, FileObject);
|
|
if (Status == GPC_STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// Validate that the client only modifies a CfInfo that
|
|
// belongs to him.
|
|
//
|
|
Status = GpcValidateCfinfoOwner (GpcClientHandle , GpcCfInfoHandle);
|
|
if ( Status == GPC_STATUS_SUCCESS )
|
|
{
|
|
Status = GpcModifyCfInfo(GpcClientHandle,
|
|
GpcCfInfoHandle,
|
|
GpcReq->CfInfoSize,
|
|
&GpcReq->CfInfo
|
|
);
|
|
}
|
|
}
|
|
if (Status == GPC_STATUS_PENDING) {
|
|
|
|
QItem.OpCode = OP_MODIFY_CFINFO;
|
|
QItem.ClientHandle = GpcClientHandle;
|
|
QItem.CfInfoHandle = GpcCfInfoHandle;
|
|
|
|
Status = CheckQueuedCompletion(&QItem, Irp);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
if (GpcCfInfoHandle) {
|
|
|
|
REFDEL(&((PBLOB_BLOCK)GpcCfInfoHandle)->RefCount, 'PGMP');
|
|
}
|
|
|
|
if (GpcClientHandle) {
|
|
|
|
//
|
|
// release the ref count we got earlier
|
|
//
|
|
|
|
REFDEL(&((PCLIENT_BLOCK)GpcClientHandle)->RefCount, 'PGMP');
|
|
}
|
|
|
|
GpcRes->Status = Status;
|
|
|
|
*outputBufferLength = sizeof(GPC_MODIFY_CF_INFO_RES);
|
|
|
|
return (Status == GPC_STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ProxyGpcRemoveCfInfo(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PIRP Irp,
|
|
PFILE_OBJECT FileObject,
|
|
BOOLEAN fNewInterface
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
GPC_HANDLE GpcClientHandle;
|
|
GPC_HANDLE GpcCfInfoHandle;
|
|
PGPC_REMOVE_CF_INFO_REQ GpcReq;
|
|
PGPC_REMOVE_CF_INFO_RES GpcRes;
|
|
QUEUED_COMPLETION QItem;
|
|
|
|
if (inputBufferLength < sizeof(GPC_REMOVE_CF_INFO_REQ)
|
|
||
|
|
*outputBufferLength < sizeof(GPC_REMOVE_CF_INFO_RES)
|
|
) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (fNewInterface){
|
|
// Do not allow HTTP to use this IOCTL interface
|
|
//
|
|
if ( KernelMode == ExGetPreviousMode() ){
|
|
return GPC_STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
GpcReq = (PGPC_REMOVE_CF_INFO_REQ)ioBuffer;
|
|
GpcRes = (PGPC_REMOVE_CF_INFO_RES)ioBuffer;
|
|
|
|
GpcClientHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->ClientHandle,
|
|
GPC_ENUM_CLIENT_TYPE,
|
|
'PGRC');
|
|
|
|
GpcCfInfoHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->GpcCfInfoHandle,
|
|
GPC_ENUM_CFINFO_TYPE,
|
|
'PGRC');
|
|
|
|
if (GpcClientHandle && GpcCfInfoHandle) {
|
|
|
|
//
|
|
// Validate that the client handle is owned by the
|
|
// calling user mode app.
|
|
//
|
|
Status = GpcValidateClientOwner(GpcClientHandle, FileObject);
|
|
if (Status == GPC_STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// Validate that the client only removes a cfinfo
|
|
// that belongs to him
|
|
//
|
|
Status = GpcValidateCfinfoOwner (GpcClientHandle , GpcCfInfoHandle);
|
|
if ( Status == GPC_STATUS_SUCCESS )
|
|
{
|
|
if (fNewInterface){
|
|
Status = privateGpcRemoveCfInfo(GpcClientHandle,
|
|
GpcCfInfoHandle,
|
|
GPC_FLAGS_USERMODE_CLIENT
|
|
|GPC_FLAGS_USERMODE_CLIENT_EX);
|
|
}
|
|
else{
|
|
Status = privateGpcRemoveCfInfo(GpcClientHandle,
|
|
GpcCfInfoHandle,
|
|
GPC_FLAGS_USERMODE_CLIENT);
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
if (Status == GPC_STATUS_PENDING) {
|
|
|
|
QItem.OpCode = OP_REMOVE_CFINFO;
|
|
QItem.ClientHandle = GpcClientHandle;
|
|
QItem.CfInfoHandle = GpcCfInfoHandle;
|
|
|
|
Status = CheckQueuedCompletion(&QItem, Irp);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
if (GpcCfInfoHandle) {
|
|
|
|
//
|
|
// release the ref count we got earlier
|
|
//
|
|
|
|
REFDEL(&((PBLOB_BLOCK)GpcCfInfoHandle)->RefCount, 'PGRC');
|
|
}
|
|
|
|
if (GpcClientHandle) {
|
|
|
|
//
|
|
// release the ref count we got earlier
|
|
//
|
|
|
|
REFDEL(&((PCLIENT_BLOCK)GpcClientHandle)->RefCount, 'PGRC');
|
|
}
|
|
|
|
GpcRes->Status = Status;
|
|
|
|
*outputBufferLength = sizeof(GPC_REMOVE_CF_INFO_RES);
|
|
|
|
return (Status == GPC_STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ProxyGpcRemovePattern(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PFILE_OBJECT FileObject,
|
|
BOOLEAN fNewInterface
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
GPC_HANDLE GpcClientHandle;
|
|
GPC_HANDLE GpcPatternHandle;
|
|
PGPC_REMOVE_PATTERN_REQ GpcReq;
|
|
PGPC_REMOVE_PATTERN_RES GpcRes;
|
|
|
|
if (inputBufferLength < sizeof(GPC_REMOVE_PATTERN_REQ)
|
|
||
|
|
*outputBufferLength < sizeof(GPC_REMOVE_PATTERN_RES)
|
|
) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (fNewInterface){
|
|
// Do not allow HTTP to use this IOCTL interface
|
|
//
|
|
if ( KernelMode == ExGetPreviousMode() ){
|
|
return GPC_STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
GpcReq = (PGPC_REMOVE_PATTERN_REQ)ioBuffer;
|
|
GpcRes = (PGPC_REMOVE_PATTERN_RES)ioBuffer;
|
|
|
|
GpcClientHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->ClientHandle,
|
|
GPC_ENUM_CLIENT_TYPE,
|
|
'PGRP');
|
|
|
|
GpcPatternHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->GpcPatternHandle,
|
|
GPC_ENUM_PATTERN_TYPE,
|
|
'PGRP');
|
|
|
|
if (!(GpcClientHandle && GpcPatternHandle) ) {
|
|
Status = GPC_STATUS_INVALID_HANDLE;
|
|
goto exit;
|
|
}
|
|
//
|
|
// Validate that the client handle is owned by the
|
|
// calling user mode app.
|
|
//
|
|
Status = GpcValidateClientOwner(GpcClientHandle, FileObject);
|
|
if (GPC_STATUS_SUCCESS != Status) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Validate that the client owns the Pattern that it is trying to delete
|
|
//
|
|
Status = GpcValidatePatternOwner(GpcClientHandle,GpcPatternHandle);
|
|
if (GPC_STATUS_SUCCESS != Status){
|
|
goto exit;
|
|
}
|
|
|
|
Status = GpcRemovePattern(GpcClientHandle,
|
|
GpcPatternHandle);
|
|
|
|
exit:
|
|
|
|
if (GpcPatternHandle) {
|
|
REFDEL(&((PPATTERN_BLOCK)GpcPatternHandle)->RefCount, 'PGRP');
|
|
}
|
|
|
|
if (GpcClientHandle) {
|
|
REFDEL(&((PCLIENT_BLOCK)GpcClientHandle)->RefCount, 'PGRP');
|
|
}
|
|
|
|
ASSERT(Status != GPC_STATUS_PENDING);
|
|
|
|
GpcRes->Status = Status;
|
|
|
|
*outputBufferLength = sizeof(GPC_REMOVE_PATTERN_RES);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ProxyGpcEnumCfInfo(
|
|
PVOID ioBuffer,
|
|
ULONG inputBufferLength,
|
|
ULONG *outputBufferLength,
|
|
PFILE_OBJECT FileObject
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PGPC_ENUM_CFINFO_REQ GpcReq;
|
|
PGPC_ENUM_CFINFO_RES GpcRes;
|
|
ULONG Size;
|
|
ULONG TotalCount;
|
|
GPC_HANDLE GpcClientHandle;
|
|
GPC_HANDLE GpcCfInfoHandle;
|
|
GPC_HANDLE EnumHandle;
|
|
PBLOB_BLOCK pBlob;
|
|
|
|
if (inputBufferLength < sizeof(GPC_ENUM_CFINFO_REQ)
|
|
||
|
|
*outputBufferLength < sizeof(GPC_ENUM_CFINFO_RES)
|
|
) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
GpcReq = (PGPC_ENUM_CFINFO_REQ)ioBuffer;
|
|
GpcRes = (PGPC_ENUM_CFINFO_RES)ioBuffer;
|
|
|
|
GpcClientHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->ClientHandle,
|
|
GPC_ENUM_CLIENT_TYPE,
|
|
'PGEC');
|
|
|
|
GpcCfInfoHandle = (GPC_HANDLE)GetHandleObjectWithRef(GpcReq->EnumHandle,
|
|
GPC_ENUM_CFINFO_TYPE,
|
|
'PGEC');
|
|
|
|
if (GpcReq->EnumHandle != NULL && GpcCfInfoHandle == NULL) {
|
|
|
|
//
|
|
// the flow has been deleted during enumeration
|
|
//
|
|
|
|
Status = STATUS_DATA_ERROR;
|
|
|
|
} else if (GpcClientHandle) {
|
|
|
|
TotalCount = GpcReq->CfInfoCount;
|
|
EnumHandle = GpcReq->EnumHandle;
|
|
Size = *outputBufferLength - FIELD_OFFSET(GPC_ENUM_CFINFO_RES,
|
|
EnumBuffer);
|
|
|
|
//
|
|
// save the blob pointer with the one extra ref count
|
|
// since we called GetHandleObjectWithRef
|
|
//
|
|
|
|
pBlob = (PBLOB_BLOCK)GpcCfInfoHandle;
|
|
|
|
Status = GpcEnumCfInfo(GpcClientHandle,
|
|
&GpcCfInfoHandle,
|
|
&EnumHandle,
|
|
&TotalCount,
|
|
&Size,
|
|
GpcRes->EnumBuffer
|
|
);
|
|
|
|
if (pBlob) {
|
|
|
|
REFDEL(&pBlob->RefCount, 'PGEC');
|
|
|
|
}
|
|
|
|
if (Status == GPC_STATUS_SUCCESS) {
|
|
|
|
//
|
|
// fill in the results
|
|
//
|
|
|
|
GpcRes->TotalCfInfo = TotalCount;
|
|
GpcRes->EnumHandle = EnumHandle;
|
|
*outputBufferLength = Size + FIELD_OFFSET(GPC_ENUM_CFINFO_RES,
|
|
EnumBuffer);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (GpcCfInfoHandle) {
|
|
|
|
REFDEL(&((PBLOB_BLOCK)GpcCfInfoHandle)->RefCount, 'PGEC');
|
|
}
|
|
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
if (GpcClientHandle) {
|
|
|
|
//
|
|
// release the ref count we got earlier
|
|
//
|
|
|
|
REFDEL(&((PCLIENT_BLOCK)GpcClientHandle)->RefCount, 'PGEC');
|
|
}
|
|
|
|
ASSERT(Status != GPC_STATUS_PENDING);
|
|
|
|
GpcRes->Status = Status;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
CancelPendingIrpCfInfo(
|
|
IN PDEVICE_OBJECT Device,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancels an outstanding IRP request for a CfInfo request.
|
|
|
|
Arguments:
|
|
|
|
Device - The device on which the request was issued.
|
|
Irp - Pointer to I/O request packet to cancel.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Notes:
|
|
|
|
This function is called with cancel spinlock held. It must be
|
|
released before the function returns.
|
|
|
|
The cfinfo block associated with this request cannot be
|
|
freed until the request completes. The completion routine will
|
|
free it.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPENDING_IRP pPendingIrp = NULL;
|
|
PPENDING_IRP pItem;
|
|
PLIST_ENTRY pEntry;
|
|
#if DBG
|
|
KIRQL irql = KeGetCurrentIrql();
|
|
HANDLE thrd = PsGetCurrentThreadId();
|
|
#endif
|
|
|
|
DBGPRINT(IOCTL, ("CancelPendingIrpCfInfo: Irp=0x%X\n",
|
|
(ULONG_PTR)Irp));
|
|
|
|
TRACE(IOCTL, Irp, 0, "CancelPendingIrpCfInfo:");
|
|
TRACE(LOCKS, thrd, irql, "CancelPendingIrpCfInfo:");
|
|
|
|
for ( pEntry = PendingIrpCfInfoList.Flink;
|
|
pEntry != &PendingIrpCfInfoList;
|
|
pEntry = pEntry->Flink ) {
|
|
|
|
pItem = CONTAINING_RECORD(pEntry, PENDING_IRP, Linkage);
|
|
|
|
if (pItem->Irp == Irp) {
|
|
|
|
pPendingIrp = pItem;
|
|
GpcRemoveEntryList(pEntry);
|
|
|
|
IoSetCancelRoutine(pPendingIrp->Irp, NULL);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
if (pPendingIrp != NULL) {
|
|
|
|
DBGPRINT(IOCTL, ("CancelPendingIrpCfInfo: found PendingIrp=0x%X\n",
|
|
(ULONG_PTR)pPendingIrp));
|
|
|
|
TRACE(IOCTL, Irp, pPendingIrp, "CancelPendingIrpCfInfo.PendingIrp:");
|
|
|
|
//
|
|
// Free the PENDING_IRP structure. The control block will be freed
|
|
// when the request completes.
|
|
//
|
|
|
|
GpcFreeToLL(pPendingIrp, &PendingIrpLL, PendingIrpTag);
|
|
|
|
//
|
|
// Complete the IRP.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
} else {
|
|
|
|
DBGPRINT(IOCTL, ("CancelPendingIrpCfInfo: PendingIrp not found\n"));
|
|
TRACE(IOCTL, Irp, 0, "CancelPendingIrpCfInfo.NoPendingIrp:");
|
|
|
|
}
|
|
|
|
#if DBG
|
|
irql = KeGetCurrentIrql();
|
|
#endif
|
|
|
|
TRACE(LOCKS, thrd, irql, "CancelPendingIrpCfInfo (end)");
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
CancelPendingIrpNotify(
|
|
IN PDEVICE_OBJECT Device,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancels an outstanding IRP request for a notification.
|
|
|
|
Arguments:
|
|
|
|
Device - The device on which the request was issued.
|
|
Irp - Pointer to I/O request packet to cancel.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Notes:
|
|
|
|
This function is called with cancel spinlock held. It must be
|
|
released before the function returns.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPENDING_IRP pPendingIrp = NULL;
|
|
PPENDING_IRP pItem;
|
|
PLIST_ENTRY pEntry;
|
|
#if DBG
|
|
KIRQL irql = KeGetCurrentIrql();
|
|
HANDLE thrd = PsGetCurrentThreadId();
|
|
#endif
|
|
|
|
DBGPRINT(IOCTL, ("CancelPendingIrpNotify: Irp=0x%X\n",
|
|
(ULONG_PTR)Irp));
|
|
TRACE(IOCTL, Irp, 0, "CancelPendingIrpNotify:");
|
|
TRACE(LOCKS, thrd, irql, "CancelPendingIrpNotify:");
|
|
|
|
for ( pEntry = PendingIrpNotifyList.Flink;
|
|
pEntry != &PendingIrpNotifyList;
|
|
pEntry = pEntry->Flink ) {
|
|
|
|
pItem = CONTAINING_RECORD(pEntry, PENDING_IRP, Linkage);
|
|
|
|
if (pItem->Irp == Irp) {
|
|
|
|
pPendingIrp = pItem;
|
|
GpcRemoveEntryList(pEntry);
|
|
|
|
IoSetCancelRoutine(pPendingIrp->Irp, NULL);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
if (pPendingIrp != NULL) {
|
|
|
|
DBGPRINT(IOCTL, ("CancelPendingIrpNotify: Found a PendingIrp=0x%X\n",
|
|
(ULONG_PTR)pPendingIrp));
|
|
TRACE(IOCTL, Irp, pPendingIrp, "CancelPendingIrpNotify.PendingIrp:");
|
|
|
|
//
|
|
// Free the PENDING_IRP structure.
|
|
//
|
|
|
|
GpcFreeToLL(pPendingIrp, &PendingIrpLL, PendingIrpTag);
|
|
|
|
//
|
|
// Complete the IRP.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
|
|
|
} else {
|
|
|
|
DBGPRINT(IOCTL, ("CancelPendingIrpNotify: PendingIrp not found\n"));
|
|
TRACE(IOCTL, Irp, 0, "CancelPendingIrpNotify.NoPendingIrp:");
|
|
}
|
|
|
|
#if DBG
|
|
irql = KeGetCurrentIrql();
|
|
#endif
|
|
|
|
TRACE(LOCKS, thrd, irql, "CancelPendingIrpNotify (end)");
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
UMClientRemoveCfInfoNotify(
|
|
IN PCLIENT_BLOCK pClient,
|
|
IN PBLOB_BLOCK pBlob
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify user mode client that the CfInfo is deleted.
|
|
This will dequeue a pending IRP and will complete it.
|
|
If there is no pending IRP, a GPC_NOTIFY_REQUEST_RES buffer
|
|
will be queued until we get an IRP down the stack.
|
|
|
|
Arguments:
|
|
|
|
pClient - the notified client
|
|
pBlob - the deleted cfinfo
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
PIRP pIrp;
|
|
PPENDING_IRP pPendingIrp = NULL;
|
|
PLIST_ENTRY pEntry;
|
|
PQUEUED_NOTIFY pQItem;
|
|
PGPC_NOTIFY_REQUEST_RES GpcRes;
|
|
|
|
ASSERT(pClient == pBlob->pOwnerClient);
|
|
|
|
DBGPRINT(IOCTL, ("UMClientRemoveCfInfoNotify: pClient=0x%X, pBlob=0x%X\n",
|
|
(ULONG_PTR)pClient, (ULONG_PTR)pBlob));
|
|
TRACE(IOCTL, pClient, pBlob, "UMClientRemoveCfInfoNotify:");
|
|
|
|
//
|
|
// Find the request IRP on the pending list.
|
|
//
|
|
IoAcquireCancelSpinLock(&oldIrql);
|
|
|
|
for ( pEntry = PendingIrpNotifyList.Flink;
|
|
pEntry != &PendingIrpNotifyList;
|
|
pEntry = pEntry->Flink ) {
|
|
|
|
pPendingIrp = CONTAINING_RECORD( pEntry, PENDING_IRP, Linkage);
|
|
|
|
if (pPendingIrp->FileObject == pClient->pFileObject) {
|
|
|
|
//
|
|
// that's the pending request
|
|
//
|
|
|
|
pIrp = pPendingIrp->Irp;
|
|
IoSetCancelRoutine(pIrp, NULL);
|
|
GpcRemoveEntryList(pEntry);
|
|
break;
|
|
|
|
} else {
|
|
|
|
pPendingIrp = NULL;
|
|
}
|
|
}
|
|
|
|
if (pPendingIrp == NULL) {
|
|
|
|
//
|
|
// No IRP, we need to queue the notification block
|
|
//
|
|
|
|
DBGPRINT(IOCTL, ("UMClientRemoveCfInfoNotify: No pending IRP found\n"
|
|
));
|
|
TRACE(IOCTL,
|
|
pClient->ClientCtx,
|
|
pBlob->arClientCtx[pClient->AssignedIndex],
|
|
"UMClientRemoveCfInfoNotify.NoPendingIrp:");
|
|
|
|
GpcAllocFromLL(&pQItem, &QueuedNotificationLL, QueuedNotificationTag);
|
|
|
|
if (pQItem) {
|
|
|
|
pQItem->FileObject = pClient->pFileObject;
|
|
|
|
//
|
|
// fill the item
|
|
//
|
|
|
|
pQItem->NotifyRes.ClientCtx = pClient->ClientCtx;
|
|
pQItem->NotifyRes.NotificationCtx =
|
|
(ULONG_PTR)pBlob->arClientCtx[pClient->AssignedIndex];
|
|
pQItem->NotifyRes.SubCode = GPC_NOTIFY_CFINFO_CLOSED;
|
|
pQItem->NotifyRes.Reason = 0; // for now...
|
|
pQItem->NotifyRes.Param1 = 0; // for now...
|
|
|
|
GpcInsertTailList(&QueuedNotificationList, &pQItem->Linkage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
|
|
if (pPendingIrp) {
|
|
|
|
//
|
|
// found an IRP, fill and complete
|
|
//
|
|
|
|
DBGPRINT(IOCTL, ("UMClientRemoveCfInfoNotify: Pending IRP found=0x%X\n",
|
|
(ULONG_PTR)pIrp));
|
|
TRACE(IOCTL,
|
|
pClient->ClientCtx,
|
|
pBlob->arClientCtx[pClient->AssignedIndex],
|
|
"UMClientRemoveCfInfoNotify.PendingIrp:");
|
|
|
|
GpcRes = (PGPC_NOTIFY_REQUEST_RES)pIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
GpcRes->ClientCtx = pClient->ClientCtx;
|
|
GpcRes->NotificationCtx =
|
|
(ULONG_PTR)pBlob->arClientCtx[pClient->AssignedIndex];
|
|
GpcRes->SubCode = GPC_NOTIFY_CFINFO_CLOSED;
|
|
GpcRes->Reason = 0; // for now...
|
|
GpcRes->Param1 = 0; // for now...
|
|
|
|
//
|
|
// complete the IRP
|
|
//
|
|
|
|
pIrp->IoStatus.Information = sizeof(GPC_NOTIFY_REQUEST_REQ);
|
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
|
|
|
|
//
|
|
// We can free the pending irp item
|
|
//
|
|
|
|
GpcFreeToLL(pPendingIrp, &PendingIrpLL, PendingIrpTag);
|
|
|
|
}
|
|
|
|
//
|
|
// for now - complete the operation
|
|
// we should probably let the User Mode client do it,
|
|
// but this complicates things a little...
|
|
//
|
|
|
|
GpcRemoveCfInfoNotifyComplete((GPC_HANDLE)pClient,
|
|
(GPC_HANDLE)pBlob,
|
|
GPC_STATUS_SUCCESS
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
UMCfInfoComplete(
|
|
IN GPC_COMPLETION_OP OpCode,
|
|
IN PCLIENT_BLOCK pClient,
|
|
IN PBLOB_BLOCK pBlob,
|
|
IN GPC_STATUS Status
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the completion routine for any pending CfInfo request.
|
|
It will search the pending IRP CfInfo list for a matching CfInfo
|
|
structure that has been stored while the Add, Modify or Remove
|
|
request returned PENDING. The client must be the CfInfo owner,
|
|
otherwise we would have never got here.
|
|
If an IRP is not found, it means the operation completed *before*
|
|
we got back the PENDING status, which is perfectly legal.
|
|
In this case, we queue a completion item and return.
|
|
|
|
Arguments:
|
|
|
|
OpCode - the code of the completion (add, modify or remove)
|
|
pClient - the notified client
|
|
pBlob - the deleted cfinfo
|
|
Status - the reported status
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
typedef union _GPC_CF_INFO_RES {
|
|
GPC_ADD_CF_INFO_RES AddRes;
|
|
GPC_MODIFY_CF_INFO_RES ModifyRes;
|
|
GPC_REMOVE_CF_INFO_RES RemoveRes;
|
|
} GPC_CF_INFO_RES;
|
|
|
|
KIRQL oldIrql;
|
|
PIRP pIrp;
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
PPENDING_IRP pPendingIrp = NULL;
|
|
PLIST_ENTRY pEntry;
|
|
GPC_CF_INFO_RES *GpcRes;
|
|
ULONG outputBuferLength;
|
|
//PQUEUED_COMPLETION pQItem;
|
|
|
|
//ASSERT(pClient == pBlob->pOwnerClient);
|
|
|
|
DBGPRINT(IOCTL, ("UMCfInfoComplete: pClient=0x%X, pBlob=0x%X, Status=0x%X\n",
|
|
(ULONG_PTR)pClient, (ULONG_PTR)pBlob, Status));
|
|
TRACE(IOCTL, OpCode, pClient, "UMCfInfoComplete:");
|
|
TRACE(IOCTL, pBlob, Status, "UMCfInfoComplete:");
|
|
|
|
//
|
|
// Find the request IRP on the pending list.
|
|
//
|
|
IoAcquireCancelSpinLock(&oldIrql);
|
|
|
|
for ( pEntry = PendingIrpCfInfoList.Flink;
|
|
pEntry != &PendingIrpCfInfoList;
|
|
pEntry = pEntry->Flink ) {
|
|
|
|
pPendingIrp = CONTAINING_RECORD( pEntry, PENDING_IRP, Linkage);
|
|
|
|
if (pPendingIrp->QComp.CfInfoHandle == (GPC_HANDLE)pBlob
|
|
&&
|
|
pPendingIrp->QComp.OpCode == OpCode ) {
|
|
|
|
//
|
|
// that's the pending request
|
|
//
|
|
|
|
pIrp = pPendingIrp->Irp;
|
|
ASSERT(pIrp);
|
|
IoSetCancelRoutine(pIrp, NULL);
|
|
GpcRemoveEntryList(pEntry);
|
|
break;
|
|
|
|
} else {
|
|
|
|
pPendingIrp = NULL;
|
|
}
|
|
}
|
|
|
|
if (pPendingIrp == NULL) {
|
|
|
|
//
|
|
// No IRP, we need to queue a completion block
|
|
//
|
|
|
|
DBGPRINT(IOCTL, ("UMCfInfoComplete: No pending IRP found\n"));
|
|
TRACE(IOCTL, pBlob, Status, "UMCfInfoComplete.NoPendingIrp:");
|
|
|
|
GpcAllocFromLL(&pPendingIrp, &PendingIrpLL, PendingIrpTag);
|
|
|
|
if (pPendingIrp) {
|
|
|
|
pPendingIrp->Irp = NULL;
|
|
pPendingIrp->FileObject = pClient->pFileObject;
|
|
pPendingIrp->QComp.OpCode = OpCode;
|
|
pPendingIrp->QComp.ClientHandle = (GPC_HANDLE)pClient;
|
|
pPendingIrp->QComp.CfInfoHandle = (GPC_HANDLE)pBlob;
|
|
pPendingIrp->QComp.Status = Status;
|
|
|
|
GpcInsertTailList(&QueuedCompletionList, &pPendingIrp->Linkage);
|
|
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
|
|
return;
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
|
|
ASSERT(pPendingIrp && pIrp);
|
|
|
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|
|
|
GpcRes = (GPC_CF_INFO_RES *)pIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
DBGPRINT(IOCTL, ("UMCfInfoComplete: Pending IRP found=0x%X, Ioctl=0x%X\n",
|
|
(ULONG_PTR)pIrp,
|
|
pIrpSp->Parameters.DeviceIoControl.IoControlCode
|
|
));
|
|
|
|
TRACE(IOCTL,
|
|
pIrp,
|
|
pIrpSp->Parameters.DeviceIoControl.IoControlCode,
|
|
"UMCfInfoComplete.PendingIrp:");
|
|
|
|
switch (pIrpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_GPC_ADD_CF_INFO:
|
|
|
|
ASSERT(OpCode == OP_ADD_CFINFO);
|
|
ASSERT(pBlob->State == GPC_STATE_ADD);
|
|
|
|
GpcRes->AddRes.Status = Status;
|
|
|
|
GpcRes->AddRes.ClientCtx = pClient->ClientCtx;
|
|
GpcRes->AddRes.CfInfoCtx = pBlob->OwnerClientCtx;
|
|
GpcRes->AddRes.GpcCfInfoHandle = pBlob->ClHandle;
|
|
|
|
if (Status == GPC_STATUS_SUCCESS) {
|
|
|
|
UNICODE_STRING CfInfoName;
|
|
|
|
if (pBlob->pNotifiedClient) {
|
|
|
|
GPC_STATUS st = GPC_STATUS_FAILURE;
|
|
|
|
if (pBlob->pNotifiedClient->FuncList.ClGetCfInfoName) {
|
|
|
|
ASSERT(pBlob->NotifiedClientCtx);
|
|
|
|
pBlob->pNotifiedClient->FuncList.ClGetCfInfoName(
|
|
pBlob->pNotifiedClient->ClientCtx,
|
|
pBlob->NotifiedClientCtx,
|
|
&CfInfoName
|
|
);
|
|
|
|
if (CfInfoName.Length >= MAX_STRING_LENGTH * sizeof(WCHAR))
|
|
CfInfoName.Length = (MAX_STRING_LENGTH-1) * sizeof(WCHAR);
|
|
}
|
|
|
|
if (!NT_SUCCESS(st)) {
|
|
|
|
//
|
|
// generate a default name
|
|
//
|
|
|
|
swprintf(CfInfoName.Buffer, L"Flow %08X", pBlob->NotifiedClientCtx);
|
|
CfInfoName.Length = wcslen(CfInfoName.Buffer)*sizeof(WCHAR);
|
|
|
|
}
|
|
|
|
//
|
|
// copy the instance name
|
|
//
|
|
|
|
GpcRes->AddRes.InstanceNameLength = CfInfoName.Length;
|
|
NdisMoveMemory(GpcRes->AddRes.InstanceName,
|
|
CfInfoName.Buffer,
|
|
CfInfoName.Length
|
|
);
|
|
}
|
|
}
|
|
|
|
outputBuferLength = sizeof(GPC_ADD_CF_INFO_RES);
|
|
break;
|
|
|
|
case IOCTL_GPC_MODIFY_CF_INFO:
|
|
|
|
ASSERT(OpCode == OP_MODIFY_CFINFO);
|
|
ASSERT(pBlob->State == GPC_STATE_MODIFY);
|
|
|
|
GpcRes->ModifyRes.Status = Status;
|
|
GpcRes->ModifyRes.ClientCtx = pClient->ClientCtx;
|
|
GpcRes->ModifyRes.CfInfoCtx = pBlob->OwnerClientCtx;
|
|
|
|
outputBuferLength = sizeof(GPC_MODIFY_CF_INFO_RES);
|
|
break;
|
|
|
|
case IOCTL_GPC_REMOVE_CF_INFO:
|
|
|
|
ASSERT(OpCode == OP_REMOVE_CFINFO);
|
|
ASSERT(pBlob->State == GPC_STATE_REMOVE);
|
|
|
|
GpcRes->RemoveRes.Status = Status;
|
|
GpcRes->RemoveRes.ClientCtx = pClient->ClientCtx;
|
|
GpcRes->RemoveRes.CfInfoCtx = pBlob->OwnerClientCtx;
|
|
|
|
outputBuferLength = sizeof(GPC_REMOVE_CF_INFO_RES);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
|
|
GpcFreeToLL(pPendingIrp, &PendingIrpLL, PendingIrpTag);
|
|
|
|
//
|
|
// Complete the IRP.
|
|
//
|
|
|
|
pIrp->IoStatus.Information = outputBuferLength;
|
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
DBGPRINT(IOCTL, ("UMCfInfoComplete: Completing IRP =0x%X, Status=0x%X\n",
|
|
(ULONG_PTR)pIrp, Status ));
|
|
|
|
TRACE(IOCTL, pIrp, Status, "UMCfInfoComplete.Completing:");
|
|
|
|
IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CheckQueuedNotification(
|
|
IN PIRP Irp,
|
|
IN OUT ULONG *outputBufferLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will check for a queued notification structrue,
|
|
and it will fill the ioBuffer in the IRP if one has been found
|
|
and return STATUS_SUCCESS. This should cover the case where
|
|
the IRP was not available when the notification was generated.
|
|
O/w the routine returns STATUS_PENDING.
|
|
|
|
Arguments:
|
|
|
|
Irp - the incoming IRP
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS or STATUS_PENDING
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
PQUEUED_NOTIFY pQItem = NULL;
|
|
PLIST_ENTRY pEntry;
|
|
NTSTATUS Status;
|
|
PPENDING_IRP pPendingIrp;
|
|
|
|
DBGPRINT(IOCTL, ("CheckQueuedNotification: IRP =0x%X\n",
|
|
(ULONG_PTR)Irp));
|
|
|
|
TRACE(IOCTL, Irp, 0, "CheckQueuedNotification:");
|
|
|
|
if (*outputBufferLength < sizeof(GPC_NOTIFY_REQUEST_RES)) {
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
pIrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
IoAcquireCancelSpinLock(&oldIrql);
|
|
|
|
for ( pEntry = QueuedNotificationList.Flink;
|
|
pEntry != &QueuedNotificationList;
|
|
pEntry = pEntry->Flink ) {
|
|
|
|
pQItem = CONTAINING_RECORD( pEntry, QUEUED_NOTIFY, Linkage);
|
|
|
|
if (pQItem->FileObject == pIrpSp->FileObject) {
|
|
|
|
//
|
|
// the queued item if for this file object
|
|
//
|
|
|
|
GpcRemoveEntryList(pEntry);
|
|
break;
|
|
|
|
} else {
|
|
|
|
pQItem = NULL;
|
|
}
|
|
}
|
|
|
|
if (pQItem) {
|
|
|
|
//
|
|
// We found something on the queue, copy it to the IRP
|
|
// and delete the item
|
|
//
|
|
|
|
DBGPRINT(IOCTL, ("CheckQueuedNotification: found QItem =0x%X\n",
|
|
(ULONG_PTR)pQItem));
|
|
|
|
TRACE(IOCTL,
|
|
pQItem,
|
|
pQItem->NotifyRes.ClientCtx,
|
|
"CheckQueuedNotification.QItem:");
|
|
|
|
ASSERT(*outputBufferLength >= sizeof(GPC_NOTIFY_REQUEST_RES));
|
|
|
|
NdisMoveMemory(Irp->AssociatedIrp.SystemBuffer,
|
|
&pQItem->NotifyRes,
|
|
sizeof(GPC_NOTIFY_REQUEST_RES) );
|
|
|
|
GpcFreeToLL(pQItem, &QueuedNotificationLL, QueuedNotificationTag);
|
|
|
|
*outputBufferLength = sizeof(GPC_NOTIFY_REQUEST_RES);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
DBGPRINT(IOCTL, ("CheckQueuedNotification: QItem not found...PENDING\n"
|
|
));
|
|
|
|
TRACE(IOCTL, 0, 0, "CheckQueuedNotification.NoQItem:");
|
|
|
|
GpcAllocFromLL(&pPendingIrp, &PendingIrpLL, PendingIrpTag);
|
|
|
|
if (pPendingIrp != NULL) {
|
|
|
|
//
|
|
// add the IRP on the pending notification list
|
|
//
|
|
|
|
DBGPRINT(IOCTL, ("CheckQueuedNotification: adding IRP=0x%X to list=0x%X\n",
|
|
(ULONG_PTR)Irp, (ULONG_PTR)pIrpSp ));
|
|
TRACE(IOCTL, Irp, pIrpSp, "CheckQueuedNotification.Irp:");
|
|
|
|
pPendingIrp->Irp = Irp;
|
|
pPendingIrp->FileObject = pIrpSp->FileObject;
|
|
|
|
if (!Irp->Cancel) {
|
|
|
|
IoSetCancelRoutine(Irp, CancelPendingIrpNotify);
|
|
GpcInsertTailList(&PendingIrpNotifyList, &(pPendingIrp->Linkage));
|
|
|
|
Status = STATUS_PENDING;
|
|
|
|
} else {
|
|
|
|
DBGPRINT(IOCTL, ("CheckQueuedNotification: Status Cacelled: IRP=0x%X\n",
|
|
(ULONG_PTR)Irp ));
|
|
|
|
TRACE(IOCTL, Irp, pIrpSp, "CheckQueuedNotification.Cancelled:");
|
|
GpcFreeToLL(pPendingIrp, &PendingIrpLL, PendingIrpTag);
|
|
|
|
Status = STATUS_CANCELLED;
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
*outputBufferLength = 0;
|
|
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CheckQueuedCompletion(
|
|
IN PQUEUED_COMPLETION pQItem,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will check for a queued completion structrue
|
|
with the same CfInfoHandle, and it will return it if found.
|
|
The original queued memory block is release here.
|
|
If not found, the returned status is PENDING, o/w the queued status
|
|
is returned.
|
|
|
|
Arguments:
|
|
|
|
pQItem - pass in the CfInfoHandle and ClientHandle
|
|
|
|
Return Value:
|
|
|
|
Queued status or STATUS_PENDING
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
PLIST_ENTRY pEntry;
|
|
NTSTATUS Status;
|
|
PPENDING_IRP pPendingIrp = NULL;
|
|
PIO_STACK_LOCATION irpStack;
|
|
|
|
DBGPRINT(IOCTL, ("CheckQueuedCompletion: pQItem=0x%X\n",
|
|
(ULONG_PTR)pQItem));
|
|
|
|
TRACE(IOCTL,
|
|
pQItem->OpCode,
|
|
pQItem->ClientHandle,
|
|
"CheckQueuedCompletion:");
|
|
TRACE(IOCTL,
|
|
pQItem->CfInfoHandle,
|
|
pQItem->Status,
|
|
"CheckQueuedCompletion:");
|
|
|
|
IoAcquireCancelSpinLock(&oldIrql);
|
|
|
|
for ( pEntry = QueuedCompletionList.Flink;
|
|
pEntry != &QueuedCompletionList;
|
|
pEntry = pEntry->Flink ) {
|
|
|
|
pPendingIrp = CONTAINING_RECORD( pEntry, PENDING_IRP, Linkage);
|
|
|
|
if ((pQItem->OpCode == OP_ANY_CFINFO ||
|
|
pQItem->OpCode == pPendingIrp->QComp.OpCode)
|
|
&&
|
|
pPendingIrp->QComp.ClientHandle == (PVOID)pQItem->ClientHandle
|
|
&&
|
|
pPendingIrp->QComp.CfInfoHandle == (PVOID)pQItem->CfInfoHandle) {
|
|
|
|
//
|
|
// the queued item if for this file object
|
|
// and the OpCode match
|
|
// and it has the same CfInfo memory pointer
|
|
//
|
|
|
|
GpcRemoveEntryList(pEntry);
|
|
break;
|
|
|
|
} else {
|
|
|
|
pPendingIrp = NULL;
|
|
}
|
|
}
|
|
|
|
if (pPendingIrp) {
|
|
|
|
//
|
|
// get the status and free the queued completion item
|
|
//
|
|
|
|
DBGPRINT(IOCTL, ("CheckQueuedCompletion: found pPendingIrp=0x%X, Status=0x%X\n",
|
|
(ULONG_PTR)pPendingIrp, pPendingIrp->QComp.Status));
|
|
|
|
|
|
TRACE(IOCTL,
|
|
pPendingIrp->QComp.OpCode,
|
|
pPendingIrp->QComp.ClientHandle,
|
|
"CheckQueuedCompletion.Q:");
|
|
TRACE(IOCTL,
|
|
pPendingIrp->QComp.CfInfoHandle,
|
|
pPendingIrp->QComp.Status,
|
|
"CheckQueuedCompletion.Q:");
|
|
|
|
#if DBG
|
|
if (pQItem->OpCode != OP_ANY_CFINFO) {
|
|
|
|
ASSERT(pPendingIrp->QComp.OpCode == pQItem->OpCode);
|
|
ASSERT(pPendingIrp->QComp.ClientHandle == pQItem->ClientHandle);
|
|
}
|
|
#endif
|
|
|
|
Status = pPendingIrp->QComp.Status;
|
|
|
|
GpcFreeToLL(pPendingIrp, &PendingIrpLL, PendingIrpTag);
|
|
|
|
} else {
|
|
|
|
DBGPRINT(IOCTL, ("CheckQueuedCompletion: pPendingIrp not found...PENDING\n"
|
|
));
|
|
|
|
TRACE(IOCTL, 0, 0, "CheckQueuedCompletion.NopQ:");
|
|
|
|
GpcAllocFromLL(&pPendingIrp, &PendingIrpLL, PendingIrpTag);
|
|
|
|
if (pPendingIrp != NULL) {
|
|
|
|
//
|
|
// add the IRP on the pending CfInfo list
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
DBGPRINT(IOCTL, ("CheckQueuedCompletion: adding IRP=0x%X\n",
|
|
(ULONG_PTR)Irp ));
|
|
TRACE(IOCTL, Irp, irpStack, "CheckQueuedCompletion.Irp:");
|
|
|
|
pPendingIrp->Irp = Irp;
|
|
pPendingIrp->FileObject = irpStack->FileObject;
|
|
pPendingIrp->QComp.OpCode = pQItem->OpCode;
|
|
pPendingIrp->QComp.ClientHandle = pQItem->ClientHandle;
|
|
pPendingIrp->QComp.CfInfoHandle = pQItem->CfInfoHandle;
|
|
pPendingIrp->QComp.Status = pQItem->Status;
|
|
|
|
if (!Irp->Cancel) {
|
|
|
|
IoSetCancelRoutine(Irp, CancelPendingIrpCfInfo);
|
|
GpcInsertTailList(&PendingIrpCfInfoList, &(pPendingIrp->Linkage));
|
|
|
|
Status = STATUS_PENDING;
|
|
|
|
} else {
|
|
|
|
DBGPRINT(IOCTL, ("CheckQueuedCompletion: Status Cacelled: IRP=0x%X\n",
|
|
(ULONG_PTR)Irp ));
|
|
|
|
TRACE(IOCTL, Irp, irpStack, "CheckQueuedCompletion.Cancelled:");
|
|
GpcFreeToLL(pPendingIrp, &PendingIrpLL, PendingIrpTag);
|
|
|
|
Status = STATUS_CANCELLED;
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
// Function : GpcValidateClientOwner
|
|
//
|
|
// Description:
|
|
// Validates that the user mode calling an irp owns the client in
|
|
// question.
|
|
//
|
|
//Arguments:
|
|
// GpcClientHandle: Object obtained from client handle.
|
|
// In effect pointer to CLIENT_BLOCK
|
|
//
|
|
//Returns:
|
|
// Returns GPC_STATUS_SUCCESS if the given client is owned by the file object
|
|
// Returns GPC_STATUS_INVALID_HANDLE otherwise
|
|
//
|
|
//Environment:
|
|
// Called to validate client ownership of CLIENT_BLOCK from the
|
|
// proxyGpc* functions . No locks held
|
|
//
|
|
NTSTATUS
|
|
GpcValidateClientOwner (
|
|
IN GPC_HANDLE GpcClientHandle,
|
|
IN PFILE_OBJECT pFile
|
|
)
|
|
{
|
|
PCLIENT_BLOCK pClient;
|
|
NTSTATUS Status = GPC_STATUS_SUCCESS;
|
|
|
|
pClient = (PCLIENT_BLOCK) GpcClientHandle;
|
|
NDIS_LOCK(&pClient->Lock);
|
|
if (pClient->pFileObject != pFile)
|
|
{
|
|
Status = GPC_STATUS_INVALID_HANDLE;
|
|
}
|
|
NDIS_UNLOCK(&pClient->Lock);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
// Function : GpcValidatePatternOwner
|
|
//
|
|
// Description:
|
|
// Validates that the user mode client owns the Pattern in
|
|
// question.
|
|
//
|
|
//Arguments:
|
|
// GpcClientHandle: Object obtained from client handle.
|
|
// In effect pointer to CLIENT_BLOCK
|
|
// GpcPatternHandle: Object Obtained from Pattern Handle
|
|
// In effect pointer to the PATTERN_BLOCK
|
|
//Returns:
|
|
// Returns GPC_STATUS_SUCCESS if the specified client owns the
|
|
// specified Pattern.
|
|
// Returns GPC_STATUS_ACCESS_DENIED otherwise
|
|
//
|
|
//Environment:
|
|
// Called to validate client ownership of PATTERN_BLOCK from the
|
|
// proxyGpc* functions .
|
|
//
|
|
NTSTATUS
|
|
GpcValidatePatternOwner (
|
|
IN GPC_HANDLE GpcClientHandle,
|
|
IN GPC_HANDLE GpcPatternHandle
|
|
)
|
|
{
|
|
PCLIENT_BLOCK pClient;
|
|
PPATTERN_BLOCK pPattern;
|
|
NTSTATUS Status = GPC_STATUS_SUCCESS;
|
|
|
|
pClient = (PCLIENT_BLOCK) GpcClientHandle;
|
|
pPattern = (PPATTERN_BLOCK)GpcPatternHandle;
|
|
if (pClient != pPattern->pClientBlock )
|
|
{
|
|
Status = GPC_STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Function : GpcValidateCfinfoOwner
|
|
//
|
|
// Description:
|
|
// Validates that the user mode client owns the BLOB in
|
|
// question.
|
|
//
|
|
//Arguments:
|
|
// GpcClientHandle: Object obtained from client handle.
|
|
// In effect pointer to CLIENT_BLOCK
|
|
// GpcCfinfoHandle: Object Obtained from Pattern Handle
|
|
// In effect pointer to the PATTERN_BLOCK
|
|
//Returns:
|
|
// Returns GPC_STATUS_SUCCESS if the specified client owns the
|
|
// specified Cfinfo - BLOB_BLOCK.
|
|
// Returns GPC_STATUS_ACCESS_DENIED otherwise
|
|
//
|
|
//Environment:
|
|
// Called to validate client ownership of BLOB_BLOCK from the
|
|
// proxyGpc* functions .
|
|
//
|
|
NTSTATUS
|
|
GpcValidateCfinfoOwner (
|
|
IN GPC_HANDLE GpcClientHandle,
|
|
IN GPC_HANDLE GpcCfInfoHandle
|
|
)
|
|
{
|
|
PCLIENT_BLOCK pClient;
|
|
PBLOB_BLOCK pBlob;
|
|
NTSTATUS Status = GPC_STATUS_SUCCESS;
|
|
|
|
pClient = (PCLIENT_BLOCK) GpcClientHandle;
|
|
pBlob = (PBLOB_BLOCK)GpcCfInfoHandle;
|
|
if (pClient != pBlob->pOwnerClient)
|
|
{
|
|
Status = GPC_STATUS_INVALID_HANDLE;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/* end ioctl.c */
|