NTSTATUS
StreamOobFlushOutgoingData(
   _Inout_ STREAM_EDITOR* streamEditor
   )
{
   NTSTATUS status = STATUS_SUCCESS;

   KLOCK_QUEUE_HANDLE editLockHandle;
   OUTGOING_STREAM_DATA* outgoingStreamData = NULL;

   for(;;)
   {
      KeAcquireInStackQueuedSpinLock(
         &streamEditor->oobEditInfo.editLock,
         &editLockHandle
         );

      if (!IsListEmpty(&streamEditor->oobEditInfo.outgoingDataQueue))
      {
         LIST_ENTRY* listEntry = 
            RemoveHeadList(&streamEditor->oobEditInfo.outgoingDataQueue);
         
         outgoingStreamData = CONTAINING_RECORD(
                                 listEntry, 
                                 OUTGOING_STREAM_DATA,
                                 listEntry
                                 );
      }

      KeReleaseInStackQueuedSpinLock(&editLockHandle);

      if (outgoingStreamData == NULL)
      {
         break;
      }

      status = FwpsStreamInjectAsync(
                  gInjectionHandle,
                  NULL,
                  0,
                  streamEditor->oobEditInfo.flowId,
                  streamEditor->oobEditInfo.calloutId,
                  streamEditor->oobEditInfo.layerId,
                  outgoingStreamData->streamFlags,
                  outgoingStreamData->netBufferList,
                  outgoingStreamData->dataLength,
                  outgoingStreamData->isClone ? StreamOobInjectCloneCompletionFn :
                                                StreamOobInjectCompletionFn,
                  outgoingStreamData->mdl
                  );

      if (!NT_SUCCESS(status))
      {
         goto Exit;
      }

      ExFreePoolWithTag(
         outgoingStreamData,
         STREAM_EDITOR_OUTGOING_DATA_TAG
         );

      outgoingStreamData = NULL;
   }
  
Exit:

   if (outgoingStreamData != NULL)
   {
      NT_ASSERT(!NT_SUCCESS(status));

      if (outgoingStreamData->isClone)
      {
         FwpsDiscardClonedStreamData(
            outgoingStreamData->netBufferList,
            0,
            FALSE
            );
      }
      else
      {
         FwpsFreeNetBufferList(outgoingStreamData->netBufferList);

         if (outgoingStreamData->mdl != NULL)
         {
            IoFreeMdl(outgoingStreamData->mdl);

            ExFreePoolWithTag(
               outgoingStreamData->mdl->MappedSystemVa, 
               STREAM_EDITOR_MDL_DATA_TAG
               );
         }
      }

      ExFreePoolWithTag(
         outgoingStreamData,
         STREAM_EDITOR_OUTGOING_DATA_TAG
         );
   }

   return status;
}
NTSTATUS
StreamOobEditData(
   _Inout_ STREAM_EDITOR* streamEditor,
   _Inout_ NET_BUFFER_LIST* netBufferListChain,
   size_t totalDataLength,
   DWORD streamFlags
   )
