1498 lines
36 KiB
C
1498 lines
36 KiB
C
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
tckrnl.c
|
|
|
|
Abstract:
|
|
|
|
This module contains routines that talk to the kernel
|
|
|
|
Author:
|
|
|
|
Jim Stewart (jstew) August 14, 1996
|
|
|
|
Revision History:
|
|
|
|
Ofer Bar (oferbar) Oct 1, 1997
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// we use this mutex to synchronous start up with other traffic.dll's
|
|
//
|
|
const UCHAR TrafficSyncMutex[] = "_TRAFFIC_CTL_MUTEX";
|
|
|
|
|
|
HANDLE hGpcNotifyThread = INVALID_HANDLE_VALUE;
|
|
HANDLE hGpcNotifyStopEvent = INVALID_HANDLE_VALUE;
|
|
|
|
DWORD
|
|
IoAddFlow(
|
|
IN PFLOW_STRUC pFlow,
|
|
IN BOOLEAN Async
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure builds up the structure necessary to add a flow.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status = NO_ERROR;
|
|
PCLIENT_STRUC pClient = pFlow->pInterface->pClient;
|
|
PTC_IFC pTcIfc = pFlow->pInterface->pTcIfc;
|
|
PCF_INFO_QOS Kflow;
|
|
PGPC_ADD_CF_INFO_REQ GpcReq;
|
|
PGPC_ADD_CF_INFO_RES GpcRes;
|
|
ULONG InBuffSize;
|
|
ULONG OutBuffSize;
|
|
ULONG CfInfoSize;
|
|
PIO_APC_ROUTINE pCbRoutine = NULL;
|
|
ULONG l;
|
|
HANDLE hEvent = NULL;
|
|
|
|
//
|
|
// allocate memory for a CF_INFO struct to be passed to the GPC.
|
|
//
|
|
|
|
ASSERT(pFlow->pGenFlow);
|
|
|
|
pFlow->GpcHandle = NULL;
|
|
|
|
l = pFlow->GenFlowLen;
|
|
ASSERT(l > 0);
|
|
CfInfoSize = l + FIELD_OFFSET(CF_INFO_QOS, GenFlow);
|
|
|
|
InBuffSize = sizeof(GPC_ADD_CF_INFO_REQ) + CfInfoSize;
|
|
|
|
//
|
|
// And for the return info...
|
|
//
|
|
|
|
OutBuffSize = sizeof(GPC_ADD_CF_INFO_RES);
|
|
|
|
AllocMem(&GpcRes, OutBuffSize);
|
|
pFlow->CompletionBuffer = (PVOID)GpcRes;
|
|
AllocMem(&GpcReq, InBuffSize);
|
|
|
|
if (GpcRes && GpcReq) {
|
|
|
|
RtlZeroMemory(GpcRes, OutBuffSize);
|
|
RtlZeroMemory(GpcReq, InBuffSize);
|
|
|
|
//
|
|
// fill in the flow information
|
|
//
|
|
|
|
GpcReq->ClientHandle = pFlow->pGpcClient->GpcHandle;
|
|
GpcReq->ClientCfInfoContext = pFlow;
|
|
GpcReq->CfInfoSize = CfInfoSize;
|
|
|
|
Kflow = (PCF_INFO_QOS)&GpcReq->CfInfo;
|
|
|
|
//
|
|
// fill the instance name
|
|
//
|
|
|
|
Kflow->InstanceNameLength = (USHORT) pTcIfc->InstanceNameLength;
|
|
RtlCopyMemory(Kflow->InstanceName,
|
|
pTcIfc->InstanceName,
|
|
pTcIfc->InstanceNameLength * sizeof(WCHAR));
|
|
|
|
//
|
|
// set the flow flags
|
|
//
|
|
Kflow->Flags = pFlow->UserFlags;
|
|
|
|
//
|
|
// copy the generic flow parameter
|
|
//
|
|
|
|
RtlCopyMemory(&Kflow->GenFlow,
|
|
pFlow->pGenFlow,
|
|
l);
|
|
|
|
|
|
if (pClient->ClHandlers.ClAddFlowCompleteHandler && Async) {
|
|
pCbRoutine = CbAddFlowComplete;
|
|
} else {
|
|
hEvent = pFlow->PendingEvent;
|
|
}
|
|
|
|
Status = DeviceControl( pGlobals->GpcFileHandle,
|
|
hEvent,
|
|
pCbRoutine,
|
|
(PVOID)pFlow,
|
|
&pFlow->IoStatBlock,
|
|
IOCTL_GPC_ADD_CF_INFO,
|
|
GpcReq,
|
|
InBuffSize,
|
|
GpcRes,
|
|
OutBuffSize);
|
|
|
|
|
|
if (!ERROR_FAILED(Status)) {
|
|
|
|
if (hEvent && Status == ERROR_SIGNAL_PENDING) {
|
|
|
|
//
|
|
// wait for the event to signal
|
|
//
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoAddFlow: Waiting for event 0x%X...\n",
|
|
PtrToUlong(hEvent)));
|
|
}
|
|
|
|
Status = WaitForSingleObject(hEvent,
|
|
INFINITE
|
|
);
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoAddFlow: ... Event 0x%X signaled, Status=0x%X\n",
|
|
PtrToUlong(hEvent), Status));
|
|
}
|
|
|
|
}
|
|
|
|
if (Status == NO_ERROR) {
|
|
|
|
Status = MapNtStatus2WinError(GpcRes->Status);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoAddFlow: GpcRes returned=0x%X mapped to =0x%X\n",
|
|
GpcRes->Status, Status));
|
|
}
|
|
}
|
|
|
|
if (ERROR_SUCCESS == Status) {
|
|
|
|
ASSERT(GpcRes->GpcCfInfoHandle);
|
|
|
|
pFlow->GpcHandle = GpcRes->GpcCfInfoHandle;
|
|
|
|
pFlow->InstanceNameLength = GpcRes->InstanceNameLength;
|
|
|
|
RtlCopyMemory(pFlow->InstanceName,
|
|
GpcRes->InstanceName,
|
|
GpcRes->InstanceNameLength
|
|
);
|
|
|
|
pFlow->InstanceName[pFlow->InstanceNameLength/sizeof(WCHAR)] = L'\0';
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoAddFlow: Flow Handle=%d Name=%S\n",
|
|
pFlow->GpcHandle,
|
|
pFlow->InstanceName));
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
}
|
|
|
|
//
|
|
// No, it's not a bug
|
|
// GpcRes will be release in CompleteAddFlow
|
|
//
|
|
|
|
if (GpcReq)
|
|
FreeMem(GpcReq);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("<==IoAddFlow: Status=0x%X\n",
|
|
Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
IoAddClassMapFlow(
|
|
IN PFLOW_STRUC pFlow,
|
|
IN BOOLEAN Async
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure builds up the structure necessary to add a flow.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status = NO_ERROR;
|
|
PCLIENT_STRUC pClient = pFlow->pInterface->pClient;
|
|
PTC_IFC pTcIfc = pFlow->pInterface->pTcIfc;
|
|
PCF_INFO_CLASS_MAP Kflow;
|
|
PGPC_ADD_CF_INFO_REQ GpcReq;
|
|
PGPC_ADD_CF_INFO_RES GpcRes;
|
|
ULONG InBuffSize;
|
|
ULONG OutBuffSize;
|
|
ULONG CfInfoSize;
|
|
PIO_APC_ROUTINE pCbRoutine = NULL;
|
|
ULONG l;
|
|
HANDLE hEvent = NULL;
|
|
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
|
|
#if NEVER
|
|
|
|
// As this is not published in MSDN and not implemented in PSCHED also
|
|
//
|
|
// allocate memory for a CF_INFO struct to be passed to the GPC.
|
|
//
|
|
|
|
ASSERT(pFlow->pClassMapFlow);
|
|
|
|
pFlow->GpcHandle = NULL;
|
|
|
|
l = sizeof(TC_CLASS_MAP_FLOW) + pFlow->pClassMapFlow->ObjectsLength;
|
|
CfInfoSize = l + FIELD_OFFSET(CF_INFO_CLASS_MAP, ClassMapInfo);
|
|
|
|
InBuffSize = sizeof(GPC_ADD_CF_INFO_REQ) + CfInfoSize;
|
|
|
|
//
|
|
// And for the return info...
|
|
//
|
|
|
|
OutBuffSize = sizeof(GPC_ADD_CF_INFO_RES);
|
|
|
|
AllocMem(&GpcRes, OutBuffSize);
|
|
pFlow->CompletionBuffer = (PVOID)GpcRes;
|
|
AllocMem(&GpcReq, InBuffSize);
|
|
|
|
if (GpcRes && GpcReq) {
|
|
|
|
RtlZeroMemory(GpcRes, OutBuffSize);
|
|
RtlZeroMemory(GpcReq, InBuffSize);
|
|
|
|
//
|
|
// fill in the flow information
|
|
//
|
|
|
|
GpcReq->ClientHandle = pFlow->pGpcClient->GpcHandle;
|
|
GpcReq->ClientCfInfoContext = pFlow;
|
|
GpcReq->CfInfoSize = CfInfoSize;
|
|
|
|
Kflow = (PCF_INFO_CLASS_MAP)&GpcReq->CfInfo;
|
|
|
|
//
|
|
// fill the instance name
|
|
//
|
|
|
|
Kflow->InstanceNameLength = (USHORT) pTcIfc->InstanceNameLength;
|
|
RtlCopyMemory(Kflow->InstanceName,
|
|
pTcIfc->InstanceName,
|
|
pTcIfc->InstanceNameLength * sizeof(WCHAR));
|
|
|
|
//
|
|
// copy the generic flow parameter
|
|
//
|
|
|
|
RtlCopyMemory(&Kflow->ClassMapInfo,
|
|
pFlow->pClassMapFlow,
|
|
l);
|
|
|
|
|
|
if (pClient->ClHandlers.ClAddFlowCompleteHandler && Async) {
|
|
pCbRoutine = CbAddFlowComplete;
|
|
} else {
|
|
hEvent = pFlow->PendingEvent;
|
|
}
|
|
|
|
Status = DeviceControl( pGlobals->GpcFileHandle,
|
|
hEvent,
|
|
pCbRoutine,
|
|
(PVOID)pFlow,
|
|
&pFlow->IoStatBlock,
|
|
IOCTL_GPC_ADD_CF_INFO,
|
|
GpcReq,
|
|
InBuffSize,
|
|
GpcRes,
|
|
OutBuffSize);
|
|
|
|
|
|
if (!ERROR_FAILED(Status)) {
|
|
|
|
if (hEvent && Status == ERROR_SIGNAL_PENDING) {
|
|
|
|
//
|
|
// wait for the event to signal
|
|
//
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoAddClassMapFlow: Waiting for event 0x%X...\n",
|
|
PtrToUlong(hEvent)));
|
|
}
|
|
|
|
Status = WaitForSingleObject(hEvent,
|
|
INFINITE
|
|
);
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoAddClassMapFlow: ... Event 0x%X signaled, Status=0x%X\n",
|
|
PtrToUlong(hEvent), Status));
|
|
}
|
|
|
|
}
|
|
|
|
if (Status == NO_ERROR) {
|
|
|
|
Status = MapNtStatus2WinError(GpcRes->Status);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoAddFlow: GpcRes returned=0x%X mapped to =0x%X\n",
|
|
GpcRes->Status, Status));
|
|
}
|
|
}
|
|
|
|
if (!ERROR_FAILED(Status)) {
|
|
|
|
ASSERT(GpcRes->GpcCfInfoHandle);
|
|
|
|
pFlow->GpcHandle = GpcRes->GpcCfInfoHandle;
|
|
|
|
pFlow->InstanceNameLength = GpcRes->InstanceNameLength;
|
|
|
|
RtlCopyMemory(pFlow->InstanceName,
|
|
GpcRes->InstanceName,
|
|
GpcRes->InstanceNameLength
|
|
);
|
|
|
|
pFlow->InstanceName[pFlow->InstanceNameLength/sizeof(WCHAR)] = L'\0';
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoAddClassMapFlow: Flow Handle=%d Name=%S\n",
|
|
pFlow->GpcHandle,
|
|
pFlow->InstanceName));
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
}
|
|
|
|
//
|
|
// No, it's not a bug
|
|
// GpcRes will be release in CompleteAddFlow
|
|
//
|
|
|
|
if (GpcReq)
|
|
FreeMem(GpcReq);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("<==IoAddClassMapFlow: Status=0x%X\n",
|
|
Status));
|
|
}
|
|
|
|
return Status;
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
IoModifyFlow(
|
|
IN PFLOW_STRUC pFlow,
|
|
IN BOOLEAN Async
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure builds up the structure necessary to modify a flow.
|
|
|
|
Arguments:
|
|
|
|
pFlow
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status = NO_ERROR;
|
|
PCLIENT_STRUC pClient = pFlow->pInterface->pClient;
|
|
PTC_IFC pTcIfc = pFlow->pInterface->pTcIfc;
|
|
PCF_INFO_QOS Kflow;
|
|
PGPC_MODIFY_CF_INFO_REQ GpcReq;
|
|
PGPC_MODIFY_CF_INFO_RES GpcRes;
|
|
ULONG InBuffSize;
|
|
ULONG OutBuffSize;
|
|
ULONG CfInfoSize;
|
|
PIO_APC_ROUTINE pCbRoutine = NULL;
|
|
ULONG l;
|
|
HANDLE hEvent = NULL;
|
|
|
|
//
|
|
// allocate memory for a CF_INFO struct to be passed to the GPC.
|
|
//
|
|
|
|
ASSERT(pFlow->pGenFlow1);
|
|
|
|
l = pFlow->GenFlowLen1;
|
|
ASSERT(l > 0);
|
|
CfInfoSize = l + FIELD_OFFSET(CF_INFO_QOS, GenFlow);
|
|
|
|
InBuffSize = sizeof(GPC_MODIFY_CF_INFO_REQ) + CfInfoSize;
|
|
|
|
//
|
|
// And for the return info...
|
|
//
|
|
|
|
OutBuffSize = sizeof(GPC_MODIFY_CF_INFO_RES);
|
|
|
|
AllocMem(&GpcRes, OutBuffSize);
|
|
pFlow->CompletionBuffer = (PVOID)GpcRes;
|
|
AllocMem(&GpcReq, InBuffSize);
|
|
|
|
if (GpcRes && GpcReq) {
|
|
|
|
RtlZeroMemory(GpcRes, OutBuffSize);
|
|
RtlZeroMemory(GpcReq, InBuffSize);
|
|
|
|
//
|
|
// fill in the flow information
|
|
//
|
|
|
|
GpcReq->ClientHandle = pFlow->pGpcClient->GpcHandle;
|
|
GpcReq->GpcCfInfoHandle = pFlow->GpcHandle;
|
|
GpcReq->CfInfoSize = CfInfoSize;
|
|
|
|
Kflow = (PCF_INFO_QOS)&GpcReq->CfInfo;
|
|
|
|
//
|
|
// fill the instance name
|
|
//
|
|
|
|
Kflow->InstanceNameLength = (USHORT) pTcIfc->InstanceNameLength;
|
|
RtlCopyMemory(Kflow->InstanceName,
|
|
pTcIfc->InstanceName,
|
|
pTcIfc->InstanceNameLength * sizeof(WCHAR));
|
|
|
|
//
|
|
// copy the generic flow parameter
|
|
//
|
|
|
|
RtlCopyMemory(&Kflow->GenFlow,
|
|
pFlow->pGenFlow1,
|
|
l);
|
|
|
|
|
|
if (pClient->ClHandlers.ClModifyFlowCompleteHandler && Async) {
|
|
pCbRoutine = CbModifyFlowComplete;
|
|
} else {
|
|
hEvent = pFlow->PendingEvent;
|
|
}
|
|
|
|
Status = DeviceControl( pGlobals->GpcFileHandle,
|
|
hEvent,
|
|
pCbRoutine,
|
|
(PVOID)pFlow,
|
|
&pFlow->IoStatBlock,
|
|
IOCTL_GPC_MODIFY_CF_INFO,
|
|
GpcReq,
|
|
InBuffSize,
|
|
GpcRes,
|
|
OutBuffSize);
|
|
|
|
if (!ERROR_FAILED(Status)) {
|
|
|
|
if (hEvent && Status == ERROR_SIGNAL_PENDING) {
|
|
|
|
//
|
|
// wait for the event to signal
|
|
//
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoModifyFlow: Waiting for event 0x%X\n",
|
|
PtrToUlong(hEvent)));
|
|
}
|
|
|
|
Status = WaitForSingleObject(hEvent,
|
|
INFINITE
|
|
);
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoModifyFlow: ... Event 0x%X signaled, Status=0x%X\n",
|
|
PtrToUlong(hEvent), Status));
|
|
}
|
|
}
|
|
|
|
if (Status == NO_ERROR) {
|
|
|
|
Status = MapNtStatus2WinError(GpcRes->Status);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoModifyFlow: GpcRes returned=0x%X mapped to =0x%X\n",
|
|
GpcRes->Status, Status));
|
|
}
|
|
}
|
|
} else{
|
|
|
|
Status = MapNtStatus2WinError(GpcRes->Status);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoModifyFlow: GpcRes returned=0x%X mapped to =0x%X\n",
|
|
GpcRes->Status, Status));
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
//
|
|
// No, it's not a bug
|
|
// GpcRes will be release in CompleteModifyFlow
|
|
//
|
|
|
|
if (GpcReq)
|
|
FreeMem(GpcReq);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoModifyFlow: Status=0x%X\n",
|
|
Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
IoDeleteFlow(
|
|
IN PFLOW_STRUC pFlow,
|
|
IN BOOLEAN Async
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure builds up the structure necessary to delete a flow.
|
|
It then calls a routine to pass this info to the GPC.
|
|
|
|
Arguments:
|
|
|
|
pFlow
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
ULONG InBuffSize;
|
|
ULONG OutBuffSize;
|
|
PGPC_REMOVE_CF_INFO_REQ GpcReq;
|
|
PGPC_REMOVE_CF_INFO_RES GpcRes;
|
|
PIO_APC_ROUTINE pCbRoutine = NULL;
|
|
PCLIENT_STRUC pClient = pFlow->pInterface->pClient;
|
|
HANDLE hEvent = NULL;
|
|
|
|
if (IS_REMOVED(pFlow->Flags)) {
|
|
|
|
//
|
|
// this flow has been already deleted in the kernel
|
|
// due to a flow close notification.
|
|
// no need to send IOTCL to GPC, just return OK
|
|
//
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoDeleteFlow: Flow has already been deleted=0x%X\n",
|
|
PtrToUlong(pFlow)));
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// If we add this over here, then if WMI deletes the flow,
|
|
// the user mode call will just return above.
|
|
//
|
|
GetLock(pFlow->Lock);
|
|
pFlow->Flags |= TC_FLAGS_REMOVED;
|
|
FreeLock(pFlow->Lock);
|
|
|
|
//
|
|
// allocate memory for in and out buffers
|
|
//
|
|
|
|
InBuffSize = sizeof(GPC_REMOVE_CF_INFO_REQ);
|
|
OutBuffSize = sizeof(GPC_REMOVE_CF_INFO_RES);
|
|
|
|
AllocMem(&GpcRes, OutBuffSize);
|
|
pFlow->CompletionBuffer = (PVOID)GpcRes;
|
|
AllocMem(&GpcReq, InBuffSize);
|
|
|
|
if (GpcReq && GpcRes){
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoDeleteFlow: preparing to delete the flow=0x%X\n",
|
|
PtrToUlong(pFlow)));
|
|
}
|
|
|
|
GpcReq->ClientHandle = pFlow->pGpcClient->GpcHandle;
|
|
GpcReq->GpcCfInfoHandle = pFlow->GpcHandle;
|
|
|
|
|
|
if (pClient->ClHandlers.ClDeleteFlowCompleteHandler && Async) {
|
|
pCbRoutine = CbDeleteFlowComplete;
|
|
} else {
|
|
hEvent = pFlow->PendingEvent;
|
|
}
|
|
|
|
Status = DeviceControl( pGlobals->GpcFileHandle,
|
|
hEvent,
|
|
pCbRoutine,
|
|
(PVOID)pFlow,
|
|
&pFlow->IoStatBlock,
|
|
IOCTL_GPC_REMOVE_CF_INFO,
|
|
GpcReq,
|
|
InBuffSize,
|
|
GpcRes,
|
|
OutBuffSize);
|
|
|
|
if (!ERROR_FAILED(Status)) {
|
|
|
|
if (hEvent && Status == ERROR_SIGNAL_PENDING) {
|
|
|
|
//
|
|
// wait for the event to signal
|
|
//
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoDeleteFlow: Waiting for event 0x%X\n",
|
|
PtrToUlong(hEvent)));
|
|
}
|
|
|
|
Status = WaitForSingleObject(hEvent,
|
|
INFINITE
|
|
);
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoDeleteFlow: ... Event 0x%X signaled, Status=0x%X\n",
|
|
PtrToUlong(hEvent), Status));
|
|
}
|
|
}
|
|
|
|
if (Status == NO_ERROR) {
|
|
|
|
Status = MapNtStatus2WinError(GpcRes->Status);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoDeleteFlow: Gpc returned=0x%X mapped to 0x%X\n",
|
|
GpcRes->Status, Status));
|
|
}
|
|
|
|
//
|
|
// If the deletion was unsuccessful, let's un-mark the REMOVED flag.
|
|
//
|
|
if (ERROR_FAILED(Status)) {
|
|
|
|
GetLock(pFlow->Lock);
|
|
pFlow->Flags &= ~TC_FLAGS_REMOVED;
|
|
FreeLock(pFlow->Lock);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
//
|
|
// No, it's not a bug
|
|
// GpcRes will be release in CompleteDeleteFlow
|
|
//
|
|
|
|
if (GpcReq)
|
|
FreeMem(GpcReq);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("<==IoDeleteFlow: Status=0x%X\n",
|
|
Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
IoAddFilter(
|
|
IN PFILTER_STRUC pFilter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure builds up the structure necessary to add a filter.
|
|
It then calls a routine to pass this info to the GPC.
|
|
|
|
Arguments:
|
|
|
|
pFilter
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
DWORD Status;
|
|
PGPC_ADD_PATTERN_REQ GpcReq;
|
|
PGPC_ADD_PATTERN_RES GpcRes;
|
|
ULONG InBuffSize;
|
|
ULONG OutBuffSize;
|
|
PFLOW_STRUC pFlow = pFilter->pFlow;
|
|
PTC_GEN_FILTER pGpcFilter = pFilter->pGpcFilter;
|
|
PUCHAR p;
|
|
ULONG PatternSize;
|
|
IO_STATUS_BLOCK IoStatBlock;
|
|
|
|
pFilter->GpcHandle = NULL;
|
|
|
|
ASSERT(pGpcFilter);
|
|
ASSERT(pFlow);
|
|
|
|
PatternSize = pGpcFilter->PatternSize;
|
|
|
|
InBuffSize = sizeof(GPC_ADD_PATTERN_REQ) + 2*PatternSize;
|
|
|
|
OutBuffSize = sizeof(GPC_ADD_PATTERN_RES);
|
|
|
|
AllocMem(&GpcReq, InBuffSize);
|
|
AllocMem(&GpcRes, OutBuffSize);
|
|
|
|
if (GpcReq && GpcRes){
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoAddFilter: Filling request: size: in=%d, out=%d\n",
|
|
InBuffSize, OutBuffSize));
|
|
}
|
|
|
|
GpcReq->ClientHandle = pFlow->pGpcClient->GpcHandle;
|
|
GpcReq->GpcCfInfoHandle = pFlow->GpcHandle;
|
|
GpcReq->ClientPatternContext = (GPC_CLIENT_HANDLE)pFilter;
|
|
GpcReq->Priority = 0;
|
|
GpcReq->PatternSize = PatternSize;
|
|
GpcReq->ProtocolTemplate = pFilter->GpcProtocolTemplate;
|
|
|
|
//
|
|
// fill in the pattern
|
|
//
|
|
|
|
p = (PUCHAR)&GpcReq->PatternAndMask;
|
|
|
|
RtlCopyMemory(p, pGpcFilter->Pattern, PatternSize);
|
|
|
|
//
|
|
// fill in the mask
|
|
//
|
|
|
|
p += PatternSize;
|
|
|
|
RtlCopyMemory(p, pGpcFilter->Mask, PatternSize);
|
|
|
|
Status = DeviceControl( pGlobals->GpcFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatBlock,
|
|
IOCTL_GPC_ADD_PATTERN,
|
|
GpcReq,
|
|
InBuffSize,
|
|
GpcRes,
|
|
OutBuffSize);
|
|
|
|
if (!ERROR_FAILED(Status)) {
|
|
|
|
Status = MapNtStatus2WinError(GpcRes->Status);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoAddFilter: GpcRes returned=0x%X mapped to =0x%X\n",
|
|
GpcRes->Status, Status));
|
|
}
|
|
|
|
//
|
|
// save the filter handle
|
|
//
|
|
|
|
if (!ERROR_FAILED(Status)) {
|
|
|
|
pFilter->GpcHandle = GpcRes->GpcPatternHandle;
|
|
|
|
} else {
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoAddFilter: GpcRes returned=0x%X mapped to =0x%X\n",
|
|
GpcRes->Status, Status));
|
|
}
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("Error - failed the addfilter call\n"));
|
|
}
|
|
|
|
//ASSERT(Status == ERROR_DUPLICATE_FILTER); removed for WAN - interface up down situation
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
WSPRINT(("IoAddFilter: Error =0x%X\n",
|
|
Status));
|
|
}
|
|
|
|
}
|
|
|
|
if (GpcReq)
|
|
FreeMem(GpcReq);
|
|
|
|
if (GpcRes)
|
|
FreeMem(GpcRes);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("<==IoAddFilter: Returned =0x%X\n",
|
|
Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
IoDeleteFilter(
|
|
IN PFILTER_STRUC pFilter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure builds up the structure necessary to delete a filter.
|
|
It then calls a routine to pass this info to the GPC.
|
|
|
|
Arguments:
|
|
|
|
pFilter
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
DWORD Status;
|
|
ULONG InBuffSize;
|
|
ULONG OutBuffSize;
|
|
GPC_REMOVE_PATTERN_REQ GpcReq;
|
|
GPC_REMOVE_PATTERN_RES GpcRes;
|
|
IO_STATUS_BLOCK IoStatBlock;
|
|
|
|
//
|
|
// allocate memory for in and out buffers
|
|
//
|
|
|
|
if (IS_REMOVED(pFilter->Flags)) {
|
|
|
|
//
|
|
// this filter has been already deleted in the kernel
|
|
// due to a flow close notification.
|
|
// no need to send IOTCL to GPC, just return OK
|
|
//
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoDeleteFilter: Filter has already been deleted=0x%X\n",
|
|
PtrToUlong(pFilter)));
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// If we add this over here, then if WMI deletes the Interface (and the
|
|
// flows/filters) the user mode call will just return above.
|
|
//
|
|
GetLock(pFilter->Lock);
|
|
pFilter->Flags |= TC_FLAGS_REMOVED;
|
|
FreeLock(pFilter->Lock);
|
|
|
|
InBuffSize = sizeof(GPC_REMOVE_PATTERN_REQ);
|
|
OutBuffSize = sizeof(GPC_REMOVE_PATTERN_RES);
|
|
|
|
GpcReq.ClientHandle = pFilter->pFlow->pGpcClient->GpcHandle;
|
|
GpcReq.GpcPatternHandle = pFilter->GpcHandle;
|
|
|
|
ASSERT(GpcReq.ClientHandle);
|
|
ASSERT(GpcReq.GpcPatternHandle);
|
|
|
|
Status = DeviceControl( pGlobals->GpcFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatBlock,
|
|
IOCTL_GPC_REMOVE_PATTERN,
|
|
&GpcReq,
|
|
InBuffSize,
|
|
&GpcRes,
|
|
OutBuffSize
|
|
);
|
|
|
|
if (!ERROR_FAILED(Status)) {
|
|
|
|
Status = MapNtStatus2WinError(GpcRes.Status);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoDeleteFilter: GpcRes returned=0x%X mapped to =0x%X\n",
|
|
GpcRes.Status, Status));
|
|
}
|
|
|
|
//
|
|
// If the deletion was unsuccessful, let's un-mark the REMOVED flag.
|
|
//
|
|
if (ERROR_FAILED(Status)) {
|
|
|
|
GetLock(pFilter->Lock);
|
|
pFilter->Flags &= ~TC_FLAGS_REMOVED;
|
|
FreeLock(pFilter->Lock);
|
|
}
|
|
|
|
}
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("<==IoDeleteFilter: Status=0x%X\n",
|
|
Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
IoRegisterClient(
|
|
IN PGPC_CLIENT pGpcClient
|
|
)
|
|
{
|
|
DWORD Status;
|
|
GPC_REGISTER_CLIENT_REQ GpcReq;
|
|
GPC_REGISTER_CLIENT_RES GpcRes;
|
|
ULONG InBuffSize;
|
|
ULONG OutBuffSize;
|
|
IO_STATUS_BLOCK IoStatBlock;
|
|
|
|
InBuffSize = sizeof(GPC_REGISTER_CLIENT_REQ);
|
|
OutBuffSize = sizeof(GPC_REGISTER_CLIENT_RES);
|
|
|
|
GpcReq.CfId = pGpcClient->CfInfoType;
|
|
GpcReq.Flags = GPC_FLAGS_FRAGMENT;
|
|
GpcReq.MaxPriorities = 1;
|
|
GpcReq.ClientContext =
|
|
(GPC_CLIENT_HANDLE)UlongToPtr(GetCurrentProcessId()); // process id
|
|
|
|
Status = DeviceControl( pGlobals->GpcFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatBlock,
|
|
IOCTL_GPC_REGISTER_CLIENT,
|
|
&GpcReq,
|
|
InBuffSize,
|
|
&GpcRes,
|
|
OutBuffSize );
|
|
|
|
if (!ERROR_FAILED(Status)) {
|
|
|
|
Status = MapNtStatus2WinError(GpcRes.Status);
|
|
pGpcClient->GpcHandle = GpcRes.ClientHandle;
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoRegisterClient: GpcRes returned=0x%X mapped to =0x%X\n",
|
|
GpcRes.Status, Status));
|
|
}
|
|
}
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("<==IoRegisterClient: Status=0x%X\n",
|
|
Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
IoDeregisterClient(
|
|
IN PGPC_CLIENT pGpcClient
|
|
)
|
|
{
|
|
DWORD Status;
|
|
GPC_DEREGISTER_CLIENT_REQ GpcReq;
|
|
GPC_DEREGISTER_CLIENT_RES GpcRes;
|
|
ULONG InBuffSize;
|
|
ULONG OutBuffSize;
|
|
IO_STATUS_BLOCK IoStatBlock;
|
|
|
|
InBuffSize = sizeof(GPC_DEREGISTER_CLIENT_REQ);
|
|
OutBuffSize = sizeof(GPC_DEREGISTER_CLIENT_RES);
|
|
|
|
GpcReq.ClientHandle = pGpcClient->GpcHandle;
|
|
|
|
Status = DeviceControl( pGlobals->GpcFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatBlock,
|
|
IOCTL_GPC_DEREGISTER_CLIENT,
|
|
&GpcReq,
|
|
InBuffSize,
|
|
&GpcRes,
|
|
OutBuffSize
|
|
);
|
|
|
|
if (!ERROR_FAILED(Status)) {
|
|
|
|
Status = MapNtStatus2WinError(GpcRes.Status);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoDeegisterClient: GpcRes returned=0x%X mapped to =0x%X\n",
|
|
GpcRes.Status, Status));
|
|
}
|
|
}
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("<==IoDeregisterClient: Status=0x%X\n",
|
|
Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
PGPC_NOTIFY_REQUEST_RES GpcResCb;
|
|
|
|
DWORD
|
|
IoRequestNotify(
|
|
VOID
|
|
//IN PGPC_CLIENT pGpcClient
|
|
)
|
|
/*
|
|
Description:
|
|
|
|
This routine sends a notification request buffer to the GPC.
|
|
The request will pend until the GPC notifies about a flow
|
|
being deleted. This will cause a callback to CbGpcNotifyRoutine.
|
|
|
|
*/
|
|
{
|
|
DWORD Status;
|
|
ULONG OutBuffSize;
|
|
|
|
//
|
|
// allocate memory for in and out buffers
|
|
//
|
|
|
|
OutBuffSize = sizeof(GPC_NOTIFY_REQUEST_RES);
|
|
|
|
AllocMem(&GpcResCb, OutBuffSize);
|
|
|
|
if (GpcResCb){
|
|
|
|
Status = DeviceControl( pGlobals->GpcFileHandle,
|
|
NULL,
|
|
CbGpcNotifyRoutine,
|
|
(PVOID)GpcResCb,
|
|
&GpcResCb->IoStatBlock,
|
|
IOCTL_GPC_NOTIFY_REQUEST,
|
|
NULL, //GpcReq,
|
|
0, //InBuffSize,
|
|
GpcResCb,
|
|
OutBuffSize);
|
|
|
|
if (ERROR_FAILED(Status)) {
|
|
|
|
FreeMem(GpcResCb);
|
|
GpcResCb = NULL;
|
|
}
|
|
else if ( ERROR_PENDING(Status) )
|
|
{
|
|
Status = NO_ERROR;
|
|
}
|
|
} else {
|
|
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("<==IoRequestNotify: Buffer=%p Status=0x%X\n",
|
|
GpcResCb, Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
CancelIoRequestNotify()
|
|
/*
|
|
|
|
Description:
|
|
This routine cancels the IRP in GPC and waits for the pending
|
|
IO to be cancelled. The callback routine set an event when
|
|
IO request is canclled and this routine waits for that event
|
|
before returning.
|
|
|
|
*/
|
|
{
|
|
// Non-zero value of GpcResCb indicates a pending IRP
|
|
if (GpcResCb)
|
|
{
|
|
GpcCancelEvent = CreateEvent (
|
|
NULL,
|
|
FALSE,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if ( CancelIo ( pGlobals->GpcFileHandle ) )
|
|
{
|
|
if ( GpcCancelEvent )
|
|
{
|
|
WaitForSingleObjectEx(
|
|
GpcCancelEvent,
|
|
INFINITE,
|
|
TRUE );
|
|
|
|
CloseHandle ( GpcCancelEvent );
|
|
GpcCancelEvent = NULL;
|
|
}
|
|
else
|
|
{
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT((
|
|
"CancelIo: Status=0x%X\n",
|
|
GetLastError() ));
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeMem(GpcResCb);
|
|
|
|
IF_DEBUG(IOCTLS)
|
|
{
|
|
WSPRINT(("<==CancelIoRequestNotify: Freed %p\n",
|
|
GpcResCb ));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
IncrementLibraryUsageCount(
|
|
HINSTANCE hinst,
|
|
int nCount)
|
|
/*
|
|
|
|
Utility routine to increment the ref count on
|
|
the TRAFFIC.DLL so that it will not get unloaded
|
|
before the GPCNotify thread gets a chance to run.
|
|
|
|
*/
|
|
{
|
|
TCHAR szModuleName[_MAX_PATH];
|
|
|
|
GetModuleFileName(hinst, szModuleName, _MAX_PATH);
|
|
|
|
while (nCount--)
|
|
LoadLibrary(szModuleName);
|
|
|
|
return;
|
|
}
|
|
|
|
DWORD
|
|
GpcNotifyThreadFunction ()
|
|
/*
|
|
|
|
This routine registers an IRP with GPC to listen for
|
|
FLOW close notifications and waits for the stop event.
|
|
When the event is signalled the IRP is canceled and this
|
|
thread exits.
|
|
|
|
Since the wait is done in an alertable state GPC callbacks
|
|
are executed in this thread itself.
|
|
|
|
*/
|
|
{
|
|
DWORD dwError;
|
|
|
|
dwError = IoRequestNotify();
|
|
|
|
WaitForSingleObjectEx(
|
|
hGpcNotifyStopEvent,
|
|
INFINITE,
|
|
TRUE );
|
|
|
|
CancelIoRequestNotify();
|
|
|
|
FreeLibraryAndExitThread(
|
|
hinstTrafficDll,
|
|
0 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
DWORD
|
|
StartGpcNotifyThread()
|
|
/*
|
|
|
|
Description:
|
|
This routine starts a thread which queues an IRP for
|
|
GPC notifications.
|
|
|
|
|
|
*/
|
|
{
|
|
DWORD dwError = 0;
|
|
DWORD dwThreadId = 0;
|
|
|
|
// Increment the ref count on this DLL so it will not be unloaded
|
|
// before the GpcNotifyThreadFunction gets to run
|
|
IncrementLibraryUsageCount(
|
|
hinstTrafficDll,
|
|
1);
|
|
|
|
// Create the stop event for the thread to receive
|
|
// GPC flow close notifications
|
|
hGpcNotifyStopEvent = CreateEvent (
|
|
NULL,
|
|
FALSE,
|
|
FALSE,
|
|
NULL );
|
|
if ( !hGpcNotifyStopEvent )
|
|
{
|
|
dwError = GetLastError();
|
|
goto Error;
|
|
}
|
|
|
|
// Start the thread.
|
|
hGpcNotifyThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE )GpcNotifyThreadFunction,
|
|
NULL,
|
|
0,
|
|
&dwThreadId );
|
|
if ( !hGpcNotifyThread )
|
|
{
|
|
dwError = GetLastError();
|
|
goto Error;
|
|
}
|
|
|
|
// Not closing the thread handle as StopGpcNotifyThread
|
|
// routine will use this handle to wait for thread to
|
|
// terminate.
|
|
|
|
return 0;
|
|
|
|
Error:
|
|
|
|
if ( hGpcNotifyStopEvent )
|
|
{
|
|
CloseHandle ( hGpcNotifyStopEvent );
|
|
hGpcNotifyStopEvent = NULL;
|
|
}
|
|
|
|
if ( hGpcNotifyThread )
|
|
{
|
|
CloseHandle ( hGpcNotifyThread );
|
|
hGpcNotifyThread = NULL;
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
StopGpcNotifyThread()
|
|
/*
|
|
Description:
|
|
Signal the GPC notification thread to stop
|
|
and wait it to stop.
|
|
*/
|
|
{
|
|
// If there was no thread created nothing more to do.
|
|
if ( hGpcNotifyThread )
|
|
{
|
|
|
|
// Tell GPC Notify thread to stop
|
|
SetEvent ( hGpcNotifyStopEvent );
|
|
|
|
// Wait for it to stop
|
|
WaitForSingleObject (
|
|
hGpcNotifyThread,
|
|
INFINITE );
|
|
|
|
CloseHandle( hGpcNotifyThread );
|
|
|
|
hGpcNotifyThread = NULL;
|
|
|
|
CloseHandle ( hGpcNotifyStopEvent );
|
|
|
|
hGpcNotifyStopEvent = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
IoEnumerateFlows(
|
|
IN PGPC_CLIENT pGpcClient,
|
|
IN OUT PHANDLE pEnumHandle,
|
|
IN OUT PULONG pFlowCount,
|
|
IN OUT PULONG pBufSize,
|
|
OUT PGPC_ENUM_CFINFO_RES *ppBuffer
|
|
)
|
|
/*
|
|
Description:
|
|
|
|
This routine sends a notification request buffer to the GPC.
|
|
The request will pend until the GPC notifies about a flow
|
|
being deleted. This will cause a callback to CbGpcNotifyRoutine.
|
|
|
|
*/
|
|
{
|
|
DWORD Status;
|
|
ULONG InBuffSize;
|
|
ULONG OutBuffSize;
|
|
PGPC_ENUM_CFINFO_REQ GpcReq;
|
|
PGPC_ENUM_CFINFO_RES GpcRes;
|
|
IO_STATUS_BLOCK IoStatBlock;
|
|
|
|
//
|
|
// allocate memory for in and out buffers
|
|
//
|
|
|
|
InBuffSize = sizeof(GPC_ENUM_CFINFO_REQ);
|
|
OutBuffSize = *pBufSize + FIELD_OFFSET(GPC_ENUM_CFINFO_RES,EnumBuffer);
|
|
|
|
*ppBuffer = NULL;
|
|
|
|
AllocMem(&GpcRes, OutBuffSize);
|
|
AllocMem(&GpcReq, InBuffSize);
|
|
|
|
if (GpcReq && GpcRes) {
|
|
|
|
GpcReq->ClientHandle = pGpcClient->GpcHandle;
|
|
GpcReq->EnumHandle = *pEnumHandle;
|
|
GpcReq->CfInfoCount = *pFlowCount;
|
|
|
|
Status = DeviceControl( pGlobals->GpcFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatBlock,
|
|
IOCTL_GPC_ENUM_CFINFO,
|
|
GpcReq,
|
|
InBuffSize,
|
|
GpcRes,
|
|
OutBuffSize);
|
|
|
|
if (!ERROR_FAILED(Status)) {
|
|
|
|
Status = MapNtStatus2WinError(GpcRes->Status);
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("IoEnumerateFlows: GpcRes returned=0x%X mapped to =0x%X\n",
|
|
GpcRes->Status, Status));
|
|
}
|
|
|
|
if (!ERROR_FAILED(Status)) {
|
|
|
|
*pEnumHandle = GpcRes->EnumHandle;
|
|
*pFlowCount = GpcRes->TotalCfInfo;
|
|
*pBufSize = (ULONG)IoStatBlock.Information -
|
|
FIELD_OFFSET(GPC_ENUM_CFINFO_RES,EnumBuffer);
|
|
*ppBuffer = GpcRes;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (GpcReq)
|
|
FreeMem(GpcReq);
|
|
|
|
if (ERROR_FAILED(Status)) {
|
|
|
|
//
|
|
// free GpcReq only if there was an error
|
|
//
|
|
|
|
if (GpcRes)
|
|
FreeMem(GpcRes);
|
|
}
|
|
|
|
IF_DEBUG(IOCTLS) {
|
|
WSPRINT(("<==IoEnumerateFlows: Status=0x%X\n",
|
|
Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|