/* ++

   This function first copies the stream data into a flat inspection buffer;
   it then parses the buffer looking for the matching pattern. For
   non-matching sections it re-injects the data back; for a match it skips 
   over and injects an replacement section.

   If a match can not be determined due to lack of data, it injects the
   non-matching section back and moves the potential match to the beginning
   of the inspection buffer.

   When an EOF is reached, it flushes all processed stream sections back
   and re-injects the FIN back to end the stream.

-- */
{
   NTSTATUS status = STATUS_SUCCESS;

   UINT i = 0;
   BOOLEAN streamModified = FALSE;
   BOOLEAN potentialMatch = FALSE;

   BYTE* dataStart;

   UINT findLength = (UINT) strlen(configStringToFind);

   status = StreamOobCopyDataToFlatBuffer(
               streamEditor,
               netBufferListChain,
               totalDataLength,
               streamFlags
               );
   if (!NT_SUCCESS(status))
   {
      goto Exit;
   }
    
   dataStart =  (BYTE*)streamEditor->scratchBuffer + streamEditor->dataOffset;

   for (; i < streamEditor->dataLength; ++i)
   {
      if (i + findLength <= streamEditor->dataLength)
      {
         if (RtlCompareMemory(
               dataStart + i,
               configStringToFind,
               findLength
               ) == findLength)
         {
            if (i != 0)
            {
               status = StreamOobReinjectData(
                              streamEditor,
                              streamFlags, 
                              dataStart,
                              i
                              );

               if (!NT_SUCCESS(status))
               {
                  goto Exit;
               }

               streamEditor->dataOffset += i;
               streamEditor->dataLength -= i;

               i = 0;
            }

            status = StreamOobInjectReplacement(
                        streamEditor,
                        streamFlags, 
                        gStringToReplaceMdl,
                        strlen(configStringToReplace)
                        );

            if (!NT_SUCCESS(status))
            {
               goto Exit;
            }

            streamEditor->dataOffset += findLength;
            streamEditor->dataLength -= findLength;

            streamModified = TRUE;

            if (streamEditor->dataLength > 0)
            {
               dataStart =  (BYTE*)streamEditor->scratchBuffer + streamEditor->dataOffset;

               --i;               

               continue;
            }
            else
            {
               streamEditor->dataOffset = 0;
            }
         }
      }
      else
      {
         if (streamEditor->oobEditInfo.noMoreData)
         {
            break;
         }

         if (RtlCompareMemory(
               dataStart + i,
               configStringToFind,
               streamEditor->dataLength - i
               ) == streamEditor->dataLength - i)
         {
            potentialMatch = TRUE;  // this is a partial find

            status = StreamOobReinjectData(
                           streamEditor,
                           streamFlags, 
                           dataStart,
                           i
                           );

            if (!NT_SUCCESS(status))
            {
               goto Exit;
            }

            RtlMoveMemory(
               (BYTE*)streamEditor->scratchBuffer,
               dataStart + i,
               streamEditor->dataLength - i
               );

            streamEditor->dataOffset = 0;
            streamEditor->dataLength = streamEditor->dataLength - i;

            break;
         }
      }
   }

   if (streamModified && streamEditor->dataLength > 0)
   {
      status = StreamOobReinjectData(
                     streamEditor,
                     streamFlags, 
                     dataStart,
                     streamEditor->dataLength
                     );

      if (!NT_SUCCESS(status))
      {
         goto Exit;
      }

      streamEditor->dataOffset = 0;
      streamEditor->dataLength = 0;
   }

   if (!streamModified && !potentialMatch)
   {
      if (totalDataLength > 0)
      {
         NT_ASSERT(!(streamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && 
                !(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT));

         status = StreamOobQueueUpOutgoingData(
                     streamEditor,
                     netBufferListChain,
                     TRUE,
                     totalDataLength,
                     streamFlags,
                     NULL
                     );

         if (!NT_SUCCESS(status))
         {
            goto Exit;
         }

         netBufferListChain = NULL;
      }
      else if (streamEditor->dataLength > 0)
      {
         status = StreamOobReinjectData(
                        streamEditor,
                        streamFlags, 
                        dataStart,
                        streamEditor->dataLength
                        );

         if (!NT_SUCCESS(status))
         {
            goto Exit;
         }
      }

      streamEditor->dataOffset = 0;
      streamEditor->dataLength = 0;
   }

   if (streamEditor->oobEditInfo.nblEof != NULL)
   {
      status = StreamOobFlushOutgoingData(streamEditor);

      if (!NT_SUCCESS(status))
      {
         goto Exit;
      }

      status = FwpsStreamInjectAsync(
                  gInjectionHandle,
                  NULL,
                  0,
                  streamEditor->oobEditInfo.flowId,
                  streamEditor->oobEditInfo.calloutId,
                  streamEditor->oobEditInfo.layerId,
                  streamFlags | (configInspectionOutbound ? FWPS_STREAM_FLAG_SEND_DISCONNECT :
                                                            FWPS_STREAM_FLAG_RECEIVE_DISCONNECT),
                  streamEditor->oobEditInfo.nblEof,
                  0,
                  StreamOobInjectCompletionFn,
                  NULL
                  );

      if (!NT_SUCCESS(status))
      {
         goto Exit;
      }

      streamEditor->oobEditInfo.nblEof = NULL;
      streamEditor->oobEditInfo.noMoreData = FALSE;     
   }

Exit:

   if (netBufferListChain != NULL)
   {
      FwpsDiscardClonedStreamData(
         netBufferListChain,
         0,
         FALSE
         );
   }

   if (streamEditor->oobEditInfo.nblEof != NULL)
   {
      FwpsDiscardClonedStreamData(
         streamEditor->oobEditInfo.nblEof,
         0,
         FALSE
         );

      streamEditor->oobEditInfo.nblEof = NULL;
   }

   return status;
}
示例#3
0
NTSTATUS
InlineEditFlushData(
    _In_ STREAM_FLOW_CONTEXT *pFlowContext,
    _In_ ULONG DataLength,
    _In_ UINT StreamFlags
    )
/*
    This function re-injects buffered data back to the data stream.
    The data was buffered because it was not big enough (size wise)
    to make an editing decision.
*/
{
    NTSTATUS Status = STATUS_SUCCESS;

    PVOID Buffer = NULL;
    MDL* mdl = NULL;
    NET_BUFFER_LIST* NetBufferList = NULL;

    NT_ASSERT(!(StreamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) &&
              !(StreamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT));

    if (DataLength == 0)
	{
		DataLength = (ULONG)pFlowContext->ScratchDataLength;
    }

    DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
        "--> %!FUNC!: FlowCtx %p, Flushing %u bytes", pFlowContext, DataLength);
    do
    {
        if (DataLength == 0)
            break;

        Buffer = ExAllocatePoolWithTag(NonPagedPool, DataLength, STMEDIT_TAG_MDL_DATA);
        if (Buffer == NULL)
		{
            Status = STATUS_INSUFFICIENT_RESOURCES;
            DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "Failed to allocate Buffer to flush data!");
            break;
        }

        // Copy the contents that need to be flushed.
        RtlMoveMemory(Buffer, pFlowContext->ScratchBuffer, DataLength);

        mdl = IoAllocateMdl(
                    Buffer,
					DataLength,
                    FALSE,
                    FALSE,
                    NULL);

        if (mdl == NULL)
		{
            Status = STATUS_INSUFFICIENT_RESOURCES;
            DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "Failed to allocate MDL");
            break;
        }

        MmBuildMdlForNonPagedPool(mdl);

        Status = FwpsAllocateNetBufferAndNetBufferList(
                        Globals.NetBufferListPool,
                        0,
                        0,
                        mdl,
                        0,
						DataLength,
                        &NetBufferList);

        if (!NT_SUCCESS(Status))
        {
            DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "FwpsAllocateNetBufferAndNetBufferList Failed with %!STATUS!", Status);
            break;
        }

        Status = FwpsStreamInjectAsync(
                        Globals.InjectionHandle,
                        NULL,
                        0,
                        pFlowContext->FlowHandle,
                        pFlowContext->CalloutId,
                        pFlowContext->LayerId,
                        StreamFlags,
                        NetBufferList,
						DataLength,
                        StreamEditInjectCompletionFn,
                        mdl);

        if (!NT_SUCCESS(Status))
        {
            DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "FwpsStreamInjectAsync failed with %!STATUS!", Status);
            break;
        }

        DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, Flushed %lu bytes via NBL %p, MDL %p",
                            pFlowContext, DataLength, NetBufferList, mdl);

        // Control transferred to WFP.
        mdl = NULL;
        NetBufferList = NULL;
        Buffer = NULL;

    } while (FALSE);


    if (!NT_SUCCESS(Status))
    {
        if (Buffer != NULL)
		{
            ExFreePoolWithTag(Buffer, STMEDIT_TAG_MDL_DATA);
        }

        if (mdl != NULL) 
		{
            IoFreeMdl(mdl);
        }

        if (NetBufferList != NULL) 
		{
            FwpsFreeNetBufferList(NetBufferList);
        }
    }

    DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: FlowCtx %p, %!STATUS!", pFlowContext, Status);
    return Status;
}
示例#4
0
NTSTATUS
InlineInjectToken(
    PSTREAM_FLOW_CONTEXT FlowContext,
    UINT32 StreamFlags
    )
/*
    Inject a replacement token into the data stream!
*/
{
    NTSTATUS Status;
    NET_BUFFER_LIST* NetBufferList;
    
    do
    {
        Status = FwpsAllocateNetBufferAndNetBufferList(
                        Globals.NetBufferListPool,
                        0,
                        0,
                        Globals.StringToReplaceMdl,
                        0,
                        Globals.StringToReplaceLength,
                        &NetBufferList
                        );

        if (!NT_SUCCESS(Status))
        {
            DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL,
                "FlowCtx %p, FwpsAllocateNetBufferAndNetBufferList failed with %#x, dropping connection",
                    FlowContext, Status);
            break;
        }

        Status = FwpsStreamInjectAsync(
                        Globals.InjectionHandle,
                        NULL,
                        0,
                        FlowContext->FlowHandle,
                        FlowContext->CalloutId,
                        FlowContext->LayerId,
                        StreamFlags,
                        NetBufferList,
                        Globals.StringToReplaceLength,
                        StreamEditInjectCompletionFn,
                        NULL
                        );

        if (!NT_SUCCESS(Status))
        {
            FwpsFreeNetBufferList(NetBufferList);

            DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL,
                    "FlowCtx %p, FwpsStreamInjectAsync failed with %!STATUS!, dropping connection",
                    FlowContext, Status);
            break;
        }

        FlowContext->ScratchDataOffset += Globals.StringXLength;
        FlowContext->ScratchDataLength -= Globals.StringXLength;

        if (FlowContext->ScratchDataLength > 0) 
		{
            FlowContext->InlineEditState = INLINE_EDIT_SCANNING;
        }
        else 
		{
            FlowContext->ScratchDataOffset = 0;
            FlowContext->InlineEditState = INLINE_EDIT_IDLE;
        }
    } while (FALSE);
    
    return Status;
}
示例#5
0
NTSTATUS
ReinjectPendedPacket(
	IN PENDED_PACKET *packet,
	IN FLOW_DATA *flowData)
{
	NTSTATUS status;
	UINT32 flags;
	NET_BUFFER_LIST* netBufferList = NULL;
	FLOW_DATA *flowCtx;
	ULONG dataLength;

	if(packet->dataLength == 0 || packet->data == NULL)
	{
		return STATUS_UNSUCCESSFUL;
	}

	packet->mdl = IoAllocateMdl(
		packet->data,
		packet->dataLength,
		FALSE,
		FALSE,
		NULL);
	
	if (packet->mdl == NULL)
	{
		status = STATUS_NO_MEMORY;
		goto Exit;
	}

	MmBuildMdlForNonPagedPool(packet->mdl);
	
	status = FwpsAllocateNetBufferAndNetBufferList(
						gNetBufferListPool,
						0,
						0,
						packet->mdl,
						0,
						packet->dataLength,
						&netBufferList);

	if(!NT_SUCCESS(status))
	{
		goto Exit;
	}

	flags = packet->flags;
	dataLength = packet->dataLength;
	flowCtx = packet->flowContext;

#ifdef DEBUG
	debugPacket(packet);
	DbgPrintEx(
		DPFLTR_IHVNETWORK_ID,
		DPFLTR_ERROR_LEVEL,
		"\n localCtr=%d, remoteCtr=%d\n", flowCtx->localCounter, flowCtx->remoteCounter);
#endif

	// Keep correct sequence numbers
	// (Assume every reinjection is successful, otherwise synchronous injection is
	// needed for consistent sequence numbers implementation)
	if(flags & FWPS_STREAM_FLAG_SEND)
	{
		flowCtx->localCounter += dataLength;
	}
	else if(flags & FWPS_STREAM_FLAG_RECEIVE)
	{
		flowCtx->remoteCounter += dataLength;
	}
	else
	{
#ifdef DEBUG
		DbgBreakPoint();
#endif
	}

	status = FwpsStreamInjectAsync(
		gInjectionHandle,
		NULL,
		0,
		flowData->flowHandle,
		gStreamCalloutIdV4,
		FWPS_LAYER_STREAM_V4,
		flags, 
		netBufferList,
		packet->dataLength,
		StreamInjectCompletionFn,
		packet);

	if (!NT_SUCCESS(status))
	{
		goto Exit;
	}

	// Ownership transferred
	netBufferList = NULL;
	packet = NULL;

	

Exit:

	if (netBufferList != NULL)
	{
		FwpsFreeNetBufferList(netBufferList);
	}

	if (packet != NULL)
	{
		FreePendedPacket(packet);
	}

	return status;
}
NTSTATUS
StreamEditFlushData(
   _Inout_ STREAM_EDITOR* streamEditor,
   UINT64 flowId,
   UINT32 calloutId,
   UINT16 layerId,
   UINT32 streamFlags
   )
/* ++

   This function re-injects buffered data back to the data stream upon
   receiving a FIN. The data was buffered because it was not big enough
   (size wise) to make an editing decision.

-- */
{
   NTSTATUS status;

   MDL* mdl = NULL;
   NET_BUFFER_LIST* netBufferList = NULL;

   NT_ASSERT(streamEditor->dataOffset == 0);

   mdl = IoAllocateMdl(
            streamEditor->scratchBuffer,
            (ULONG)(streamEditor->dataLength),
            FALSE,
            FALSE,
            NULL
            );

   if (mdl == NULL)
   {
      status = STATUS_NO_MEMORY;
      goto Exit;
   }

   MmBuildMdlForNonPagedPool(mdl);

   status = FwpsAllocateNetBufferAndNetBufferList(
                  gNetBufferListPool,
                  0,
                  0,
                  mdl,
                  0,
                  streamEditor->dataLength,
                  &netBufferList
                  );
   if (!NT_SUCCESS(status))
   {
      goto Exit;
   }

   streamFlags &= ~(FWPS_STREAM_FLAG_SEND_DISCONNECT | FWPS_STREAM_FLAG_RECEIVE_DISCONNECT);

   status = FwpsStreamInjectAsync(
               gInjectionHandle,
               NULL,
               0,
               flowId,
               calloutId,
               layerId,
               streamFlags, 
               netBufferList,
               streamEditor->dataLength,
               StreamInjectCompletionFn,
               mdl
               );
   if (!NT_SUCCESS(status))
   {
      goto Exit;
   }

   mdl = NULL;
   netBufferList = NULL;

Exit:

   if (mdl != NULL)
   {
      IoFreeMdl(mdl);
   }
   if (netBufferList != NULL)
   {
      FwpsFreeNetBufferList(netBufferList);
   }

   return status;
}
void
StreamInlineEdit(
   _Inout_ STREAM_EDITOR* streamEditor,
   _In_ const FWPS_INCOMING_VALUES* inFixedValues,
   _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
   _In_ const FWPS_FILTER* filter,
   _In_ const FWPS_STREAM_DATA* streamData,
   _Inout_ FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket,
   _Inout_ FWPS_CLASSIFY_OUT* classifyOut
   )
/* ++

   This function implements the state machine that scans the content 
   and computes the number of bytes to permit, bytes to block, and 
   performs stream injection to replace the blocked data.

-- */
{
   UINT findLength = (UINT) strlen(configStringToFind);
   UINT replaceLength = (UINT) strlen(configStringToReplace);

   if ((streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) || 
       (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT))
   {
      if (streamEditor->dataLength > 0)
      {
         StreamEditFlushData(
            streamEditor,
            inMetaValues->flowHandle,
            filter->action.calloutId,
            inFixedValues->layerId,
            streamData->flags
            );

         streamEditor->dataLength = 0;
         streamEditor->dataOffset = 0;
      }

      NT_ASSERT(streamEditor->inlineEditState == INLINE_EDIT_WAITING_FOR_DATA);

      ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;
      classifyOut->actionType = FWP_ACTION_PERMIT;

      if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
      {
         classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
      }

      goto Exit;
   }

   if (streamData->dataLength == 0)
   {
      ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;
      classifyOut->actionType = FWP_ACTION_PERMIT;

      if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
      {
         classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
      }

      goto Exit;
   }

   if (streamEditor->inlineEditState != INLINE_EDIT_SKIPPING)
   {
      if ((streamData->dataLength < findLength) && 
          !(classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA))
      {
         ioPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA;
         ioPacket->countBytesRequired = findLength;

         classifyOut->actionType = FWP_ACTION_NONE;
         goto Exit;
      }
   }

   switch (streamEditor->inlineEditState)
   {
      case INLINE_EDIT_WAITING_FOR_DATA:
      {
         if (StreamCopyDataForInspection(
               streamEditor,
               streamData
               ) == FALSE)
         {
            ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION;
            classifyOut->actionType = FWP_ACTION_NONE;
            goto Exit;
         }

         //
         // Pass-thru to scanning
         //
      }
      case INLINE_EDIT_SCANNING:
      {
         UINT i;
         BYTE* dataStart =  (BYTE*)streamEditor->scratchBuffer + streamEditor->dataOffset;
         BOOLEAN found = FALSE;

         for (i = 0; i < streamEditor->dataLength; ++i)
         {
            if (i + findLength <= streamEditor->dataLength)
            {
               if (RtlCompareMemory(
                     dataStart + i,
                     configStringToFind,
                     findLength
                     ) == findLength)
               {
                  found = TRUE;

                  streamEditor->inlineEditState = INLINE_EDIT_MODIFYING;

                  if (i != 0)
                  {
                     ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;
                     ioPacket->countBytesEnforced = i;

                     classifyOut->actionType = FWP_ACTION_PERMIT;

                     if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
                     {
                        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
                     }

                     streamEditor->dataOffset += i;
                     streamEditor->dataLength -= i;

                     break;
                  }
                  else
                  {
                     goto modify_data;
                  }
               }
            }
            else
            {
               if (classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA)
               {
                  break;
               }

               if (RtlCompareMemory(
                     dataStart + i,
                     configStringToFind,
                     streamEditor->dataLength - i
                     ) == streamEditor->dataLength - i)
               {
                  found = TRUE;  // this is a partial find
   
                  ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;
                  ioPacket->countBytesEnforced = i;

                  classifyOut->actionType = FWP_ACTION_PERMIT;

                  if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
                  {
                     classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
                  }

                  RtlMoveMemory(
                     streamEditor->scratchBuffer,
                     dataStart + i,
                     streamEditor->dataLength - i
                     );

                  streamEditor->dataOffset = 0;
                  streamEditor->dataLength = streamEditor->dataLength - i;

                  streamEditor->inlineEditState = INLINE_EDIT_SKIPPING;
   
                  break;
               }
            }
         }

         if (!found)
         {
            ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;
            ioPacket->countBytesEnforced = 0;

            classifyOut->actionType = FWP_ACTION_PERMIT;

            if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
            {
               classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
            }

            streamEditor->dataOffset = 0;
            streamEditor->dataLength = 0;

            streamEditor->inlineEditState = INLINE_EDIT_WAITING_FOR_DATA;
         }

         break;
      }
      case INLINE_EDIT_SKIPPING:
      {
         ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;
         ioPacket->countBytesEnforced = 0;

         classifyOut->actionType = FWP_ACTION_BLOCK;
         classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;

         streamEditor->inlineEditState = INLINE_EDIT_WAITING_FOR_DATA;

         break;
      }
      case INLINE_EDIT_MODIFYING:

modify_data:
      
      {
         NTSTATUS status;
         NET_BUFFER_LIST* netBufferList;

         status = FwpsAllocateNetBufferAndNetBufferList(
                     gNetBufferListPool,
                     0,
                     0,
                     gStringToReplaceMdl,
                     0,
                     replaceLength,
                     &netBufferList
                     );

         if (!NT_SUCCESS(status))
         {
            ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION;
            classifyOut->actionType = FWP_ACTION_NONE;
            goto Exit;
         }

         status = FwpsStreamInjectAsync(
                     gInjectionHandle,
                     NULL,
                     0,
                     inMetaValues->flowHandle,
                     filter->action.calloutId,
                     inFixedValues->layerId,
                     streamData->flags,
                     netBufferList,
                     replaceLength,
                     StreamInjectCompletionFn,
                     NULL
                     );

         if (!NT_SUCCESS(status))
         {
            FwpsFreeNetBufferList(netBufferList);

            ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION;
            classifyOut->actionType = FWP_ACTION_NONE;
            goto Exit;
         }

         ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;
         ioPacket->countBytesEnforced = findLength;

         classifyOut->actionType = FWP_ACTION_BLOCK;
         classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;

         streamEditor->dataOffset += findLength;
         streamEditor->dataLength -= findLength;

         if (streamEditor->dataLength > 0)
         {
            streamEditor->inlineEditState = INLINE_EDIT_SCANNING;
         }
         else
         {
            streamEditor->dataOffset = 0;

            streamEditor->inlineEditState = INLINE_EDIT_WAITING_FOR_DATA;
         }

         break;
      }
      default:
         NT_ASSERT(FALSE);
         break;
   };

Exit:

   return;
}