summaryrefslogtreecommitdiff
path: root/network/trans
diff options
context:
space:
mode:
authorkarlf <[email protected]>2016-08-11 13:28:13 -0700
committerkarlf <[email protected]>2016-08-11 13:28:13 -0700
commit96eb96dfb613e4c745db6bd1f53a92fe7e2290fc (patch)
treead5f3ede5cbcd6b598677ce41bcf8318471bdd92 /network/trans
parent687b274aa38fd05c8c26e3068932121876d7f745 (diff)
Updated for "Windows 10 Anniversary Update" (Version 1607)
Diffstat (limited to 'network/trans')
-rw-r--r--network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp14
-rw-r--r--network/trans/ddproxy/ddproxy.sln18
-rw-r--r--network/trans/ddproxy/sys/ddproxy.vcxproj4
-rw-r--r--network/trans/ddproxy/sys/ddproxy.vcxproj.Filters8
-rw-r--r--network/trans/inspect/inspect.sln18
-rw-r--r--network/trans/inspect/sys/inspect.c48
-rw-r--r--network/trans/inspect/sys/inspect.vcxproj4
-rw-r--r--network/trans/inspect/sys/inspect.vcxproj.Filters8
-rw-r--r--network/trans/msnmntr/exe/monitor.vcxproj4
-rw-r--r--network/trans/msnmntr/exe/monitor.vcxproj.Filters6
-rw-r--r--network/trans/msnmntr/msnmntr.sln44
-rw-r--r--network/trans/msnmntr/sys/msnmntr.vcxproj4
-rw-r--r--network/trans/msnmntr/sys/msnmntr.vcxproj.Filters8
-rw-r--r--network/trans/stmedit/stmedit.sln25
-rw-r--r--network/trans/stmedit/sys/InlineEdit.c566
-rw-r--r--network/trans/stmedit/sys/LwQueue.c170
-rw-r--r--network/trans/stmedit/sys/LwQueue.h69
-rw-r--r--network/trans/stmedit/sys/OobEdit.c1412
-rw-r--r--network/trans/stmedit/sys/StreamEdit.c1633
-rw-r--r--network/trans/stmedit/sys/StreamEdit.h558
-rw-r--r--network/trans/stmedit/sys/Trace.h61
-rw-r--r--network/trans/stmedit/sys/inline_edit.c546
-rw-r--r--network/trans/stmedit/sys/inline_edit.h65
-rw-r--r--network/trans/stmedit/sys/oob_edit.c1363
-rw-r--r--network/trans/stmedit/sys/oob_edit.h83
-rw-r--r--network/trans/stmedit/sys/stmedit.inf16
-rw-r--r--network/trans/stmedit/sys/stmedit.vcxproj34
-rw-r--r--network/trans/stmedit/sys/stmedit.vcxproj.Filters39
-rw-r--r--network/trans/stmedit/sys/stream_callout.c772
-rw-r--r--network/trans/stmedit/sys/stream_callout.h79
30 files changed, 4659 insertions, 3020 deletions
diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp
index f2782ce7..e70d9999 100644
--- a/network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp
+++ b/network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp
@@ -113,20 +113,20 @@ NTSTATUS PrvKrnlHlprCopyBufferToMDL(_In_reads_(bytesToCopy) const BYTE* pBuffer,
SIZE_T mdlByteCount = 0;
SIZE_T remainingBytesToCopy = bytesToCopy;
SIZE_T copySize = 0;
+ UINT32 noExecute = 0;
*pBytesCopied = 0;
- if(MmGetMdlByteCount(pMDL) >= mdlOffset + bytesToCopy)
- {
- BYTE* pSystemAddress = 0;
- UINT32 noExecute = 0;
-
#if(NTDDI_VERSION >= NTDDI_WIN8)
- noExecute = MdlMappingNoExecute;
+ noExecute = MdlMappingNoExecute;
#endif /// (NTDDI_VERSION >= NTDDI_WIN8)
+ if(MmGetMdlByteCount(pMDL) >= mdlOffset + bytesToCopy)
+ {
+ BYTE* pSystemAddress = 0;
+
pSystemAddress = (BYTE*)MmGetSystemAddressForMdlSafe(pMDL,
LowPagePriority | noExecute);
if(pSystemAddress)
@@ -171,7 +171,7 @@ NTSTATUS PrvKrnlHlprCopyBufferToMDL(_In_reads_(bytesToCopy) const BYTE* pBuffer,
mdlByteCount);
pSystemAddress = (BYTE*)MmGetSystemAddressForMdlSafe(pMDL,
- LowPagePriority);
+ LowPagePriority | noExecute);
if(pSystemAddress)
{
RtlCopyMemory(pSystemAddress + mdlOffset,
diff --git a/network/trans/ddproxy/ddproxy.sln b/network/trans/ddproxy/ddproxy.sln
index 0b35cc64..f5f56da8 100644
--- a/network/trans/ddproxy/ddproxy.sln
+++ b/network/trans/ddproxy/ddproxy.sln
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0
MinimumVisualStudioVersion = 12.0
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ddproxy", "sys\ddproxy.vcxproj", "{88969E59-3140-4A06-8E52-7E9483F6E42C}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ddproxy", "sys\ddproxy.vcxproj", "{FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -13,14 +13,14 @@ Global
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {88969E59-3140-4A06-8E52-7E9483F6E42C}.Debug|Win32.ActiveCfg = Debug|Win32
- {88969E59-3140-4A06-8E52-7E9483F6E42C}.Debug|Win32.Build.0 = Debug|Win32
- {88969E59-3140-4A06-8E52-7E9483F6E42C}.Release|Win32.ActiveCfg = Release|Win32
- {88969E59-3140-4A06-8E52-7E9483F6E42C}.Release|Win32.Build.0 = Release|Win32
- {88969E59-3140-4A06-8E52-7E9483F6E42C}.Debug|x64.ActiveCfg = Debug|x64
- {88969E59-3140-4A06-8E52-7E9483F6E42C}.Debug|x64.Build.0 = Debug|x64
- {88969E59-3140-4A06-8E52-7E9483F6E42C}.Release|x64.ActiveCfg = Release|x64
- {88969E59-3140-4A06-8E52-7E9483F6E42C}.Release|x64.Build.0 = Release|x64
+ {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Debug|Win32.ActiveCfg = Debug|Win32
+ {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Debug|Win32.Build.0 = Debug|Win32
+ {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Release|Win32.ActiveCfg = Release|Win32
+ {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Release|Win32.Build.0 = Release|Win32
+ {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Debug|x64.ActiveCfg = Debug|x64
+ {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Debug|x64.Build.0 = Debug|x64
+ {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Release|x64.ActiveCfg = Release|x64
+ {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/network/trans/ddproxy/sys/ddproxy.vcxproj b/network/trans/ddproxy/sys/ddproxy.vcxproj
index 09c0e9bc..4e2da962 100644
--- a/network/trans/ddproxy/sys/ddproxy.vcxproj
+++ b/network/trans/ddproxy/sys/ddproxy.vcxproj
@@ -19,12 +19,12 @@
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
- <ProjectGuid>{88969E59-3140-4A06-8E52-7E9483F6E42C}</ProjectGuid>
+ <ProjectGuid>{FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}</ProjectGuid>
<RootNamespace>$(MSBuildProjectName)</RootNamespace>
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
- <SampleGuid>{DA73B837-D1BB-4D0D-908B-717988311EE3}</SampleGuid>
+ <SampleGuid>{6462DC10-B4DC-441C-8A08-D8E436A4566C}</SampleGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
diff --git a/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters b/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters
index 031dba36..8e16cf3d 100644
--- a/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters
+++ b/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters
@@ -3,19 +3,19 @@
<ItemGroup>
<Filter Include="Source Files">
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
- <UniqueIdentifier>{8D3EB44B-EF28-4AEB-BB5B-C48CB78F4C77}</UniqueIdentifier>
+ <UniqueIdentifier>{BFAC6914-B9A3-4F2B-BFE9-BFC27304CA74}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files">
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
- <UniqueIdentifier>{E9F8ED7B-6F16-4D51-ADE0-E86D3E1BD5CE}</UniqueIdentifier>
+ <UniqueIdentifier>{8FB969E4-D27A-4B85-832B-EACA8E280317}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
- <UniqueIdentifier>{4EEA7F36-617A-4A62-BE16-0D2EBF088BEF}</UniqueIdentifier>
+ <UniqueIdentifier>{293AD434-85E8-431D-9C4A-989DBCE5D623}</UniqueIdentifier>
</Filter>
<Filter Include="Driver Files">
<Extensions>inf;inv;inx;mof;mc;</Extensions>
- <UniqueIdentifier>{B59BFA52-A599-4C9B-9360-ABB5C95276BC}</UniqueIdentifier>
+ <UniqueIdentifier>{9B57B05B-FB4B-4582-8F2B-E95E2F7CD3E3}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
diff --git a/network/trans/inspect/inspect.sln b/network/trans/inspect/inspect.sln
index e5e2cc7e..40b0cead 100644
--- a/network/trans/inspect/inspect.sln
+++ b/network/trans/inspect/inspect.sln
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0
MinimumVisualStudioVersion = 12.0
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inspect", "sys\inspect.vcxproj", "{72A988A3-0603-4315-A76D-AADCC057DB3F}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inspect", "sys\inspect.vcxproj", "{5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -13,14 +13,14 @@ Global
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {72A988A3-0603-4315-A76D-AADCC057DB3F}.Debug|Win32.ActiveCfg = Debug|Win32
- {72A988A3-0603-4315-A76D-AADCC057DB3F}.Debug|Win32.Build.0 = Debug|Win32
- {72A988A3-0603-4315-A76D-AADCC057DB3F}.Release|Win32.ActiveCfg = Release|Win32
- {72A988A3-0603-4315-A76D-AADCC057DB3F}.Release|Win32.Build.0 = Release|Win32
- {72A988A3-0603-4315-A76D-AADCC057DB3F}.Debug|x64.ActiveCfg = Debug|x64
- {72A988A3-0603-4315-A76D-AADCC057DB3F}.Debug|x64.Build.0 = Debug|x64
- {72A988A3-0603-4315-A76D-AADCC057DB3F}.Release|x64.ActiveCfg = Release|x64
- {72A988A3-0603-4315-A76D-AADCC057DB3F}.Release|x64.Build.0 = Release|x64
+ {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|Win32.Build.0 = Debug|Win32
+ {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|Win32.ActiveCfg = Release|Win32
+ {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|Win32.Build.0 = Release|Win32
+ {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|x64.ActiveCfg = Debug|x64
+ {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|x64.Build.0 = Debug|x64
+ {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|x64.ActiveCfg = Release|x64
+ {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/network/trans/inspect/sys/inspect.c b/network/trans/inspect/sys/inspect.c
index 26b01e9b..418daf9a 100644
--- a/network/trans/inspect/sys/inspect.c
+++ b/network/trans/inspect/sys/inspect.c
@@ -1233,6 +1233,8 @@ TLInspectWorker(
KLOCK_QUEUE_HANDLE packetQueueLockHandle;
KLOCK_QUEUE_HANDLE connListLockHandle;
+ BOOLEAN found = FALSE;
+
UNREFERENCED_PARAMETER(StartContext);
for(;;)
@@ -1261,15 +1263,45 @@ TLInspectWorker(
if (!IsListEmpty(&gConnList))
{
- _Analysis_assume_(gConnList.Flink != NULL);
- listEntry = gConnList.Flink;
+ //
+ // Skip pended connections in the list, for which the auth decision is already taken.
+ // They should not be for inbound connections.
+ //
+ _Analysis_assume_(gConnList.Flink != NULL);
+ for (listEntry = gConnList.Flink;
+ listEntry != &gConnList;
+ listEntry = listEntry->Flink)
+ {
+ packet = CONTAINING_RECORD(
+ listEntry,
+ TL_INSPECT_PENDED_PACKET,
+ listEntry
+ );
- packet = CONTAINING_RECORD(
- listEntry,
- TL_INSPECT_PENDED_PACKET,
- listEntry
- );
- if (packet->direction == FWP_DIRECTION_INBOUND)
+ NT_ASSERT((packet->direction == FWP_DIRECTION_INBOUND) ||
+ (packet->authConnectDecision == 0));
+
+ if (packet->authConnectDecision == 0)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ //
+ // If not found, reset entry and packet
+ //
+ if (!found)
+ {
+ listEntry = NULL;
+ packet = NULL;
+ }
+
+ //
+ // Completing a pended recv_accept auth does not trigger reauth.
+ // So the pended entries for AUTH_RECV_ACCEPT are removed here.
+ //
+ if (packet != NULL && packet->direction == FWP_DIRECTION_INBOUND)
{
RemoveEntryList(&packet->listEntry);
}
diff --git a/network/trans/inspect/sys/inspect.vcxproj b/network/trans/inspect/sys/inspect.vcxproj
index 7c7ef16b..2115972e 100644
--- a/network/trans/inspect/sys/inspect.vcxproj
+++ b/network/trans/inspect/sys/inspect.vcxproj
@@ -19,12 +19,12 @@
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
- <ProjectGuid>{72A988A3-0603-4315-A76D-AADCC057DB3F}</ProjectGuid>
+ <ProjectGuid>{5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}</ProjectGuid>
<RootNamespace>$(MSBuildProjectName)</RootNamespace>
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
- <SampleGuid>{9F9DA1EB-580C-4DA1-A053-63D6E3EE2D2F}</SampleGuid>
+ <SampleGuid>{910EE323-2C33-42E0-B2DD-3C3BB1F4689E}</SampleGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
diff --git a/network/trans/inspect/sys/inspect.vcxproj.Filters b/network/trans/inspect/sys/inspect.vcxproj.Filters
index 602cb4af..96fc4202 100644
--- a/network/trans/inspect/sys/inspect.vcxproj.Filters
+++ b/network/trans/inspect/sys/inspect.vcxproj.Filters
@@ -3,19 +3,19 @@
<ItemGroup>
<Filter Include="Source Files">
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
- <UniqueIdentifier>{8A1B2EA9-BFC6-4FD8-B2F2-0216B285279F}</UniqueIdentifier>
+ <UniqueIdentifier>{A71F6518-899B-48DE-AA03-736717A44CB7}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files">
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
- <UniqueIdentifier>{A6A4AE15-BDDC-4797-B4FA-CE4EB8CF8E04}</UniqueIdentifier>
+ <UniqueIdentifier>{DAB5B4B7-AD3F-47C2-9EAD-148B9BC6A298}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
- <UniqueIdentifier>{BC4CCBE2-11CC-4557-8986-1D28735C2F00}</UniqueIdentifier>
+ <UniqueIdentifier>{1A63D560-FDA1-4F2F-8133-00B67CBBA270}</UniqueIdentifier>
</Filter>
<Filter Include="Driver Files">
<Extensions>inf;inv;inx;mof;mc;</Extensions>
- <UniqueIdentifier>{B4358050-C623-4EE4-9397-7BA2FFC69B7E}</UniqueIdentifier>
+ <UniqueIdentifier>{B163A086-1586-4E13-AED0-DA6AEEE0D31F}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
diff --git a/network/trans/msnmntr/exe/monitor.vcxproj b/network/trans/msnmntr/exe/monitor.vcxproj
index ced8f9d3..242045dd 100644
--- a/network/trans/msnmntr/exe/monitor.vcxproj
+++ b/network/trans/msnmntr/exe/monitor.vcxproj
@@ -19,11 +19,11 @@
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
- <ProjectGuid>{73B16E02-2FA9-45E9-BAC6-D2371C627EE8}</ProjectGuid>
+ <ProjectGuid>{A775BC8D-48D4-4332-B731-6135FDD0C94A}</ProjectGuid>
<RootNamespace>$(MSBuildProjectName)</RootNamespace>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
- <SampleGuid>{7817CD59-C9C2-4DE2-90D9-EF9E3F2489A8}</SampleGuid>
+ <SampleGuid>{2142A4F7-2EDA-43A2-AA9B-237435A8B04E}</SampleGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
diff --git a/network/trans/msnmntr/exe/monitor.vcxproj.Filters b/network/trans/msnmntr/exe/monitor.vcxproj.Filters
index 64c456be..c4048b2e 100644
--- a/network/trans/msnmntr/exe/monitor.vcxproj.Filters
+++ b/network/trans/msnmntr/exe/monitor.vcxproj.Filters
@@ -3,15 +3,15 @@
<ItemGroup>
<Filter Include="Source Files">
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
- <UniqueIdentifier>{C70F02F2-1801-4CE3-9B14-C3AF71C4A36B}</UniqueIdentifier>
+ <UniqueIdentifier>{23749ED7-3361-4C55-BC65-7FEB41F22410}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files">
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
- <UniqueIdentifier>{3CE61C1C-1EDB-4A29-82C7-5BDEC0568397}</UniqueIdentifier>
+ <UniqueIdentifier>{514D9180-B3CA-48A3-A0A1-6E1693F852C4}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
- <UniqueIdentifier>{0A993A34-46CF-4CCB-B3AE-1714A7538940}</UniqueIdentifier>
+ <UniqueIdentifier>{DB4C51F6-0F6E-404E-9F69-06769044B4B1}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
diff --git a/network/trans/msnmntr/msnmntr.sln b/network/trans/msnmntr/msnmntr.sln
index 9c496dca..364b4268 100644
--- a/network/trans/msnmntr/msnmntr.sln
+++ b/network/trans/msnmntr/msnmntr.sln
@@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0
MinimumVisualStudioVersion = 12.0
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{1F0A7D31-AD24-4250-A45D-1C6ED72D3AA9}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{60E53BA1-BED0-47E5-9907-8CEA2995D3A4}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{E66178FA-3CEE-447F-ACEE-15052142FE60}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{AD377DD1-7334-4B0B-BDDA-3BF2E96C8247}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "monitor", "exe\monitor.vcxproj", "{73B16E02-2FA9-45E9-BAC6-D2371C627EE8}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "monitor", "exe\monitor.vcxproj", "{A775BC8D-48D4-4332-B731-6135FDD0C94A}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msnmntr", "sys\msnmntr.vcxproj", "{8D90BF13-8184-4C0E-A938-99B22509F363}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msnmntr", "sys\msnmntr.vcxproj", "{0334D910-32A8-48D1-945A-A58B9F2FAD22}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -19,28 +19,28 @@ Global
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Debug|Win32.ActiveCfg = Debug|Win32
- {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Debug|Win32.Build.0 = Debug|Win32
- {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Release|Win32.ActiveCfg = Release|Win32
- {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Release|Win32.Build.0 = Release|Win32
- {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Debug|x64.ActiveCfg = Debug|x64
- {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Debug|x64.Build.0 = Debug|x64
- {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Release|x64.ActiveCfg = Release|x64
- {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Release|x64.Build.0 = Release|x64
- {8D90BF13-8184-4C0E-A938-99B22509F363}.Debug|Win32.ActiveCfg = Debug|Win32
- {8D90BF13-8184-4C0E-A938-99B22509F363}.Debug|Win32.Build.0 = Debug|Win32
- {8D90BF13-8184-4C0E-A938-99B22509F363}.Release|Win32.ActiveCfg = Release|Win32
- {8D90BF13-8184-4C0E-A938-99B22509F363}.Release|Win32.Build.0 = Release|Win32
- {8D90BF13-8184-4C0E-A938-99B22509F363}.Debug|x64.ActiveCfg = Debug|x64
- {8D90BF13-8184-4C0E-A938-99B22509F363}.Debug|x64.Build.0 = Debug|x64
- {8D90BF13-8184-4C0E-A938-99B22509F363}.Release|x64.ActiveCfg = Release|x64
- {8D90BF13-8184-4C0E-A938-99B22509F363}.Release|x64.Build.0 = Release|x64
+ {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Debug|Win32.Build.0 = Debug|Win32
+ {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Release|Win32.ActiveCfg = Release|Win32
+ {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Release|Win32.Build.0 = Release|Win32
+ {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Debug|x64.ActiveCfg = Debug|x64
+ {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Debug|x64.Build.0 = Debug|x64
+ {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Release|x64.ActiveCfg = Release|x64
+ {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Release|x64.Build.0 = Release|x64
+ {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Debug|Win32.ActiveCfg = Debug|Win32
+ {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Debug|Win32.Build.0 = Debug|Win32
+ {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Release|Win32.ActiveCfg = Release|Win32
+ {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Release|Win32.Build.0 = Release|Win32
+ {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Debug|x64.ActiveCfg = Debug|x64
+ {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Debug|x64.Build.0 = Debug|x64
+ {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Release|x64.ActiveCfg = Release|x64
+ {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {73B16E02-2FA9-45E9-BAC6-D2371C627EE8} = {1F0A7D31-AD24-4250-A45D-1C6ED72D3AA9}
- {8D90BF13-8184-4C0E-A938-99B22509F363} = {E66178FA-3CEE-447F-ACEE-15052142FE60}
+ {A775BC8D-48D4-4332-B731-6135FDD0C94A} = {60E53BA1-BED0-47E5-9907-8CEA2995D3A4}
+ {0334D910-32A8-48D1-945A-A58B9F2FAD22} = {AD377DD1-7334-4B0B-BDDA-3BF2E96C8247}
EndGlobalSection
EndGlobal
diff --git a/network/trans/msnmntr/sys/msnmntr.vcxproj b/network/trans/msnmntr/sys/msnmntr.vcxproj
index 3e5e6b63..07458206 100644
--- a/network/trans/msnmntr/sys/msnmntr.vcxproj
+++ b/network/trans/msnmntr/sys/msnmntr.vcxproj
@@ -19,12 +19,12 @@
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
- <ProjectGuid>{8D90BF13-8184-4C0E-A938-99B22509F363}</ProjectGuid>
+ <ProjectGuid>{0334D910-32A8-48D1-945A-A58B9F2FAD22}</ProjectGuid>
<RootNamespace>$(MSBuildProjectName)</RootNamespace>
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
- <SampleGuid>{CBB4D2BA-9991-40A9-BDA7-A164E72B50A4}</SampleGuid>
+ <SampleGuid>{E5A95292-DFAE-41E4-A0B2-62D97EC6575F}</SampleGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
diff --git a/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters b/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters
index c367ced7..3ed91334 100644
--- a/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters
+++ b/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters
@@ -3,19 +3,19 @@
<ItemGroup>
<Filter Include="Source Files">
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
- <UniqueIdentifier>{21B35188-8ABA-4678-8D98-453C58E58904}</UniqueIdentifier>
+ <UniqueIdentifier>{57260C1A-8BC8-4BFB-8443-187F6386E3CD}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files">
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
- <UniqueIdentifier>{203B4798-2BF9-4998-8E54-C0474AD49CB7}</UniqueIdentifier>
+ <UniqueIdentifier>{E379C1F3-41C5-407E-A95E-087A8732A4CE}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
- <UniqueIdentifier>{B3C8566B-2D21-4AA7-BC03-4EE7DED7DD43}</UniqueIdentifier>
+ <UniqueIdentifier>{552405F1-D8E0-402D-85DD-0B8E345F0634}</UniqueIdentifier>
</Filter>
<Filter Include="Driver Files">
<Extensions>inf;inv;inx;mof;mc;</Extensions>
- <UniqueIdentifier>{6DB69CEC-0955-4458-8D01-665D15B180CA}</UniqueIdentifier>
+ <UniqueIdentifier>{F56EC5D2-6494-433C-9F61-D4F039E15BE3}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
diff --git a/network/trans/stmedit/stmedit.sln b/network/trans/stmedit/stmedit.sln
index e99fea00..1dc7150f 100644
--- a/network/trans/stmedit/stmedit.sln
+++ b/network/trans/stmedit/stmedit.sln
@@ -1,26 +1,27 @@

Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0
+# Visual Studio 14
+VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 12.0
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stmedit", "sys\stmedit.vcxproj", "{00B26024-D8C5-40FD-A6C7-BA15FD324FD0}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stmedit", "stmedit.vcxproj", "{9CE912A5-6210-4EF8-B22D-611D13254D4C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
- Release|Win32 = Release|Win32
Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Debug|Win32.ActiveCfg = Debug|Win32
- {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Debug|Win32.Build.0 = Debug|Win32
- {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Release|Win32.ActiveCfg = Release|Win32
- {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Release|Win32.Build.0 = Release|Win32
- {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Debug|x64.ActiveCfg = Debug|x64
- {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Debug|x64.Build.0 = Debug|x64
- {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Release|x64.ActiveCfg = Release|x64
- {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Release|x64.Build.0 = Release|x64
+ {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Debug|Win32.ActiveCfg = Debug|Win32
+ {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Debug|Win32.Build.0 = Debug|Win32
+ {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Debug|x64.ActiveCfg = Release|Win32
+ {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Debug|x64.Build.0 = Release|Win32
+ {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Debug|x64.Deploy.0 = Release|Win32
+ {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Release|Win32.ActiveCfg = Release|Win32
+ {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Release|Win32.Build.0 = Release|Win32
+ {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Release|x64.ActiveCfg = Release|x64
+ {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/network/trans/stmedit/sys/InlineEdit.c b/network/trans/stmedit/sys/InlineEdit.c
new file mode 100644
index 00000000..a251d722
--- /dev/null
+++ b/network/trans/stmedit/sys/InlineEdit.c
@@ -0,0 +1,566 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved
+
+Abstract:
+ Stream Edit Callout Driver Sample.
+
+ This sample demonstrates inline stream inspection/editing
+ via the WFP stream API.
+
+Environment:
+ Kernel mode
+--*/
+
+#include "Trace.h"
+#include "StreamEdit.h"
+#include "InlineEdit.tmh"
+
+#if defined _MODULE_ID
+#undef _MODULE_ID
+#endif
+#define _MODULE_ID 'I'
+
+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;
+}
+
+#define PermitBytes(_l)\
+{\
+ ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;\
+ ioPacket->countBytesEnforced = (_l);\
+ ClassifyOut->actionType = FWP_ACTION_PERMIT;\
+}
+
+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;
+}
+
+
+VOID
+NTAPI
+InlineEditClassify(
+ _In_ const FWPS_INCOMING_VALUES* InFixedValues,
+ _In_ const FWPS_INCOMING_METADATA_VALUES* InMetaValues,
+ _Inout_ VOID* LayerData,
+ _In_ const FWPS_FILTER* Filter,
+ _In_ UINT64 InFlowContext,
+ _Inout_ FWPS_CLASSIFY_OUT* ClassifyOut
+ )
+/*
+ This is the ClassifyFn function registered by the inline stream edit callout.
+
+ An inline stream modification callout performs editing from within the
+ classifyFn call by permitting sections of the content and replacing other
+ sections by removing them and injecting new content.
+
+ Here we implement 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.
+
+ @ Inline Stream Inspection
+ @ https://msdn.microsoft.com/en-us/library/windows/hardware/ff570891.aspx
+
+ To replace a pattern Found in the middle of an indicated segment (for example,
+ n bytes followed by a pattern of p bytes followed by m bytes), the callout
+ would follow these steps:
+
+ 1. The callout's classifyFn function is invoked with 'n + p + m' bytes.
+ 2. The callout returns FWP_ACTION_PERMIT with countBytesEnforced set to n.
+ 3. The callout's classifyFn function is called again with p + m bytes.
+ (WFP will call classifyFn again if countBytesEnforced is less than the
+ indicated amount.)
+ 4. From the classifyFn function, the callout calls the FwpStreamInjectAsync0
+ function to inject the replacement pattern for p. The callout then
+ returns FWP_ACTION_BLOCK with countBytesEnforced set to p.
+ 5. The callout's classifyFn function is called again with m bytes.
+ 6. The callout returns FWP_ACTION_PERMIT with countBytesEnforced set to m.
+
+ If the indicated data is insufficient for the callout to make an inspection
+ decision, it can set FWPS_STREAM_CALLOUT_IO_PACKET->streamAction to
+ FWPS_STREAM_ACTION_NEED_MORE_DATA and set the countBytesRequired member to the
+ minimal amount WFP should accumulate before the data is indicated again. When
+ streamAction is set, the callout should return FWP_ACTION_NONE from the
+ classifyFn function.
+*/
+{
+ FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket;
+ FWPS_STREAM_DATA* streamData;
+ PSTREAM_FLOW_CONTEXT FlowContext = (PSTREAM_FLOW_CONTEXT)(ULONG_PTR)InFlowContext;
+ ULONG PartialLength = 0;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(InFixedValues);
+ UNREFERENCED_PARAMETER(InMetaValues);
+
+ ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)LayerData;
+ streamData = ioPacket->streamData;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "--> %!FUNC!: FlowCtx %p, sFlags %#x, Length %Iu, LayerId %hu, CalloutId %u, "
+ "FlowId %I64u, cFlags %#x, Flow State %!InlineState!, Scratch:%Iu/%Iu",
+ FlowContext,
+ streamData->flags,
+ streamData->dataLength,
+ InFixedValues->layerId,
+ Filter->action.calloutId,
+ InMetaValues->flowHandle,
+ ClassifyOut->flags,
+ FlowContext->InlineEditState,
+ FlowContext->ScratchDataLength,
+ FlowContext->ScratchDataOffset);
+
+
+ // The following checks if the classifyFn is invoked
+ // simultaneously on multiple processors for the same flow.
+ // This should NOT happen!
+ {
+ ULONG Processor = KeGetCurrentProcessorIndex();
+ Processor = InterlockedExchange(&FlowContext->CurrentProcessor, Processor);
+ NT_ASSERT(Processor == INVALID_PROC_NUMBER);
+ }
+
+ // If a FIN/RST has been classified, flush any data and permit the FIN/RST.
+ //
+ if ((!FlowContext->bFlowActive) ||
+ (streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) ||
+ (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, FIN/RST classified (Flow Active %d)!", FlowContext, FlowContext->bFlowActive);
+
+ if (FlowContext->ScratchDataLength > 0)
+ {
+ InlineEditFlushData(FlowContext, 0, FlowContext->PartialSFlags);
+ FlowContext->ScratchDataLength = 0;
+ FlowContext->ScratchDataOffset = 0;
+ }
+
+ NT_ASSERT(FlowContext->InlineEditState == INLINE_EDIT_IDLE);
+
+ PermitBytes(0);
+
+ goto Exit;
+ }
+
+ if (streamData->dataLength == 0)
+ {
+
+ PermitBytes(0);
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p -> DataLength = 0, Permit!", FlowContext);
+ goto Exit;
+ }
+
+ if (FlowContext->InlineEditState != INLINE_EDIT_SKIPPING)
+ {
+ if ((streamData->dataLength < Globals.StringXLength) &&
+ !(ClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA))
+ {
+ ioPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA;
+ ioPacket->countBytesRequired = (UINT32)Globals.StringXLength;
+
+ ClassifyOut->actionType = FWP_ACTION_NONE;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p - need more data!", FlowContext);
+ goto Exit;
+ }
+ }
+
+ switch (FlowContext->InlineEditState)
+ {
+ case INLINE_EDIT_IDLE:
+ {
+ NT_ASSERT(FlowContext->ScratchDataOffset == 0);
+
+ PartialLength = (ULONG)FlowContext->ScratchDataLength;
+ if (PartialLength)
+ {
+ NT_ASSERT(FlowContext->PartialSFlags);
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, Partial Data Found. Length %u", FlowContext, PartialLength);
+ }
+
+ if (FALSE == StreamEditCopyDataForInspection(FlowContext, streamData, streamData->dataLength))
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION;
+ ClassifyOut->actionType = FWP_ACTION_NONE;
+
+ DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL,
+ "FlowCtx %p, failed to copy data, drop connection", FlowContext);
+ goto Exit;
+ }
+
+ //
+ // Fall-thru to scanning
+ //
+ }
+
+ case INLINE_EDIT_SCANNING:
+ {
+ UINT i;
+ BYTE* DataStart = (BYTE*)FlowContext->ScratchBuffer + FlowContext->ScratchDataOffset;
+ BOOLEAN Found = FALSE;
+
+ for (i = 0; i < FlowContext->ScratchDataLength; ++i)
+ {
+ // Look for a Full match if we have enough data to scan
+ //
+ if (i + Globals.StringXLength <= FlowContext->ScratchDataLength)
+ {
+ if (RtlCompareMemory(DataStart + i, Globals.StringX, Globals.StringXLength ) == Globals.StringXLength)
+ {
+ // We Found a pattern match
+ Found = TRUE;
+
+ FlowContext->InlineEditState = INLINE_EDIT_MODIFYING;
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, Found match @ %lu", FlowContext, i-PartialLength);
+
+ // If the match is in the middle of data packet, permit the data
+ // before match from (n + p + m), permit n. We'll be reclassified
+ // at beginning of the match (with p + m).
+ //
+ if (i != 0)
+ {
+ // Flush any left over partial match data on the scratch buffer.
+ if (PartialLength)
+ {
+ InlineEditFlushData(FlowContext, PartialLength, FlowContext->PartialSFlags);
+ }
+
+ PermitBytes(i - PartialLength);
+
+ FlowContext->ScratchDataOffset += i;
+ FlowContext->ScratchDataLength -= i;
+
+ break;
+ }
+ else
+ {
+ Status = InlineInjectToken(FlowContext, streamData->flags);
+ if (NT_SUCCESS(Status))
+ {
+ // Block the segment for which we injected a replacement
+ //
+ ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;
+ ioPacket->countBytesEnforced = Globals.StringXLength - PartialLength;
+ ClassifyOut->actionType = FWP_ACTION_BLOCK;
+ }
+ break; // out of for loop.
+ }
+ } // if (RtlCompareMemory ...)
+ }
+
+ //
+ // If we do not have enough data, try to look for a partial pattern match
+ //
+ else
+ {
+ // If we do not expect more data to come in, quit now!
+ if (ClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA)
+ {
+ PermitBytes(0);
+ break;
+ }
+
+ if (RtlCompareMemory(DataStart + i, Globals.StringX, FlowContext->ScratchDataLength - i )
+ == FlowContext->ScratchDataLength - i)
+ {
+ Found = TRUE; // This is a partial match
+ FlowContext->InlineEditState = INLINE_EDIT_SKIPPING;
+
+ // Permit data before partial match. When we get more data, we'll look for a complete match.
+ //
+ if (PartialLength)
+ {
+ InlineEditFlushData(FlowContext, PartialLength, FlowContext->PartialSFlags);
+ }
+
+ PermitBytes(i - PartialLength);
+
+ // Move partial matching data to Scratch Buffer.
+ //
+ RtlMoveMemory(FlowContext->ScratchBuffer, DataStart + i, FlowContext->ScratchDataLength - i );
+
+ FlowContext->PartialSFlags = streamData->flags;
+
+ FlowContext->ScratchDataOffset = 0;
+ FlowContext->ScratchDataLength -= i;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "FlowCtx %p, Found partial match of %Iu byte(s) @ %lu",
+ FlowContext, FlowContext->ScratchDataLength, i - PartialLength);
+ break;
+ }
+ }
+ } // for ( i = 0; i < ScratchDataLength...
+
+ // If no match is Found, inject the whole chunk back into the stream
+ //
+ if (!Found)
+ {
+ FlowContext->InlineEditState = INLINE_EDIT_IDLE;
+
+ if (PartialLength)
+ {
+ InlineEditFlushData(FlowContext, PartialLength, FlowContext->PartialSFlags );
+ }
+
+ PermitBytes(0);
+
+ FlowContext->ScratchDataOffset = 0;
+ FlowContext->ScratchDataLength = 0;
+ }
+
+ break;
+ }
+
+ // No new data will be classified until we consume all the data originally
+ // indicated. We block the partially matching data (left in scratch-buffer),
+ // and continue search for a full match when more data in indicated.
+ //
+ case INLINE_EDIT_SKIPPING:
+ {
+ NT_ASSERT(FWPS_STREAM_ACTION_NONE == ioPacket->streamAction);
+ ioPacket->countBytesEnforced = 0;
+ ClassifyOut->actionType = FWP_ACTION_BLOCK;
+
+ FlowContext->InlineEditState = INLINE_EDIT_IDLE;
+ DoTraceLevelMessage(TRACE_LEVEL_VERBOSE, CO_GENERAL, "Contents of Blocked buffer : %!HEXDUMP!",
+ log_xstr(FlowContext->ScratchBuffer, (USHORT)streamData->dataLength));
+ break;
+ }
+
+ // Injecting a replacement pattern when a full match is
+ // Found at beginning of the scan buffer
+ case INLINE_EDIT_MODIFYING:
+ {
+ Status = InlineInjectToken(FlowContext, streamData->flags);
+ if (NT_SUCCESS(Status))
+ {
+ // Block the segment for which we injected a replacement
+ //
+ ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;
+ ioPacket->countBytesEnforced = Globals.StringXLength - PartialLength;
+ ClassifyOut->actionType = FWP_ACTION_BLOCK;
+ }
+ break;
+ }
+
+ default:
+ NT_ASSERT(FALSE);
+ break;
+ }; // switch
+
+Exit:
+
+ if (!NT_SUCCESS (Status))
+ {
+ ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION;
+ ClassifyOut->actionType = FWP_ACTION_NONE;
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "<-- %!FUNC!: FlowCtx %p, cOut->Action %#x, sAction %#x, #Enforced %Iu, Scratch: %Iu/%Iu, New State %!InlineState!",
+ FlowContext,
+ ClassifyOut->actionType,
+ ioPacket->streamAction,
+ ioPacket->countBytesEnforced,
+ FlowContext->ScratchDataLength,
+ FlowContext->ScratchDataOffset,
+ FlowContext->InlineEditState);
+
+ InterlockedExchange(&FlowContext->CurrentProcessor, INVALID_PROC_NUMBER);
+
+ return;
+}
diff --git a/network/trans/stmedit/sys/LwQueue.c b/network/trans/stmedit/sys/LwQueue.c
new file mode 100644
index 00000000..a88186c7
--- /dev/null
+++ b/network/trans/stmedit/sys/LwQueue.c
@@ -0,0 +1,170 @@
+/*++
+Copyright (c) Microsoft Corporation. All rights reserved
+
+Abstract:
+ Stream Edit Callout Driver Sample.
+ This file implements Light Weight queues using Worker Item routines.
+
+Environment:
+ Kernel mode
+
+--*/
+
+#include "LwQueue.h"
+
+IO_WORKITEM_ROUTINE LwWorker;
+
+NTSTATUS
+LwInitializeQueue(
+ _In_ PVOID IoObject,
+ _Out_ PLW_QUEUE Queue,
+ _In_ PIO_WORKITEM_ROUTINE WorkerRoutine
+ )
+/*
+ Initializes Light Weight Queue data structures!
+*/
+{
+ RtlZeroMemory(Queue, sizeof(Queue[0]));
+
+ Queue->Head = &Queue->Dummy;
+ Queue->Tail = &Queue->Dummy;
+ Queue->WorkerScheduled = FALSE;
+
+ Queue->WorkerRoutine = WorkerRoutine;
+ Queue->IoObject = IoObject;
+
+ KeInitializeSpinLock(&Queue->Lock);
+
+ Queue->WorkItem =
+ ExAllocatePoolWithTag(
+ NonPagedPool, IoSizeofWorkItem(), STMEDIT_TAG_LQWI);
+
+ if (Queue->WorkItem == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ IoInitializeWorkItem(IoObject, Queue->WorkItem);
+ Queue->Initialized = TRUE;
+ return STATUS_SUCCESS;
+}
+
+VOID
+LwUninitializeQueue(
+ _Inout_ PLW_QUEUE Queue
+ )
+/*
+ De-Initializes the Light Weight Queue!
+*/
+{
+ if (Queue->Initialized == FALSE)
+ {
+ return;
+ }
+
+ Queue->Initialized = FALSE;
+
+ IoUninitializeWorkItem(Queue->WorkItem);
+ ExFreePoolWithTag(Queue->WorkItem, STMEDIT_TAG_LQWI);
+ Queue->WorkItem = NULL;
+}
+
+__drv_functionClass(IO_WORKITEM_ROUTINE)
+__drv_requiresIRQL(PASSIVE_LEVEL)
+__drv_sameIRQL
+VOID
+LwWorker(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_opt_ PVOID Context
+ )
+/*
+ Queue processing workitem routine.
+ Drains the task queue and invokes OOB Workitem to process the tasks.
+*/
+{
+ PLW_ENTRY Entry;
+ PLW_QUEUE Queue = (PLW_QUEUE)Context;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ NT_ASSERT(Queue != NULL);
+ Entry = LwDequeueAll(Queue);
+
+ //
+ // Why were we scheduled if there are no entries?
+ //
+ NT_ASSERT(Entry != NULL);
+
+ while (Entry != NULL)
+ {
+ //
+ // Invoke the caller's worker routine.
+ //
+ Queue->WorkerRoutine(DeviceObject, Entry);
+
+ //
+ // Check if any other entries were added while we were
+ // working.
+ //
+ Entry = LwDequeueAll(Queue);
+ }
+}
+
+VOID
+LwEnqueue(
+ _In_ PLW_QUEUE Queue,
+ _In_ PLW_ENTRY Entry
+ )
+/*
+ Queue a task into the task queue.
+*/
+{
+ KLOCK_QUEUE_HANDLE LockHandle;
+
+ NT_ASSERT(Entry != NULL);
+ NT_ASSERT(Queue != NULL);
+
+ KeAcquireInStackQueuedSpinLock(&Queue->Lock, &LockHandle);
+
+ Entry->Next = NULL;
+ Queue->Tail->Next = Entry;
+ Queue->Tail = Entry;
+
+ if (!Queue->WorkerScheduled)
+ {
+ Queue->WorkerScheduled = TRUE;
+
+ IoQueueWorkItem(
+ Queue->WorkItem, LwWorker, DelayedWorkQueue, Queue);
+ }
+
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+}
+
+PLW_ENTRY
+LwDequeueAll(
+ _In_ PLW_QUEUE Queue
+ )
+{
+ KLOCK_QUEUE_HANDLE LockHandle;
+ PLW_ENTRY Entry = NULL;
+
+ KeAcquireInStackQueuedSpinLock(&Queue->Lock, &LockHandle);
+
+ NT_ASSERT(Queue->Head == &Queue->Dummy);
+
+ //
+ // Snap and return the entire queue contents.
+ //
+ Entry = Queue->Head->Next;
+ Queue->Head->Next = NULL;
+ Queue->Tail = Queue->Head;
+
+ if (Entry == NULL)
+ {
+ Queue->WorkerScheduled = FALSE;
+ }
+
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+ return Entry;
+}
diff --git a/network/trans/stmedit/sys/LwQueue.h b/network/trans/stmedit/sys/LwQueue.h
new file mode 100644
index 00000000..d62f41aa
--- /dev/null
+++ b/network/trans/stmedit/sys/LwQueue.h
@@ -0,0 +1,69 @@
+#ifndef _LWQUEUE_H
+#define _LWQUEUE_H
+
+#include <wdm.h>
+
+#define STMEDIT_TAG_LQWI 'wLeS' // Light Weight Queue Work Items.
+
+typedef struct _LW_ENTRY
+{
+ struct _LW_ENTRY *Next;
+} LW_ENTRY, *PLW_ENTRY;
+
+typedef struct _LW_QUEUE
+{
+ // Queue Head
+ PLW_ENTRY Head;
+
+ // Queue Tail
+ PLW_ENTRY Tail;
+
+ // Dummy Queue head.
+ LW_ENTRY Dummy;
+
+ // Lock for queue synchronization
+ KSPIN_LOCK Lock;
+
+ // If a workitem is already scheduled
+ BOOLEAN WorkerScheduled;
+
+ // Is the Queue initialized
+ BOOLEAN Initialized;
+
+ // Workitem for IoXxxWorkItem
+ PIO_WORKITEM WorkItem;
+
+ // Callback routine to be invoked
+ PIO_WORKITEM_ROUTINE WorkerRoutine;
+
+ // One of the caller's device objects.
+ PVOID IoObject;
+
+} LW_QUEUE, *PLW_QUEUE;
+
+
+NTSTATUS
+LwInitializeQueue(
+ _In_ PVOID IoObject,
+ _Out_ PLW_QUEUE Queue,
+ _In_ PIO_WORKITEM_ROUTINE WorkerRoutine
+ );
+
+VOID
+LwUninitializeQueue(
+ _Inout_ PLW_QUEUE Queue
+ );
+
+VOID
+LwEnqueue(
+ _In_ PLW_QUEUE Queue,
+ _In_ PLW_ENTRY Entry
+ );
+
+PLW_ENTRY
+LwDequeueAll(
+ _In_ PLW_QUEUE Queue
+ );
+
+
+#endif // _LWQUEUE_H
diff --git a/network/trans/stmedit/sys/OobEdit.c b/network/trans/stmedit/sys/OobEdit.c
new file mode 100644
index 00000000..28883ab7
--- /dev/null
+++ b/network/trans/stmedit/sys/OobEdit.c
@@ -0,0 +1,1412 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved
+
+Abstract:
+ Stream Edit Callout Driver Sample.
+
+ This sample demonstrates Out-of-band (OOB) stream inspection/editing
+ via the WFP stream API.
+
+Environment:
+ Kernel mode
+
+--*/
+
+#include "Trace.h"
+#include "StreamEdit.h"
+#include "OobEdit.tmh"
+
+#if defined _MODULE_ID
+#undef _MODULE_ID
+#endif
+#define _MODULE_ID 'O'
+
+NTSTATUS
+StreamEditInitializeWorkitemPool(
+ )
+{
+/*
+ This functions initializes the Light Weight Queue (LW_QUEUE) pool.
+*/
+ NTSTATUS Status;
+ ULONG nCount;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!");
+
+ for (nCount = 0; nCount < NUM_WORKITEM_QUEUES; nCount++)
+ {
+ NT_ASSERT(Globals.WdmDevice);
+ Status = LwInitializeQueue(Globals.WdmDevice, &Globals.ProcessingQueues[nCount], StreamEditOobPoolWorker);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "Worker Queue Initialization failed with %!STATUS!", Status);
+ break;
+ }
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: Status %!STATUS!", Status);
+ return Status;
+}
+
+VOID
+NTAPI
+StreamOobInjectCompletionFn(
+ _In_ PVOID Context,
+ _Inout_ NET_BUFFER_LIST* NetBufferList,
+ _In_ BOOLEAN DispatchLevel
+ )
+/*
+ Injection completion function for injecting an NBL created using
+ FwpsAllocateNetBufferAndNetBufferList. This function frees up
+ resources allocated during StreamOobReinjectData().
+*/
+{
+ MDL* mdl = (MDL *)Context;
+
+ UNREFERENCED_PARAMETER(DispatchLevel);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "-><- %!FUNC!: NBL %p, Status=%!STATUS!, MDL %p", NetBufferList, NetBufferList->Status, mdl);
+
+
+ // Supress warning 28922: Redundant test against NULL. Pointer is already guaranteed to be non-NULL.
+ // Rationale : mdl is not guaranteed to be non-NULL here...
+
+#pragma prefast(push)
+#pragma prefast(disable:28922)
+
+ if (mdl != NULL)
+ {
+ //
+ // The MDL mapped over a pool alloc which we need to free here.
+ //
+ ExFreePoolWithTag(mdl->MappedSystemVa, STMEDIT_TAG_MDL_DATA);
+
+ IoFreeMdl(mdl);
+ }
+#pragma prefast(pop)
+
+ FwpsFreeNetBufferList(NetBufferList);
+}
+
+VOID
+NTAPI
+StreamOobCloneInjectCompletionFn(
+ _In_ VOID* Context,
+ _Inout_ NET_BUFFER_LIST* NetBufferList,
+ _In_ BOOLEAN DispatchLevel
+ )
+/*
+ Injection completion function for injecting one of the NBLs cloned
+ via FwpsCloneStreamData.
+
+ FwpsCloneStreamData can return a chain of cloned NBLs; each NBL will
+ complete separately.
+*/
+{
+ UNREFERENCED_PARAMETER(Context);
+ UNREFERENCED_PARAMETER(DispatchLevel);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "-><- %!FUNC!: NBL %p, Status=%!STATUS!", NetBufferList, NetBufferList->Status);
+ FwpsFreeCloneNetBufferList(NetBufferList, 0);
+}
+
+_Requires_lock_held_(TaskEntry->FlowCtx->OobInfo.EditLock)
+NTSTATUS
+StreamOobQueueUpIncomingData(
+ _Inout_ PTASK_ENTRY TaskEntry,
+ _Inout_ FWPS_STREAM_DATA* streamData,
+ _In_ UINT32 flags
+ )
+/*
+ This function clones the indicated stream data into a NBL chain and
+ saves it in the task entry, along with other information (Flags etc.)
+
+ This function assumes that the OobInfo lock inside the flow's context
+ is being held.
+*/
+{
+ NTSTATUS Status;
+ NET_BUFFER_LIST* ClonedNbl;
+ PSTREAM_FLOW_CONTEXT FlowCtx = TaskEntry->FlowCtx;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!: FlowCtx %p, Task %p, StreamData %p",
+ FlowCtx,
+ TaskEntry,
+ streamData);
+
+ Status = FwpsCloneStreamData(streamData, NULL, NULL, 0, &ClonedNbl);
+ if (NT_SUCCESS(Status))
+ {
+ PLW_QUEUE Queue;
+ ULONG Count;
+ TaskEntry->StreamFlags = streamData->flags;
+ TaskEntry->NetBufferList = ClonedNbl;
+ TaskEntry->DataLength = streamData->dataLength;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "NBL Chain @ %p was Cloned as %p and added to Task %p",
+ streamData->netBufferListChain, ClonedNbl, TaskEntry);
+
+ Count = InterlockedIncrement(& (LONG)FlowCtx->OobInfo.PendingTasks);
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_REFCOUNT,
+ "FlowCtx %p, QueueIncoming : PendingTasks @++ = %lu", FlowCtx, Count);
+
+ // Reference the flow for task
+ StmEditReferenceFlow(FlowCtx, _MODULE_ID, __LINE__);
+
+ Queue = &(Globals.ProcessingQueues[FlowCtx->OobInfo.QueueNumber]);
+ LwEnqueue(Queue , &TaskEntry->LwQLink);
+
+ if (flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA)
+ {
+ FlowCtx->bNoMoreData = TRUE;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "FlowCtx %p, NO_MORE_DATA flag set, bNoMoreData = TRUE.", FlowCtx);
+ }
+
+ //
+ // TCP FIN is indicated by an empty NBL with disconnect flag set and
+ // does not contain any data.
+ //
+
+ if ((streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) ||
+ (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Received FIN for FlowCtx %p in Task %p", FlowCtx, TaskEntry);
+
+ NT_ASSERT(FlowCtx->bNoMoreData);
+ NT_ASSERT(streamData->dataLength == 0);
+
+ FlowCtx->bFlowTerminating = TRUE;
+ }
+
+ InterlockedAdd( (LONG *)&FlowCtx->OobInfo.PendedDataLength, (LONG)streamData->dataLength);
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "<-- %!FUNC!: FlowCtx %p, Pending %Iu, %!STATUS!", FlowCtx, FlowCtx->OobInfo.PendedDataLength, Status);
+ return Status;
+}
+
+_Requires_lock_not_held_(FlowContext->OobInfo.EditLock)
+NTSTATUS
+StreamOobQueueUpOutgoingData(
+ _Inout_ PSTREAM_FLOW_CONTEXT FlowContext,
+ _Inout_ PNET_BUFFER_LIST NetBufferList,
+ _In_ BOOLEAN IsClone,
+ _In_ size_t DataLength,
+ _In_ UINT32 StreamFlags,
+ _In_opt_ MDL* Mdl
+ )
+/*
+ This function queues up processed data (either sections of the indicated
+ data or newly created data) such that they can be (re-)injected back to
+ the data stream during the following context.
+
+ 1. Before FWP_ACTION_BLOCK is returned from the ClassifyFn, or
+ 2. After EOF is indicated.
+
+ Under the conditions above, the incoming data (which we pend) and the
+ outgoing data (which we (re-)inject) can be synchronized properly).
+*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ KLOCK_QUEUE_HANDLE LockHandle;
+ POUTGOING_STREAM_DATA OutgoingData;
+
+ NT_ASSERT(0 != StreamFlags);
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "--> %!FUNC!: FlowCtx %p, DataLength %Iu, sFlags %#x",
+ FlowContext, DataLength, StreamFlags);
+
+ OutgoingData = (POUTGOING_STREAM_DATA)ExAllocateFromLookasideListEx(&Globals.LookasideList);
+
+ if (OutgoingData == NULL)
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ return Status;
+ }
+
+ OutgoingData->NetBufferList = NetBufferList;
+ OutgoingData->isClone = IsClone;
+ OutgoingData->DataLength = DataLength;
+ OutgoingData->StreamFlags = StreamFlags;
+ OutgoingData->Mdl = Mdl;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "OutgoingStreamData %p has NBL %p (clone %d) with Length of %Iu, sFlags %#x, mdl %p",
+ OutgoingData, NetBufferList, IsClone, DataLength, StreamFlags, Mdl);
+
+ KeAcquireInStackQueuedSpinLock(&FlowContext->OobInfo.EditLock, &LockHandle);
+ InsertTailList(&FlowContext->OobInfo.OutgoingDataQueue, &OutgoingData->Link);
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,"<-- %!FUNC!: %!STATUS!", Status);
+ return Status;
+}
+
+NTSTATUS
+StreamOobFlushOutgoingData(
+ _Inout_ PSTREAM_FLOW_CONTEXT FlowContext
+ )
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ KLOCK_QUEUE_HANDLE LockHandle;
+ OUTGOING_STREAM_DATA* OutgoingData = NULL;
+ BOOLEAN FlowInError = FALSE;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!: FlowCtx %p", FlowContext);
+
+ for (;;)
+ {
+ KeAcquireInStackQueuedSpinLock(&FlowContext->OobInfo.EditLock, &LockHandle);
+
+ if (!IsListEmpty(&FlowContext->OobInfo.OutgoingDataQueue))
+ {
+ LIST_ENTRY* Entry = RemoveHeadList(&FlowContext->OobInfo.OutgoingDataQueue);
+ OutgoingData = CONTAINING_RECORD(Entry, OUTGOING_STREAM_DATA, Link);
+
+ FlowInError = (OOB_EDIT_ERROR == FlowContext->OobInfo.EditState);
+ }
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+
+ if (OutgoingData == NULL || FlowInError)
+ {
+ break;
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "Oob Flush Outgoing: OutgoingStreamData %p, Injecting NBL %p, Data Length %Iu, Stream Flags %#x",
+ OutgoingData,
+ OutgoingData->NetBufferList,
+ OutgoingData->DataLength,
+ OutgoingData->StreamFlags);
+
+ Status = FwpsStreamInjectAsync(
+ Globals.InjectionHandle,
+ NULL,
+ 0,
+ FlowContext->FlowHandle,
+ FlowContext->CalloutId,
+ FlowContext->LayerId,
+ OutgoingData->StreamFlags,
+ OutgoingData->NetBufferList,
+ OutgoingData->DataLength,
+ OutgoingData->isClone ? StreamOobCloneInjectCompletionFn : StreamOobInjectCompletionFn,
+ OutgoingData->Mdl
+ );
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL,
+ "OobFlushOutgoingData: FwpsStreamInjectAsync() failed with Status %!STATUS!", Status);
+ break;
+ }
+
+ ExFreeToLookasideListEx(&Globals.LookasideList, OutgoingData);
+ OutgoingData = NULL;
+ }
+
+ if (!NT_SUCCESS(Status) || FlowInError)
+ {
+ while (OutgoingData != NULL)
+ {
+ if (OutgoingData->isClone)
+ {
+ FwpsDiscardClonedStreamData(OutgoingData->NetBufferList, 0, FALSE);
+ }
+ else
+ {
+ // Invoke the injection completion routine to free the resources
+ StreamOobInjectCompletionFn(OutgoingData->Mdl, OutgoingData->NetBufferList, FALSE);
+ }
+
+ ExFreeToLookasideListEx(&Globals.LookasideList, OutgoingData);
+ OutgoingData = NULL;
+
+ // Free up any queued data.
+ //
+ KeAcquireInStackQueuedSpinLock(&FlowContext->OobInfo.EditLock, &LockHandle);
+
+ if (!IsListEmpty(&FlowContext->OobInfo.OutgoingDataQueue))
+ {
+ LIST_ENTRY* Entry = RemoveHeadList(&FlowContext->OobInfo.OutgoingDataQueue);
+ OutgoingData = CONTAINING_RECORD(Entry, OUTGOING_STREAM_DATA, Link);
+ }
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+ }
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "<-- %!FUNC!: FlowCtx %p, FlowInError %d, Status %!STATUS!",
+ FlowContext, FlowInError, Status);
+ return Status;
+}
+
+_Requires_lock_not_held_(FlowContext->OobInfo.EditLock)
+NTSTATUS
+StreamOobReinjectData(
+ _In_ STREAM_FLOW_CONTEXT* FlowContext,
+ _In_ const PVOID Data,
+ _In_ size_t Length,
+ _In_ UINT32 StreamFlags
+ )
+/*
+ This function injects a section of the original indicated data back
+ to the data stream.
+
+ An MDL is allocated to describe the data section.
+*/
+{
+ NTSTATUS Status;
+
+ VOID* DataCopy = NULL;
+ MDL* mdl = NULL;
+ NET_BUFFER_LIST* NetBufferList = NULL;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "--> %!FUNC!: FlowCtx %p, Length %Iu, sFlags 0x%x", FlowContext, Length, StreamFlags);
+
+ NT_ASSERT(StreamFlags);
+ NT_ASSERT(!(StreamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) &&
+ !(StreamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT));
+ NT_ASSERT(Length);
+
+ do
+ {
+ DataCopy = ExAllocatePoolWithTag(
+ NonPagedPool,
+ Length,
+ STMEDIT_TAG_MDL_DATA
+ );
+
+ if (DataCopy == NULL)
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "Failed to allocate memory.");
+ break;
+ }
+
+ RtlCopyMemory(DataCopy, Data, Length);
+
+ mdl = IoAllocateMdl(
+ DataCopy,
+ (ULONG)Length,
+ FALSE,
+ FALSE,
+ NULL);
+
+ if (mdl == NULL)
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "Failed to allocate MDL for re-injection.");
+ break;
+ }
+ MmBuildMdlForNonPagedPool(mdl);
+
+ Status = FwpsAllocateNetBufferAndNetBufferList(
+ Globals.NetBufferListPool,
+ 0,
+ 0,
+ mdl,
+ 0,
+ Length,
+ &NetBufferList
+ );
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "AllocateNetBufferAndNetBufferList failed with %!STATUS!\n", Status);
+ break;
+ }
+
+ Status = StreamOobQueueUpOutgoingData(
+ FlowContext,
+ NetBufferList,
+ FALSE,
+ Length,
+ StreamFlags,
+ mdl
+ );
+
+ if (NT_SUCCESS(Status))
+ {
+ DataCopy = NULL;
+ mdl = NULL;
+ NetBufferList = NULL;
+ }
+ } while (FALSE);
+
+ if (!NT_SUCCESS(Status))
+ {
+ if (NetBufferList != NULL)
+ {
+ FwpsFreeNetBufferList(NetBufferList);
+ }
+
+ if (mdl != NULL)
+ {
+ IoFreeMdl(mdl);
+ }
+
+ if (DataCopy != NULL)
+ {
+ ExFreePoolWithTag(DataCopy, STMEDIT_TAG_MDL_DATA );
+ }
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "<-- %!FUNC!: FlowCtx %p, %!STATUS!", FlowContext, Status);
+
+ return Status;
+}
+
+_Requires_lock_not_held_(FlowContext->OobInfo.EditLock)
+NTSTATUS
+StreamOobInjectReplacement(
+ _In_ STREAM_FLOW_CONTEXT* FlowContext,
+ _In_ UINT32 StreamFlags,
+ _In_ MDL* Mdl, // Optional
+ _In_ size_t Length
+ )
+/*
+ This function injects a section of replacement data (in place of data
+ removed from the stream) into the data stream.
+
+ The MDL describing the replacement data is allocated during DriverEntry
+ and does not need to be freed during injection completion.
+*/
+{
+ NTSTATUS Status;
+ NET_BUFFER_LIST* NetBufferList = NULL;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "--> %!FUNC!: FlowCtx %p, sFlags %#x, Length %Iu", FlowContext, StreamFlags, Length);
+
+ Status = FwpsAllocateNetBufferAndNetBufferList(
+ Globals.NetBufferListPool,
+ 0,
+ 0,
+ Mdl,
+ 0,
+ Length,
+ &NetBufferList
+ );
+
+ if (NT_SUCCESS(Status))
+ {
+ NT_ASSERT(!(StreamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) &&
+ !(StreamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT));
+
+ Status = StreamOobQueueUpOutgoingData(
+ FlowContext,
+ NetBufferList,
+ FALSE,
+ Length,
+ StreamFlags,
+ NULL
+ );
+
+ if (NT_SUCCESS(Status))
+ {
+ NetBufferList = NULL;
+ }
+ }
+
+ if (NetBufferList)
+ {
+ FwpsFreeNetBufferList(NetBufferList);
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: FlowCtx %p, %!STATUS!", FlowContext, Status);
+
+ return Status;
+}
+
+FORCEINLINE
+NTSTATUS
+StreamOobCopyDataToFlatBuffer(
+ _Inout_ PTASK_ENTRY TaskEntry
+ )
+/*
+ This function copies the data described by NBL(s) into a flat buffer for easy searching.
+
+ It reuses the FwpsCopyStreamDataToBuffer API (via StreamEditCopyDataForInspection)
+ by creating a FWPS_STREAM_DATA struct.
+*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ FWPS_STREAM_DATA streamData;
+
+ if (TaskEntry->DataLength > 0)
+ {
+ streamData.netBufferListChain = TaskEntry->NetBufferList;
+ streamData.dataLength = TaskEntry->DataLength;
+ streamData.flags = TaskEntry->StreamFlags;
+
+ streamData.dataOffset.netBufferList = TaskEntry->NetBufferList;
+ streamData.dataOffset.netBuffer = NET_BUFFER_LIST_FIRST_NB(streamData.dataOffset.netBufferList);
+ streamData.dataOffset.mdl = NET_BUFFER_CURRENT_MDL(streamData.dataOffset.netBuffer);
+ streamData.dataOffset.mdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(streamData.dataOffset.netBuffer);
+
+ streamData.dataOffset.netBufferOffset = 0;
+ streamData.dataOffset.streamDataOffset = 0;
+
+ if (!StreamEditCopyDataForInspection(TaskEntry->FlowCtx, &streamData, streamData.dataLength))
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+ return Status;
+}
+
+_Requires_lock_not_held_(TaskEntry->FlowCtx->OobInfo.EditLock)
+NTSTATUS
+StreamOobEditData(
+_Inout_ PTASK_ENTRY TaskEntry
+)
+/*
+ This function processes Stream data in "Out of Band" processing, looking
+ for pattern matches, and replacing them.
+
+ It 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 a replacement pattern.
+
+ If a match can not be determined due to lack of data, it injects the
+ non-matching section back and moves the potential (or partial) match
+ to the beginning of the (flattened) inspection buffer.
+
+ If a FIN is presented by the NetBufferList, it flushes all processed stream
+ sections back and re-injects the FIN back at the end the stream.
+*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PSTREAM_FLOW_CONTEXT FlowContext = TaskEntry->FlowCtx;
+ UINT i;
+ BOOLEAN bStreamModified, bPartialMatch;
+ BYTE* dataStart;
+
+
+ size_t BytesToProcess = 0; // # of bytes to be processed in one loop!
+ size_t ProcessedBytes = 0; // # of bytes of original data actually processed/consumed
+ size_t BytesRemaining = TaskEntry->DataLength; // # of bytes of original data remaining to be processed.
+
+ FWPS_STREAM_DATA StreamData; // Local StreamData struct for keeping track of stream data offset
+ PNET_BUFFER_LIST TheNbl; // The NBL to be processed next in the StreamData's netBufferListChain
+
+ BOOLEAN bIsLastNbl; // True if last NBL of the chain is being processed
+ BOOLEAN bProcessingPartialTask = FALSE; // True if we are only processing a part of current task data
+
+ NT_ASSERT(TaskEntry->NetBufferList != NULL);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "--> %!FUNC!: FlowCtx %p, Task %p, NBL Chain @ %p, Length %Iu, sFlags %#x, Pended Data %Iu",
+ FlowContext,
+ TaskEntry,
+ TaskEntry->NetBufferList,
+ TaskEntry->DataLength,
+ TaskEntry->StreamFlags,
+ FlowContext->OobInfo.PendedDataLength);
+
+ StreamData.dataLength = TaskEntry->DataLength;
+ StreamData.netBufferListChain = TheNbl = TaskEntry->NetBufferList;
+ StreamData.flags = TaskEntry->StreamFlags;
+ StreamData.dataOffset.streamDataOffset = 0;
+
+ while (BytesRemaining)
+ {
+ bStreamModified = bPartialMatch = FALSE;
+
+ StreamData.dataOffset.netBufferList = TheNbl;
+
+ StreamData.dataOffset.netBuffer = NET_BUFFER_LIST_FIRST_NB(TheNbl);
+ StreamData.dataOffset.netBufferOffset = NET_BUFFER_CURRENT_MDL_OFFSET(StreamData.dataOffset.netBuffer);
+
+ StreamData.dataOffset.mdl = NET_BUFFER_CURRENT_MDL(StreamData.dataOffset.netBuffer);
+ StreamData.dataOffset.mdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(StreamData.dataOffset.netBuffer);
+
+ StreamData.dataOffset.streamDataOffset += BytesToProcess;
+
+ // Presume we are going to process all data.
+ BytesToProcess = BytesRemaining;
+ bIsLastNbl = TRUE;
+
+ // However, if we have more data than we can handle, lets try to consume it in chunks!
+ //
+ if (BytesRemaining + FlowContext->ScratchDataLength > Globals.BusyThreshold)
+ {
+
+ // Process ONE NBL at a time...
+ //
+ // It is possible that the length of NBL being processed is more than the
+ // BusyThreshold limit (though, very unlikely for a realistic BusyThreshold).
+ //
+ // The code can be modified to handle such situations -- i.e. to process a
+ // partial NBL or to process multiple NBLs (or a combination thereof ) at a
+ // time by appropriately setting StreamData.dataOffset.
+ //
+ // This is left as an exercise for the user.
+ //
+
+ BytesToProcess = NetBufferListLength(TheNbl);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "Partially processing task %p - Now Processing NBL %p with %Iu bytes",
+ TaskEntry, TheNbl, BytesToProcess);
+
+ TheNbl = TheNbl->Next;
+
+ if (TheNbl != NULL)
+ bIsLastNbl = FALSE;
+
+ bProcessingPartialTask = TRUE;
+
+ if (BytesToProcess == 0)
+ {
+ NT_ASSERT(TheNbl != NULL);
+ continue;
+ }
+ }
+
+
+ if (!StreamEditCopyDataForInspection(TaskEntry->FlowCtx, &StreamData, BytesToProcess))
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "FlowCtx %p, Task %p - CopyDataToFlatBuffer failed with status %#x",
+ FlowContext, TaskEntry, Status);
+
+ goto Exit;
+ }
+
+
+ /*
+ Search for a pattern through the "flattened" buffer.
+
+ If we find a full match, inject the (if any) data before the match into stream,
+ followed by the injection of replacement pattern.
+
+ If we find a partial match (at the end of flat buffer), then inject the data
+ before the pattern, and move the partial pattern at the beginning of the ScratchBuffer.
+ Next time (if) we get more data, we can try for a full match.
+ */
+
+ NT_ASSERT(FlowContext->ScratchDataOffset == 0);
+ dataStart = (BYTE*)FlowContext->ScratchBuffer;
+
+ ProcessedBytes = FlowContext->ScratchDataLength;
+
+ for (i = 0; i < FlowContext->ScratchDataLength; ++i)
+ {
+ // If there is enough data on which to perform a search
+ //
+ if (i + Globals.StringToFindLength <= FlowContext->ScratchDataLength)
+ {
+ if (RtlCompareMemory(
+ dataStart + i,
+ Globals.StringToFind,
+ Globals.StringToFindLength
+ ) == Globals.StringToFindLength)
+ {
+ // If the match is not at the beginning of the data,
+ // inject back the data before the match
+ //
+ if (i != 0)
+ {
+ Status = StreamOobReinjectData(FlowContext, dataStart, i, TaskEntry->StreamFlags);
+ if (!NT_SUCCESS(Status))
+ {
+ goto Exit;
+ }
+
+ FlowContext->ScratchDataOffset += i;
+ FlowContext->ScratchDataLength -= i;
+ i = 0;
+ }
+
+ // Now inject the replacement string in place of the match (Globals.StringToFind)!
+ //
+ Status = StreamOobInjectReplacement(
+ FlowContext,
+ TaskEntry->StreamFlags,
+ Globals.StringXMdl,
+ Globals.StringXLength
+ );
+
+ if (!NT_SUCCESS(Status))
+ {
+ goto Exit;
+ }
+
+ FlowContext->ScratchDataOffset += Globals.StringToFindLength;
+ FlowContext->ScratchDataLength -= Globals.StringToFindLength;
+
+ bStreamModified = TRUE;
+
+ // Still more data to be searched for the match
+ //
+ if (FlowContext->ScratchDataLength > 0)
+ {
+ dataStart = (BYTE*)FlowContext->ScratchBuffer + FlowContext->ScratchDataOffset;
+ --i;
+
+ continue;
+ }
+ else {
+ FlowContext->ScratchDataOffset = 0;
+ }
+ }
+ }
+ else // If we do not have enough data to perform a search on
+ {
+ // If we do not expect more data to come in, get out...
+ // 1 == FlowContext->OobInfo.RefCount ==> This is the last
+ // (data-processing) Task being processed for the flow
+ //
+ if (bIsLastNbl && FlowContext->bNoMoreData && (0 == FlowContext->OobInfo.PendingTasks))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "FlowCtx %p -> giving up on partial match search - offset %lu, scratch length %Iu",
+ FlowContext, i, FlowContext->ScratchDataLength);
+
+ break;
+ }
+
+ // Look for a partial match!
+ // If we find a partial match, move the partially matching pattern
+ // to the beginning of ScratchBuffer... when more data comes in,
+ // we'll try a complete match again.
+ //
+ if (RtlCompareMemory(dataStart + i, Globals.StringToFind, FlowContext->ScratchDataLength - i)
+ == FlowContext->ScratchDataLength - i)
+ {
+ bPartialMatch = TRUE; // This is a partial find
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "FlowCtx %p -> partial match @ offset %lu, match length %Iu",
+ FlowContext, i, (FlowContext->ScratchDataLength - i));
+
+ if (i != 0)
+ {
+ // Inject any data before partial match back into the stream
+ //
+ Status = StreamOobReinjectData(
+ FlowContext,
+ dataStart,
+ i,
+ TaskEntry->StreamFlags
+ );
+
+ if (!NT_SUCCESS(Status))
+ {
+ goto Exit;
+ }
+ }
+ // Move the partially matching bytes to the beginning of scratch buffer
+ //
+ RtlMoveMemory((BYTE*)FlowContext->ScratchBuffer, dataStart + i, FlowContext->ScratchDataLength - i);
+ FlowContext->PartialSFlags = TaskEntry->StreamFlags;
+
+ FlowContext->ScratchDataOffset = 0;
+ FlowContext->ScratchDataLength -= i;
+
+ break;
+ }
+ }
+ }
+
+ //
+ // At this point we should fall into one of the following :
+ // case 1: We found a partial match (irrespective of whether a full match was
+ // found before it or not). Data before the partial match has already
+ // been injected; we do not need to inject any more data.
+ //
+ // case 2: We found a full match (but no partial match).
+ // Data before the match has been taken care of; if there is any data
+ // left after the matching token, inject it.
+ //
+ // case 3: No full or partial match was found.
+ // subcase 1: There is data left in the ScratchBuffer from a previous
+ // partial match:
+ // inject "data left from previous match"
+ // . . . then the current NBL (subcase 2 below)
+ //
+ // subcase 2: There is no data left from previous partial match
+ // (i.e. all data is what was in the indicated NBL Chain) :
+ // inject the indicated NetBufferList Chain...
+ //
+ // Injecting (original/indicated) NBLs will save us from having to
+ // allocate memory and unnecessary processing to create new NBL.
+ //
+
+ if (bPartialMatch)
+ {
+ // case 1. nothing to be done!
+ //
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "FlowCtx %p: Partial match, nothing to reinject, %Iu bytes left...",
+ FlowContext, FlowContext->ScratchDataLength);
+ }
+ else if (bStreamModified)
+ {
+ // case 2. We found a match. Reinject data after matching token
+ //
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "FlowCtx %p: Full match, reinjecting %Iu remaining bytes",
+ FlowContext, FlowContext->ScratchDataLength);
+
+ if (FlowContext->ScratchDataLength > 0)
+ {
+ Status = StreamOobReinjectData(
+ FlowContext,
+ dataStart,
+ FlowContext->ScratchDataLength,
+ TaskEntry->StreamFlags
+ );
+
+ if (!NT_SUCCESS(Status))
+ {
+ goto Exit;
+ }
+
+ FlowContext->ScratchDataOffset = FlowContext->ScratchDataLength = 0;
+ }
+ }
+ else
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "FlowCtx %p: No match, reinjecting %Iu+%Iu bytes",
+ FlowContext,
+ (FlowContext->ScratchDataLength - BytesToProcess),
+ BytesToProcess);
+
+ if (!bProcessingPartialTask)
+ {
+ NT_ASSERT(TaskEntry->DataLength == BytesToProcess);
+
+ if (FlowContext->ScratchDataLength > BytesToProcess)
+ {
+ // Case 3/subcase 1: We have partial data left in the scratch buffer.
+ //
+ NT_ASSERT(FlowContext->ScratchBuffer == dataStart);
+ NT_ASSERT(FlowContext->ScratchDataOffset == 0);
+
+ Status = StreamOobReinjectData(
+ FlowContext,
+ dataStart,
+ (FlowContext->ScratchDataLength - BytesToProcess),
+ FlowContext->PartialSFlags
+ );
+
+ if (!NT_SUCCESS(Status))
+ {
+ goto Exit;
+ }
+
+ FlowContext->ScratchDataLength = BytesToProcess;
+ }
+
+ // case 3/subcase 2: Inject the rest of the (original) data.
+ //
+ if (TaskEntry->DataLength)
+ {
+ NT_ASSERT(TaskEntry->DataLength == FlowContext->ScratchDataLength);
+
+ Status = StreamOobQueueUpOutgoingData(
+ FlowContext,
+ TaskEntry->NetBufferList,
+ TRUE,
+ TaskEntry->DataLength,
+ TaskEntry->StreamFlags,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status))
+ {
+ goto Exit;
+ }
+
+ TaskEntry->NetBufferList = NULL;
+ }
+ }
+ else
+ {
+ NT_ASSERT(FlowContext->ScratchBuffer == dataStart);
+ NT_ASSERT(FlowContext->ScratchDataOffset == 0);
+
+ Status = StreamOobReinjectData(
+ FlowContext,
+ FlowContext->ScratchBuffer,
+ FlowContext->ScratchDataLength,
+ TaskEntry->StreamFlags
+ );
+
+ if (!NT_SUCCESS(Status))
+ {
+ goto Exit;
+ }
+ }
+
+ FlowContext->ScratchDataOffset = FlowContext->ScratchDataLength = 0;
+ }
+
+ ProcessedBytes -= FlowContext->ScratchDataLength;
+
+ NT_ASSERT((signed)ProcessedBytes >= 0);
+ InterlockedAdd((LONG *)&FlowContext->OobInfo.PendedDataLength, -(signed)ProcessedBytes);
+
+
+ BytesRemaining -= BytesToProcess;
+
+ NT_ASSERT(FlowContext->ScratchDataLength < Globals.StringToFindLength);
+
+ } // while (BytesRemaining);
+
+ // If we received a FIN and there are left overs in ScratchBuffer,
+ // let us inject it before injecting the FIN.
+ //
+ if ((TRUE == FlowContext->bFlowTerminating) &&
+ (0 == TaskEntry->DataLength))
+ {
+ NT_ASSERT(FlowContext->OobInfo.PendedDataLength == FlowContext->ScratchDataLength);
+
+ if (FlowContext->OobInfo.PendedDataLength)
+ {
+ NT_ASSERT(FlowContext->ScratchDataOffset == 0);
+
+ Status = StreamOobReinjectData(FlowContext,
+ FlowContext->ScratchBuffer,
+ FlowContext->ScratchDataLength,
+ FlowContext->PartialSFlags);
+
+ if (!NT_SUCCESS(Status))
+ {
+ goto Exit;
+ }
+
+ // InterlockedExchangeSubtract can be used as well!
+ //
+ InterlockedAdd((LONG *)&FlowContext->OobInfo.PendedDataLength, -(signed)FlowContext->ScratchDataLength);
+ FlowContext->ScratchDataOffset = FlowContext->ScratchDataLength = 0;
+ }
+ }
+
+
+ //
+ // Its a good time to reinject the processed data back into the stream
+ //
+ Status = StreamOobFlushOutgoingData(FlowContext);
+
+ if (!NT_SUCCESS(Status))
+ {
+ goto Exit;
+ }
+
+ // FIN/RST :: inject FIN after flushing the processed stream data
+ //
+ if ( (TRUE == FlowContext->bFlowTerminating) &&
+ (0 == TaskEntry->DataLength) )
+ {
+ NT_ASSERT(TRUE == FlowContext->bNoMoreData);
+ NT_ASSERT(FlowContext->OobInfo.PendedDataLength == 0);
+
+ NT_ASSERT( TaskEntry->StreamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT ||
+ TaskEntry->StreamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Now injecting FIN for FlowCtx %p", FlowContext);
+ Status = FwpsStreamInjectAsync(
+ Globals.InjectionHandle,
+ NULL,
+ 0,
+ FlowContext->FlowHandle,
+ FlowContext->CalloutId,
+ FlowContext->LayerId,
+ (DWORD)TaskEntry->StreamFlags,
+ TaskEntry->NetBufferList,
+ 0,
+ StreamOobCloneInjectCompletionFn,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FIN injection for FlowCtx %p failed with %!STATUS!", FlowContext, Status);
+ goto Exit;
+ }
+
+ TaskEntry->NetBufferList = NULL;
+ }
+
+Exit:
+
+ if (TaskEntry->NetBufferList != NULL)
+ {
+ FwpsDiscardClonedStreamData(TaskEntry->NetBufferList, 0, FALSE);
+ TaskEntry->NetBufferList = NULL;
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: FlowCtx %p, %!STATUS!", FlowContext, Status);
+ return Status;
+}
+
+VOID
+StreamEditOobProcessTask(
+ TASK_ENTRY *TaskEntry
+ )
+/*
+ This function processes a Task Entry data and resume
+ a paused stream if conditions are right.
+*/
+{
+ KLOCK_QUEUE_HANDLE LockHandle;
+ NTSTATUS Status = STATUS_SUCCESS;
+ STREAM_FLOW_CONTEXT* FlowCtx = TaskEntry->FlowCtx;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!: Processing Task %p for FlowCtx %p", TaskEntry, FlowCtx);
+
+ do
+ {
+ KeAcquireInStackQueuedSpinLock(&FlowCtx->OobInfo.EditLock, &LockHandle);
+
+ if (OOB_EDIT_ERROR == FlowCtx->OobInfo.EditState)
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "TaskProc: Flow in Error/Shutdown (%d) state", FlowCtx->OobInfo.EditState);
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+ break;
+ }
+
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+
+ Status = StreamOobEditData(TaskEntry);
+
+ if (!NT_SUCCESS(Status))
+ {
+ KeAcquireInStackQueuedSpinLock(&FlowCtx->OobInfo.EditLock, &LockHandle);
+ FlowCtx->OobInfo.EditState = OOB_EDIT_ERROR;
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+ break;
+ }
+
+ // Wait till we have some room to process more data
+ //
+ if (FlowCtx->OobInfo.PendedDataLength <= (Globals.BusyThreshold >> 2))
+ {
+ BOOLEAN bStreamPaused;
+
+ KeAcquireInStackQueuedSpinLock(&FlowCtx->OobInfo.EditLock, &LockHandle);
+ bStreamPaused = (OOB_EDIT_BUSY == FlowCtx->OobInfo.EditState);
+
+ if (FlowCtx->OobInfo.PendedDataLength < Globals.StringToFindLength)
+ FlowCtx->OobInfo.EditState = OOB_EDIT_IDLE;
+ else
+ if (FlowCtx->OobInfo.EditState == OOB_EDIT_BUSY)
+ FlowCtx->OobInfo.EditState = OOB_EDIT_PROCESSING;
+
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+
+ if (bStreamPaused)
+ {
+ NTSTATUS ContinueStatus;
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "TaskProc: Calling FwpsStreamContinue. PendedLength %Iu, new State %!OobState!",
+ FlowCtx->OobInfo.PendedDataLength,
+ FlowCtx->OobInfo.EditState);
+
+ ContinueStatus = FwpsStreamContinue(
+ FlowCtx->FlowHandle,
+ FlowCtx->CalloutId,
+ FlowCtx->LayerId,
+ FlowCtx->OobInfo.StreamFlags
+ );
+
+ if (!NT_SUCCESS(ContinueStatus))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "TaskProc: FwpsStreamContinue() returns with %!STATUS!", ContinueStatus);
+
+ KeAcquireInStackQueuedSpinLock(&FlowCtx->OobInfo.EditLock, &LockHandle);
+ FlowCtx->OobInfo.EditState = OOB_EDIT_ERROR;
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+
+ break;
+ }
+ }
+ }
+ } while (FALSE);
+
+ // DeRef the FlowContext for completed task
+ StmEditDeReferenceFlow(TaskEntry->FlowCtx, _MODULE_ID, __LINE__);
+
+ KeAcquireInStackQueuedSpinLock(&FlowCtx->OobInfo.EditLock, &LockHandle);
+ if (OOB_EDIT_ERROR == FlowCtx->OobInfo.EditState)
+ {
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+ (VOID)StreamOobFlushOutgoingData(FlowCtx);
+ Status = FwpsFlowAbort(FlowCtx->FlowHandle);
+
+ if (!NT_SUCCESS(Status))
+ {
+ (VOID) FwpsFlowRemoveContext(FlowCtx->FlowHandle, FlowCtx->LayerId, FlowCtx->CalloutId);
+ }
+#else
+ (VOID) StreamEditRemoveFlowCtx(FlowCtx);
+#endif
+
+ }
+ else
+ {
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "<-- %!FUNC!: Task %p for FlowCtx %p (Ref %lu, Pending %lu), State %!OobState!",
+ TaskEntry, FlowCtx, FlowCtx->RefCount, FlowCtx->OobInfo.PendingTasks, FlowCtx->OobInfo.EditState);
+}
+
+
+__drv_functionClass(IO_WORKITEM_ROUTINE)
+__drv_requiresIRQL(PASSIVE_LEVEL)
+__drv_sameIRQL
+VOID
+StreamEditOobPoolWorker(
+ _In_ PDEVICE_OBJECT DevObj,
+ _In_opt_ PVOID Context
+ )
+{
+ PLW_ENTRY LinkEntry = (PLW_ENTRY)Context;
+ PTASK_ENTRY TaskEntry;
+ ULONG Count;
+
+ UNREFERENCED_PARAMETER(DevObj);
+
+ NT_ASSERT(LinkEntry != NULL);
+
+ while (LinkEntry)
+ {
+ TaskEntry = CONTAINING_RECORD(LinkEntry, TASK_ENTRY, LwQLink);
+ NT_ASSERT(TaskEntry);
+ LinkEntry = LinkEntry->Next;
+
+ //
+ // Process the task.
+ //
+ Count = InterlockedDecrement(& (LONG)TaskEntry->FlowCtx->OobInfo.PendingTasks);
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_REFCOUNT,
+ "FlowCtx %p, ProcessingTask %p: PendingTasks @-- = %lu", TaskEntry->FlowCtx, TaskEntry, Count);
+
+ StreamEditOobProcessTask(TaskEntry);
+
+ //
+ // Free the task Memory.
+ //
+ ExFreeToLookasideListEx(&Globals.LookasideList, TaskEntry);
+ }
+
+ return;
+}
+
+VOID
+OobEditClassify(
+ _In_ const FWPS_INCOMING_VALUES* InFixedValues,
+ _In_ const FWPS_INCOMING_METADATA_VALUES* InMetaValues,
+ _Inout_ VOID* LayerData,
+ _In_ const FWPS_FILTER* Filter,
+ _In_ UINT64 InFlowContext,
+ _Inout_ FWPS_CLASSIFY_OUT* ClassifyOut
+ )
+/*
+ This is the ClassifyFn function registered by the OOB stream edit callout.
+
+ An OOB stream modification callout blocks all indicated data after cloning
+ them for processing by a kernel mode worker thread (or marshalling the data
+ to user mode for inspection); the resultant/edited data will then be put
+ back to the stream via the stream injection API.
+
+*/
+{
+ FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket;
+ FWPS_STREAM_DATA* streamData;
+
+ PTASK_ENTRY TaskEntry = NULL;
+ NTSTATUS Status = STATUS_SUCCESS;
+ KLOCK_QUEUE_HANDLE LockHandle;
+ STREAM_FLOW_CONTEXT* FlowContext = (STREAM_FLOW_CONTEXT *)(ULONG_PTR)InFlowContext;
+
+ ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)LayerData;
+ NT_ASSERT(ioPacket != NULL);
+
+ streamData = ioPacket->streamData;
+ NT_ASSERT(streamData != NULL);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "--> %!FUNC!: FlowCtx %p, sFlags %#x, Length %Iu, LayerId %hu, CalloutId %u, FlowId %I64u, cFlags %#x",
+ FlowContext,
+ streamData->flags,
+ streamData->dataLength,
+ InFixedValues->layerId,
+ Filter->action.calloutId,
+ InMetaValues->flowHandle,
+ ClassifyOut->flags);
+
+ // Set default classify action to Block, and stream action to None!
+ //
+ ClassifyOut->actionType = FWP_ACTION_BLOCK;
+ ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;
+
+ // Setting countBytesEnforced to 0 bytes means that action applies
+ // to entire stream... We'll consume or reject the entire stream data segment
+ //
+ ioPacket->countBytesEnforced = 0;
+
+ //
+ // If the indicated data is insufficient for the callout to make an inspection
+ // decision, it can request for more data. To do so :
+ //
+ // 1. set IoPacket->streamAction to FWPS_STREAM_ACTION_NEED_MORE_DATA
+ // 2. set the countBytesRequired member to the minimal amount WFP should
+ // accumulate before the data is indicated again.
+ // 3. return FWP_ACTION_NONE from the classifyFn function
+ //
+
+ if ((streamData->dataLength < Globals.StringToFindLength) &&
+ !(ClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA))
+ {
+ ioPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA;
+ ioPacket->countBytesRequired = (UINT32)Globals.StringToFindLength;
+
+ ClassifyOut->actionType = FWP_ACTION_NONE;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "<-- %!FUNC!: FlowCtx %p - Need more data.", FlowContext);
+ return;
+ }
+
+ if (FlowContext->bFlowTerminating)
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "Received data classified after FIN for FlowCtx %p.", FlowContext);
+ return;
+ }
+
+ KeAcquireInStackQueuedSpinLock(&FlowContext->OobInfo.EditLock, &LockHandle );
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "FlowCtx %p: PendedDataLength %Iu, StreamDataLength %Iu, State %!OobState!, sFlags %#x",
+ FlowContext,
+ FlowContext->OobInfo.PendedDataLength,
+ streamData->dataLength,
+ FlowContext->OobInfo.EditState,
+ streamData->flags);
+
+ switch (FlowContext->OobInfo.EditState)
+ {
+ case OOB_EDIT_PROCESSING:
+
+ // If we are to pause a flow, make sure it is an inbound flow. Currently,
+ // WFP Stream layer only supports pausing and resuming inbound flows.
+ //
+ // Let's not pause the stream if we receive a DISCONNECT (FIN) packet.
+ // For FIN packets, streamData->dataLength == 0, and streamData->flags has
+ // one of the following flags set:
+ // FWPS_STREAM_FLAG_SEND_DISCONNECT
+ // FWPS_STREAM_FLAG_RECEIVE_DISCONNECT
+ //
+ if (( streamData->flags & FWPS_STREAM_FLAG_RECEIVE) &&
+ streamData->dataLength &&
+ FALSE == FlowContext->bNoMoreData )
+ {
+ // While the stream is paused/deferred, it keeps storing incoming data
+ // for the callouts. If resuming a stream causes to give us more data
+ // than we can handle, then pause the stream again.
+ //
+ if (((FlowContext->OobInfo.PendedDataLength + streamData->dataLength) > Globals.BusyThreshold))
+ {
+ ioPacket->streamAction = FWPS_STREAM_ACTION_DEFER;
+ ClassifyOut->actionType = FWP_ACTION_NONE;
+
+ FlowContext->OobInfo.StreamFlags = streamData->flags;
+ FlowContext->OobInfo.EditState = OOB_EDIT_BUSY;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, Flow is pausing", FlowContext);
+ break;
+ }
+ }
+
+ // If this is an outbound flow OR inbound flow that has not yet hit
+ // threshold limit, keep queueing the data for processing.
+ //
+ // Fall through to IDLE State.
+ //
+
+ case OOB_EDIT_IDLE:
+
+#pragma prefast(push)
+#pragma prefast(disable:6014, "Warning Leaking memory 'TaskEntry' : TaskEntry is freed in StreamEditOobPoolWorker")
+ TaskEntry = (PTASK_ENTRY) ExAllocateFromLookasideListEx(&Globals.LookasideList);
+#pragma prefast(pop)
+
+ if (TaskEntry)
+ {
+ TaskEntry->FlowCtx = FlowContext;
+
+#pragma prefast(push)
+#pragma prefast(disable: 26110, "The lock is being held via KeAcquireInStackQueuedSpinLock")
+ Status = StreamOobQueueUpIncomingData(TaskEntry, streamData, ClassifyOut->flags);
+#pragma prefast(pop)
+
+ if (NT_SUCCESS(Status))
+ {
+ // If the previous state was _PROCESSING, it stays the same...
+ FlowContext->OobInfo.EditState = OOB_EDIT_PROCESSING;
+
+ // Block original data
+ //
+ break;
+ }
+ else
+ {
+ ExFreeToLookasideListEx(&Globals.LookasideList, TaskEntry);
+ TaskEntry = NULL;
+ }
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL,
+ "OC: FlowCtx %p ERROR. Task %p, %!STATUS!", FlowContext, TaskEntry, Status);
+
+ FlowContext->OobInfo.EditState = OOB_EDIT_ERROR;
+
+ //
+ // Fall through for ERROR processing
+ //
+
+ case OOB_EDIT_ERROR:
+ //
+ // An out-of-band inspection module must not arbitrarily inject a FIN
+ // (which indicates no more data from the sender) into an (outgoing) data
+ // stream. If the module must drop the connection, its classifyFn callout
+ // function must set the FWPS_STREAM_CALLOUT_IO_PACKET->streamAction to
+ // FWPS_STREAM_ACTION_DROP_CONNECTION.
+ //
+
+ ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION;
+ break;
+
+ default:
+ NT_ASSERT(FALSE);
+ break;
+ };
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "<-- %!FUNC!: FlowCtx %p, cAction %#x, sAction %#x, #Enforced %Iu, New State %!OobState!",
+ FlowContext, ClassifyOut->actionType, ioPacket->streamAction, ioPacket->countBytesEnforced, FlowContext->OobInfo.EditState);
+
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+ return;
+}
diff --git a/network/trans/stmedit/sys/StreamEdit.c b/network/trans/stmedit/sys/StreamEdit.c
new file mode 100644
index 00000000..9bad88d1
--- /dev/null
+++ b/network/trans/stmedit/sys/StreamEdit.c
@@ -0,0 +1,1633 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved
+
+Abstract:
+ Stream Edit Callout Driver Sample.
+
+ This sample demonstrates finding and replacing a string pattern from a
+ live TCP stream via the WFP stream API.
+
+ The driver demonstrates the two modes of stream editing/inspection --
+
+ o Inline Editing where all modification is carried out within the
+ WFP ClassifyFn callout function.
+
+ o Out-of-band (OOB) Editing where all modification is done by a
+ worker thread. (this is the default)
+
+ The mode setting, along with other inspection parameters are configurable
+ via the following registry values
+
+ HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\StmEdit\Parameters
+
+ o StringToFind (REG_SZ, default = "rainy")
+ o StringX (REG_SZ, default = "cloudy")
+ o StringToReplace (REG_SZ, default = "sunny")
+
+ o InspectionLocalPort (REG_DWORD, default = 8888)
+
+ o InspectionRemotePort (REG_DWORD, default = 0)
+ Note: for this sample, a local or remote port is mandatory. Both cannot be zero.
+
+ o InspectioDirection (REG_DWORD, default = 2)
+ possible values : 2 (inbound + outbound), 0 (FWP_DIRECTION_OUTBOUND), 1 (FWP_DIRECTION_INBOUND)
+
+ o MultipleCallouts (REG_DWORD, default = true/1)
+ controls registration of multiple callouts. Set 0 for false, other for TRUE
+
+ o BusyThreshold (REG_DWORD, default = 16KB)
+ BusyThreshold value is in KBs (e.g. a value of 5 means 5KB)
+
+ The sample is IP version agnostic. It is capable of performing inspections
+ on both IPv4 and IPv6 data streams
+
+ Before experimenting with the sample, please be sure to add an exception for
+ the InspectionPort configured to the firewall.
+
+Environment:
+ Kernel mode
+
+--*/
+
+#include "Trace.h"
+#include "StreamEdit.h"
+#include "StreamEdit.tmh"
+
+STMEDIT_GLOBALS Globals;
+
+DRIVER_INITIALIZE DriverEntry;
+EVT_WDF_DRIVER_UNLOAD StreamEditEvtDriverUnload;
+
+#if defined _MODULE_ID
+#undef _MODULE_ID
+#endif
+#define _MODULE_ID 'S'
+
+VOID
+StmEditReferenceFlow(
+ _Inout_ PSTREAM_FLOW_CONTEXT FlowContext,
+ _In_ char Module,
+ _In_ UINT Line
+ )
+{
+ LONG Count = InterlockedIncrement((LONG *)&FlowContext->RefCount);
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_REFCOUNT, "FlowCtx %p RefCount++ @ %c/%lu = %lu", FlowContext, Module, Line, Count);
+}
+
+VOID
+StmEditDeReferenceFlow(
+ _Inout_ PSTREAM_FLOW_CONTEXT FlowContext,
+ _In_ char Module,
+ _In_ UINT Line
+ )
+{
+ LONG Count;
+ KLOCK_QUEUE_HANDLE LockHandle;
+
+ NT_ASSERT(FlowContext->RefCount > 0);
+
+ Count = InterlockedDecrement((LONG *)&FlowContext->RefCount);
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_REFCOUNT, "FlowCtx %p RefCount-- @ %c/%lu = %lu", FlowContext, Module, Line, Count);
+
+ if (Count == 0)
+ {
+ NT_ASSERT( ! FlowContext->bFlowActive);
+
+ // Remove the context from global context list
+ //
+ KeAcquireInStackQueuedSpinLock(&Globals.FlowContextListLock, &LockHandle);
+
+ if (!FlowContext->bEntryRemoved)
+ {
+ RemoveEntryList(&FlowContext->Link);
+ FlowContext->bEntryRemoved = TRUE;
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, -- Link removed", FlowContext);
+ }
+ Count = --Globals.FlowContextCount;
+
+ if (Globals.FlowContextCount == 0)
+ {
+ NT_ASSERT(IsListEmpty(&Globals.FlowContextList));
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Setting ZeroFlowCountEvent.");
+ KeSetEvent(&Globals.ZeroFlowCountEvent, IO_NO_INCREMENT, FALSE);
+ }
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+
+ if (!FlowContext->bEditInline)
+ {
+ NT_ASSERT(IsListEmpty(&FlowContext->OobInfo.OutgoingDataQueue));
+ }
+
+ if (FlowContext->ScratchBuffer)
+ {
+ ExFreePoolWithTag(FlowContext->ScratchBuffer, STMEDIT_TAG_FLAT_BUFFER);
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p is being freed., %lu remain @--", FlowContext, Count);
+ ExFreePoolWithTag(FlowContext, STMEDIT_TAG_FLOWCTX);
+ }
+}
+
+
+NTSTATUS
+StreamEditNotifyFunction(
+ _In_ FWPS_CALLOUT_NOTIFY_TYPE NotifyType,
+ _In_ const GUID* FilterKey,
+ _In_ const FWPS_FILTER* Filter
+ )
+{
+/*
+ Notify Function.
+*/
+ UNREFERENCED_PARAMETER(FilterKey);
+
+#if 0
+ UNREFERENCED_PARAMETER(notifyType);
+ UNREFERENCED_PARAMETER(filter);
+#else
+ NT_ASSERT(Filter != NULL);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "-><- %!FUNC! invoked with %I64u for Filter ID %I64u",
+ NotifyType, Filter->filterId);
+
+#endif
+ return STATUS_SUCCESS;
+}
+
+void
+NTAPI
+StreamEditInjectCompletionFn(
+ _In_ VOID* Context,
+ _Inout_ NET_BUFFER_LIST* NetBufferList,
+ _In_ BOOLEAN DispatchLevel
+ )
+/*
+ Injection completion function for injecting an NBL created using
+ FwpsAllocateNetBufferAndNetBufferList.
+*/
+{
+ MDL* mdl = (MDL*)Context;
+
+ UNREFERENCED_PARAMETER(DispatchLevel);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "-><- %!FUNC!: NBL %p (%!STATUS!), MDL %p", NetBufferList, NetBufferList->Status, mdl);
+
+ // Supress warning 28922: Redundant test against NULL. Pointer is already guaranteed to be non-NULL.
+ // Rationale : mdl is not guaranteed to be non-NULL here.
+#pragma prefast(push)
+#pragma prefast(disable:28922)
+
+ if (mdl != NULL)
+ {
+ //
+ // The MDL mapped over a pool alloc which we need to free here.
+ //
+
+ ExFreePoolWithTag(mdl->MappedSystemVa, STMEDIT_TAG_MDL_DATA);
+
+ IoFreeMdl(mdl);
+ }
+#pragma prefast(pop)
+
+ NT_ASSERT(NetBufferList != NULL);
+ FwpsFreeNetBufferList(NetBufferList);
+}
+
+NTSTATUS
+StreamEditRemoveFlowCtx(
+ _In_ PSTREAM_FLOW_CONTEXT Context
+ )
+/*
+ Function to disassociate a previously associated context from a data flow.
+ This will cause flowDelete function to be invoked (either synchronously or asynchronously).
+
+ Remarks @ http://msdn.microsoft.com/en-us/library/windows/hardware/ff551169.aspx
+
+ If the FwpsFlowRemoveContext0 function returns STATUS_SUCCESS, FwpsFlowRemoveContext0
+ calls the flowDeleteFn callout function synchronously.If FwpsFlowRemoveContext0 returns
+ STATUS_PENDING, FwpsFlowRemoveContext0 calls flowDeleteFn asynchronously because an
+ active callout classification is in progress.
+*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ //
+ // Possible synchronization problem for accessing bFlowActive...
+ // while we are flushing the data, FlowDeleteFn can get invoked.
+ //
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!: FlowCtx %p", Context);
+
+ NT_ASSERT(Context);
+
+ if (Context->bEditInline)
+ {
+ (VOID) InlineEditFlushData(Context, 0, Context->PartialSFlags);
+ }
+ else {
+ (VOID) StreamOobFlushOutgoingData(Context);
+ }
+
+ if (Context->bFlowActive)
+ {
+
+ Status = FwpsFlowRemoveContext(
+ Context->FlowHandle,
+ Context->LayerId,
+ Context->CalloutId);
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: FlowCtx %p, %!STATUS!", Context, Status);
+ return Status;
+}
+
+VOID
+StreamEditSignalShutdown(
+)
+/*
+ This function attempts to Disassociate all active FlowContexts so that
+ a shutdown can be performed.
+*/
+{
+ KLOCK_QUEUE_HANDLE LockHandle;
+ PSTREAM_FLOW_CONTEXT FlowContext;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!");
+
+ KeAcquireInStackQueuedSpinLock(&Globals.FlowContextListLock, &LockHandle);
+ while ( ! IsListEmpty(&Globals.FlowContextList) )
+ {
+ PLIST_ENTRY Entry = RemoveHeadList(&Globals.FlowContextList);
+
+ FlowContext = CONTAINING_RECORD(Entry, STREAM_FLOW_CONTEXT, Link);
+ FlowContext->bEntryRemoved = TRUE;
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, -- Link removed at shutdown", FlowContext);
+
+ if (FlowContext->bFlowActive)
+ {
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+ (VOID)StreamEditRemoveFlowCtx(FlowContext);
+ KeAcquireInStackQueuedSpinLock(&Globals.FlowContextListLock, &LockHandle);
+ }
+ }
+ KeReleaseInStackQueuedSpinLock(&LockHandle);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!");
+}
+
+VOID
+StreamEditFlowDeleteFunction(
+ _In_ UINT16 LayerId,
+ _In_ UINT32 CalloutId,
+ _In_ UINT64 Context
+ )
+/*
+ This is the flowDeleteFn function. This callback is invoked when a flow is
+ terminated or due to call to FwpsFlowRemoveContext0.
+
+ We removes the FlowContext from the global FlowcCntextList and releases resources.
+
+ IRQL <= DISPATCH_LEVEL
+*/
+{
+ PSTREAM_FLOW_CONTEXT FlowCtx = (PSTREAM_FLOW_CONTEXT)(ULONG_PTR)Context;
+
+ NT_ASSERT(NULL != FlowCtx);
+ NT_ASSERT(TRUE == FlowCtx->bFlowActive);
+
+ InterlockedExchange8(&FlowCtx->bFlowActive, FALSE);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!: FlowCtx %p, LayerId %hu, CalloutId %u, FlowId %I64u (RefCt = %lu)",
+ FlowCtx, LayerId, CalloutId, FlowCtx->FlowHandle, FlowCtx->RefCount );
+
+ // Deref the reference taken in Flow-established when the FlowCtx was allocated
+ //
+ StmEditDeReferenceFlow(FlowCtx, _MODULE_ID, __LINE__);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: FlowCtx %p", FlowCtx);
+}
+
+
+VOID
+NTAPI
+StreamEditCommonStreamClassify(
+ _In_ const FWPS_INCOMING_VALUES* InFixedValues,
+ _In_ const FWPS_INCOMING_METADATA_VALUES* InMetaValues,
+ _In_ PVOID LayerData,
+#if(NTDDI_VERSION >= NTDDI_WIN7)
+ _In_ const VOID* ClassifyContext,
+#endif
+ _In_ const FWPS_FILTER* Filter,
+ _In_ UINT64 InFlowContext,
+ _Inout_ FWPS_CLASSIFY_OUT* ClassifyOut
+ )
+/*
+ Common classifyFn for both Inline and Out-of-band Stream layer callouts.
+ Invokes corresponding classify-function based on FlowContext->bEditInline flag.
+*/
+{
+ PSTREAM_FLOW_CONTEXT FlowContext = (PSTREAM_FLOW_CONTEXT)(ULONG_PTR)InFlowContext;
+
+#if(NTDDI_VERSION >= NTDDI_WIN7)
+ UNREFERENCED_PARAMETER(ClassifyContext);
+#endif
+
+ NT_ASSERT(FlowContext);
+
+ // Reference the flow to keep around while we are in classifyFn
+ //
+ StmEditReferenceFlow(FlowContext, _MODULE_ID, __LINE__);
+
+ if (FlowContext->bEditInline)
+ {
+ InlineEditClassify(
+ InFixedValues,
+ InMetaValues,
+ LayerData,
+ Filter,
+ InFlowContext,
+ ClassifyOut
+ );
+ }
+ else
+ {
+ OobEditClassify (
+ InFixedValues,
+ InMetaValues,
+ LayerData,
+ Filter,
+ InFlowContext,
+ ClassifyOut
+ );
+ }
+
+ StmEditDeReferenceFlow(FlowContext, _MODULE_ID, __LINE__);
+}
+
+VOID
+StreamEditFlowEstablishedClassify(
+ _In_ const FWPS_INCOMING_VALUES* InFixedValues,
+ _In_ const FWPS_INCOMING_METADATA_VALUES* InMetaValues,
+ _In_ PVOID Packet,
+#if(NTDDI_VERSION >= NTDDI_WIN7)
+ _In_ const void* ClassifyContext,
+#endif
+ _In_ const FWPS_FILTER* Filter,
+ _In_ UINT64 InFlowContext,
+ _Inout_ FWPS_CLASSIFY_OUT* ClassifyOut
+)
+/*
+ Flow-established call out for IPV4 and IPV6 traffic.
+ Allocates and sets up a flow-context, and associate it with the flow.
+*/
+{
+ NTSTATUS Status;
+ PSTREAM_FLOW_CONTEXT StreamFlowContext;
+ UINT32 StreamCalloutId;
+ UINT16 StreamLayerId;
+ KLOCK_QUEUE_HANDLE lockHandle;
+ USHORT ipProtIndex;
+
+ int CalloutSet = 0;
+
+#if(NTDDI_VERSION >= NTDDI_WIN7)
+ UNREFERENCED_PARAMETER(ClassifyContext);
+#endif
+ UNREFERENCED_PARAMETER(InFlowContext);
+ UNREFERENCED_PARAMETER(Packet);
+
+
+ if ((Filter->action.calloutId == Globals.FlowEstablishedV4Callout1) ||
+ (Filter->action.calloutId == Globals.FlowEstablishedV6Callout1))
+ {
+ CalloutSet = 1;
+ }
+ else
+ if ((Filter->action.calloutId == Globals.FlowEstablishedV4Callout2) ||
+ (Filter->action.calloutId == Globals.FlowEstablishedV6Callout2))
+ {
+ CalloutSet = 2;
+ NT_ASSERT(TRUE == Globals.MultipleCallouts);
+ }
+ else
+ {
+ NT_ASSERT(FALSE);
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!%d: LayerId %hu, CalloutId %u, FlowId %I64u",
+ CalloutSet, InFixedValues->layerId, Filter->action.calloutId, InMetaValues->flowHandle);
+
+ ClassifyOut->actionType = FWP_ACTION_CONTINUE;
+
+ // Lets not entertain any new flows if the driver is unloading!
+ //
+ if (Globals.DriverUnloading)
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,"<-- %!FUNC!: -- Driver unloading, flow not being associated with");
+ return;
+ }
+
+ //
+ // Setup the flow context for IPV4 Flows
+ //
+ if (FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 == InFixedValues->layerId)
+ {
+ ipProtIndex = FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL;
+ StreamLayerId = FWPS_LAYER_STREAM_V4;
+
+ StreamCalloutId = CalloutSet == 1 ? Globals.StreamLayerV4Callout1 : Globals.StreamLayerV4Callout2;
+ }
+ //
+ // Setup the flow context for IPV6 Flows
+ //
+ else if (FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 == InFixedValues->layerId)
+ {
+ ipProtIndex = FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_PROTOCOL;
+ StreamLayerId = FWPS_LAYER_STREAM_V6;
+
+ StreamCalloutId = CalloutSet == 1 ? Globals.StreamLayerV6Callout1 : Globals.StreamLayerV6Callout2;
+ }
+ else
+ {
+ // We should not be here.
+ //
+ NT_ASSERT(FALSE);
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: -- Invalid layer.");
+ return;
+ }
+
+ //
+ // Creates a flow context and associate it with the current flow
+ // FlowContext gets deleted via flowDeleteFn
+ //
+
+ do
+ {
+ StreamFlowContext = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(STREAM_FLOW_CONTEXT),
+ STMEDIT_TAG_FLOWCTX);
+
+ if (StreamFlowContext == NULL)
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Unable to allocate flow context");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ // Initialize the flow-context
+ //
+ RtlZeroMemory(StreamFlowContext, sizeof(STREAM_FLOW_CONTEXT));
+
+ StreamFlowContext->IpProto = InFixedValues->incomingValue[ipProtIndex].value.uint16;
+ StreamFlowContext->bFlowActive = TRUE;
+
+ StreamFlowContext->FlowHandle = InMetaValues->flowHandle;
+ StreamFlowContext->LayerId = StreamLayerId;
+ StreamFlowContext->CalloutId = StreamCalloutId;
+
+ // Reference to take ownership!
+ StmEditReferenceFlow(StreamFlowContext, _MODULE_ID, __LINE__);
+
+ // Callout Set #1 is for Out of Band editing
+ //
+ if (CalloutSet == 1)
+ {
+ // Initialize OOB editing specific flow context structure fields
+ // this includes, creating a worker thread to handle
+
+ KeInitializeSpinLock(&StreamFlowContext->OobInfo.EditLock);
+ InitializeListHead(&StreamFlowContext->OobInfo.OutgoingDataQueue);
+
+ StreamFlowContext->OobInfo.EditState = OOB_EDIT_IDLE;
+ StreamFlowContext->OobInfo.QueueNumber = InterlockedIncrement((LONG *)&Globals.QueueIndex) % NUM_WORKITEM_QUEUES;
+
+ }
+ // Callout Set #2 is for InLine editing
+ //
+ else
+ {
+ // Initialize inline editing specific flow context structure areas
+ //
+ StreamFlowContext->InlineEditState = INLINE_EDIT_IDLE;
+ StreamFlowContext->bEditInline = TRUE;
+
+ StreamFlowContext->CurrentProcessor = INVALID_PROC_NUMBER;
+ }
+
+ // Add the newly created context on global context list
+ KeAcquireInStackQueuedSpinLock(&Globals.FlowContextListLock, &lockHandle);
+ InsertTailList(&Globals.FlowContextList, &StreamFlowContext->Link);
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, ++ Link inserted into global list", StreamFlowContext);
+
+ ++Globals.FlowContextCount;
+ if (Globals.FlowContextCount == 1)
+ {
+ // Reset the shut-down event in case it was set due to no active flows
+ //
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Clearing ZeroFlowCountEvent.");
+ KeClearEvent(&Globals.ZeroFlowCountEvent);
+ }
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p is allocated., Total %lu @++", StreamFlowContext, Globals.FlowContextCount);
+
+ KeReleaseInStackQueuedSpinLock(&lockHandle);
+
+ Status = FwpsFlowAssociateContext(
+ StreamFlowContext->FlowHandle,
+ StreamLayerId,
+ StreamCalloutId,
+ (UINT64)StreamFlowContext);
+
+ //
+ // If not able to associate a flow context, free the memory and return.
+ //
+ if (!NT_SUCCESS(Status))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "FlowContext association to FlowId %I64u failed with %!STATUS!",
+ InMetaValues->flowHandle, Status);
+ break;
+ }
+
+ } while (FALSE);
+
+ if (!NT_SUCCESS(Status) && StreamFlowContext)
+ {
+ StmEditDeReferenceFlow(StreamFlowContext, _MODULE_ID, __LINE__);
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!%d: FlowCtx %p, cOut->Action %#x, %!STATUS!",
+ CalloutSet, StreamFlowContext, ClassifyOut->actionType, Status);
+ return;
+}
+
+NTSTATUS
+StreamEditRegisterFlowEstablishedCallouts(
+ _In_ PVOID DeviceObject,
+ _In_ const GUID* LayerKey,
+ _In_ const GUID* CalloutKey,
+ _In_ FWPM_DISPLAY_DATA* DisplayData,
+ _Out_ UINT32* CalloutId,
+ _In_ int CalloutNum
+ )
+/*
+ This function registers callouts and filters that intercept TCP
+ traffic at WFP FWPM_LAYER_STREAM_V4 or FWPM_LAYER_STREAM_V6 layer.
+*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ USHORT condIndex = 0;
+ BOOLEAN calloutRegistered = FALSE;
+
+ FWPS_CALLOUT sCallout = { 0 };
+ FWPM_CALLOUT mCallout = { 0 };
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!%d", CalloutNum);
+
+ sCallout.calloutKey = *CalloutKey; // STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V4 / V6;
+ sCallout.notifyFn = StreamEditNotifyFunction;
+
+ sCallout.classifyFn = StreamEditFlowEstablishedClassify;
+
+ Status = FwpsCalloutRegister(DeviceObject, &sCallout, CalloutId);
+
+ if (NT_SUCCESS(Status))
+ {
+ calloutRegistered = TRUE;
+
+ mCallout.calloutKey = *CalloutKey;
+ mCallout.displayData = *DisplayData;
+ mCallout.applicableLayer = *LayerKey; // FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 / V6
+
+ Status = FwpmCalloutAdd(Globals.EngineHandle, &mCallout, NULL, NULL);
+
+ if (NT_SUCCESS(Status))
+ {
+ FWPM_FILTER filter = { 0 };
+ FWPM_FILTER_CONDITION filterConditions[4] = { 0 };
+
+ // Add Filters for StreamEditFlowEstablishedClassify
+ //
+
+ filter.layerKey = *LayerKey;
+ filter.displayData.name = L"Stream Edit Sample Filter";
+ filter.displayData.description = L"Filter that finds and replaces a token from a TCP stream (@ Flow Established)";
+
+ filter.action.type = FWP_ACTION_CALLOUT_INSPECTION; // FWP_ACTION_CALLOUT_TERMINATING;
+ filter.action.calloutKey = *CalloutKey;
+ filter.filterCondition = filterConditions;
+
+ // In this sample, we edit TCP streams only
+ //
+ filterConditions[condIndex].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
+ filterConditions[condIndex].matchType = FWP_MATCH_EQUAL;
+ filterConditions[condIndex].conditionValue.type = FWP_UINT8;
+ filterConditions[condIndex].conditionValue.uint8 = IPPROTO_TCP;
+ filter.numFilterConditions++;
+ condIndex++;
+
+ // Filter according to the direction of the flow we are interested in
+ //
+ // @ http://msdn.microsoft.com/en-us/library/windows/desktop/aa364005.aspx
+ //
+ // For stream layers (FWPM_LAYER_STREAM_*) and flow established layers
+ // ( FWPM_LAYER_ALE_FLOW_ESTABLISHED_* ), the value will be the same as
+ // direction of the connection.
+ //
+ // For example, when a local application initiates the connection, an
+ // inbound packet has FWPM_CONDITION_DIRECTION set to FWP_DIRECTION_OUTBOUND.
+ //
+
+ if (Globals.InspectionDirection != FWP_DIRECTION_MAX)
+ {
+ filterConditions[condIndex].fieldKey = FWPM_CONDITION_DIRECTION;
+ filterConditions[condIndex].matchType = FWP_MATCH_EQUAL;
+ filterConditions[condIndex].conditionValue.type = FWP_UINT32;
+ filterConditions[condIndex].conditionValue.uint32 = Globals.InspectionDirection;
+ filter.numFilterConditions++;
+ condIndex++;
+ }
+
+ // Make sure that either the remote or the local port is specified...
+ // i.e. both ports are not zero
+ //
+
+ if (Globals.InspectionLocalPort > 0)
+ {
+ filterConditions[condIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
+ filterConditions[condIndex].matchType = FWP_MATCH_EQUAL;
+ filterConditions[condIndex].conditionValue.type = FWP_UINT16;
+ filterConditions[condIndex].conditionValue.uint16 = Globals.InspectionLocalPort;
+ filter.numFilterConditions++;
+ condIndex++;
+ }
+
+ if (Globals.InspectionRemotePort > 0)
+ {
+ filterConditions[condIndex].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
+ filterConditions[condIndex].matchType = FWP_MATCH_EQUAL;
+ filterConditions[condIndex].conditionValue.type = FWP_UINT16;
+ filterConditions[condIndex].conditionValue.uint16 = Globals.InspectionRemotePort;
+ filter.numFilterConditions++;
+ }
+
+ filter.subLayerKey = CalloutNum == 1 ? STREAM_EDITOR_SUBLAYER_1 : STREAM_EDITOR_SUBLAYER_2;
+ filter.weight.type = FWP_EMPTY;
+
+ Status = FwpmFilterAdd(Globals.EngineHandle, &filter, NULL, NULL);
+
+ } //FwpmCalloutAdd
+ } //FwpsCalloutRegister
+
+ if (!NT_SUCCESS(Status))
+ {
+ if (calloutRegistered)
+ {
+ FwpsCalloutUnregisterById(*CalloutId);
+ }
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!, %!STATUS!", Status);
+ return Status;
+}
+
+NTSTATUS
+StreamEditRegisterStreamLayerCallouts(
+ _In_ PVOID DeviceObject,
+ _In_ const GUID* LayerKey,
+ _In_ const GUID* CalloutKey,
+ _In_ FWPM_DISPLAY_DATA* DisplayData,
+ _Out_ UINT32* CalloutId,
+ _In_ const int CalloutNum
+ )
+/*
+ This function registers callouts that intercept TCP traffic
+ at WFP FWPM_LAYER_STREAM_V4 or FWPM_LAYER_STREAM_V6 layer.
+*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ FWPS_CALLOUT sCallout = {0};
+ FWPM_CALLOUT mCallout = {0};
+ BOOLEAN calloutRegistered = FALSE;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!%d", CalloutNum);
+
+ sCallout.calloutKey = *CalloutKey;
+ sCallout.classifyFn = StreamEditCommonStreamClassify;
+ sCallout.notifyFn = StreamEditNotifyFunction;
+ sCallout.flowDeleteFn = StreamEditFlowDeleteFunction;
+
+ // http://msdn.microsoft.com/en-us/library/windows/hardware/ff551224.aspx
+ //
+ // FWPS_CALLOUT0 structure
+ //
+ // FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW
+ // If this flag is specified, the filter engine calls the callout driver's
+ // classifyFn callout function only if there is a context associated with
+ // the data flow.
+ //
+
+ sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
+
+ Status = FwpsCalloutRegister(
+ DeviceObject,
+ &sCallout,
+ CalloutId );
+
+ if (NT_SUCCESS(Status))
+ {
+ calloutRegistered = TRUE;
+
+ mCallout.calloutKey = *CalloutKey;
+ mCallout.displayData = *DisplayData;
+ mCallout.applicableLayer = *LayerKey; // FWPM_LAYER_STREAM_V4 / V6
+
+ Status = FwpmCalloutAdd(
+ Globals.EngineHandle,
+ &mCallout,
+ NULL,
+ NULL );
+
+ if (NT_SUCCESS(Status))
+ {
+ //
+ // Add Filters for Stream Classify
+ //
+ // Note : we are adding a filter with no filter conditions -- i.e. this classifyFn callout
+ // will be classified for ALL streams/flows.
+ // However, due to FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW set above,
+ // the classifyFn will be classified only for flows that have a context associated.
+ //
+
+ FWPM_FILTER filter = { 0 };
+
+ filter.layerKey = *LayerKey;
+ filter.displayData.name = L"Stream Edit Sample Filter";
+ filter.displayData.description = L"Filter that finds and replaces a token from a TCP stream (@ Stream Layer)";
+
+ filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
+ filter.action.calloutKey = *CalloutKey;
+ filter.numFilterConditions = 0;
+ filter.filterCondition = 0;
+
+ filter.subLayerKey = CalloutNum == 1 ? STREAM_EDITOR_SUBLAYER_1 : STREAM_EDITOR_SUBLAYER_2;
+ filter.weight.type = FWP_EMPTY; // auto-weight
+
+ Status = FwpmFilterAdd(Globals.EngineHandle, &filter, NULL, NULL);
+ }
+ }
+
+ if (!NT_SUCCESS(Status))
+ {
+ if (calloutRegistered)
+ {
+ FwpsCalloutUnregisterById(*CalloutId);
+ }
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!, %!STATUS!", Status);
+ return Status;
+}
+
+
+NTSTATUS
+StreamEditRegisterCallouts(
+_In_ PVOID DeviceObject
+ )
+/*
+ This function registers dynamic callouts and filters that intercept
+ TCP traffic at WFP FWPM_LAYER_STREAM_V4 and FWPM_LAYER_STREAM_V6
+ layer.
+
+ Callouts and filters will be removed during DriverUnload.
+*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ BOOLEAN EngineOpened = FALSE;
+ BOOLEAN InTransaction = FALSE;
+
+ FWPM_SESSION session = {0};
+
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,"--> %!FUNC!");
+
+ session.flags = FWPM_SESSION_FLAG_DYNAMIC;
+
+ Status = FwpmEngineOpen(
+ NULL,
+ RPC_C_AUTHN_WINNT,
+ NULL,
+ &session,
+ &Globals.EngineHandle
+ );
+
+ if (NT_SUCCESS(Status))
+ {
+ EngineOpened = TRUE;
+
+ Status = FwpmTransactionBegin(Globals.EngineHandle, 0);
+ if (NT_SUCCESS(Status))
+ {
+ FWPM_SUBLAYER0 StreamEditSubLayer = { 0 };
+ FWPM_DISPLAY_DATA DisplayData;
+ NTSTATUS StatusV6;
+
+ InTransaction = TRUE;
+
+ // Add SubLayer for Callout Set 1 (OoB V4/V6 callouts)
+ // Register the first set of callouts at Flow-established V4 and V6 layers
+ //
+ StreamEditSubLayer.subLayerKey = STREAM_EDITOR_SUBLAYER_1;
+ StreamEditSubLayer.displayData.name = L"Stream Edit Sample Sub-Layer 1";
+ StreamEditSubLayer.displayData.description = L"Sub-Layer for use by Stream Edit Sample callouts";
+ StreamEditSubLayer.weight = 0x40;
+
+ Status = FwpmSubLayerAdd(Globals.EngineHandle, &StreamEditSubLayer, NULL);
+
+ if (NT_SUCCESS(Status))
+ {
+ DisplayData.name = L"Stream Editor Sample ALE Flow Established V4 Callout #1";
+ DisplayData.description = L"Flow Established V4 Callout to associate flow-contexts with flows";
+
+ Status = StreamEditRegisterFlowEstablishedCallouts(
+ DeviceObject,
+ &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4,
+ &STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V4,
+ &DisplayData,
+ &Globals.FlowEstablishedV4Callout1,
+ 1);
+
+ DisplayData.name = L"Stream Editor Sample ALE Flow Established V6 Callout #1";
+ DisplayData.description = L"Flow Established V6 Callout to associate flow-contexts with flows";
+
+ StatusV6 = StreamEditRegisterFlowEstablishedCallouts(
+ DeviceObject,
+ &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6,
+ &STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V6,
+ &DisplayData,
+ &Globals.FlowEstablishedV6Callout1,
+ 1);
+
+ if (NT_SUCCESS(Status) || NT_SUCCESS(StatusV6))
+ {
+ DisplayData.name = L"Stream Editor Sample Stream Layer V4 Callout #1";
+ DisplayData.description = L"Stream-Layer V4 Callout finds and replaces token(s) from a TCP stream";
+
+ Status = StreamEditRegisterStreamLayerCallouts(
+ DeviceObject,
+ &FWPM_LAYER_STREAM_V4,
+ &STREAM_EDITOR_STREAM_CALLOUT_V4,
+ &DisplayData,
+ &Globals.StreamLayerV4Callout1,
+ 1);
+
+ DisplayData.name = L"Stream Editor Sample Stream Layer V6 Callout #1";
+ DisplayData.description = L"Stream-Layer V6 Callout finds and replaces token(s) from a TCP stream";
+
+ StatusV6 = StreamEditRegisterStreamLayerCallouts(
+ DeviceObject,
+ &FWPM_LAYER_STREAM_V6,
+ &STREAM_EDITOR_STREAM_CALLOUT_V6,
+ &DisplayData,
+ &Globals.StreamLayerV6Callout1,
+ 1);
+
+ if (!(NT_SUCCESS(Status) || NT_SUCCESS(StatusV6)))
+ {
+ NT_ASSERT(FALSE);
+ }
+ } // RegisterStreamLayerCallouts
+ }//FwpmSubLayerAdd
+
+
+
+
+ if (Globals.MultipleCallouts)
+ {
+ // Add SubLayer for Callout Set 2 (OoB V4/V6 callouts)
+ // Register the second set of callouts at Flow-established V4 and V6 layers
+ //
+
+ StreamEditSubLayer.subLayerKey = STREAM_EDITOR_SUBLAYER_2;
+ StreamEditSubLayer.displayData.name = L"Stream Edit Sample Sub-Layer 2";
+ StreamEditSubLayer.displayData.description = L"Sub-Layer for use by Stream Edit Sample callouts";
+ StreamEditSubLayer.flags = 0;
+ StreamEditSubLayer.weight = 0x20;
+
+ Status = FwpmSubLayerAdd(Globals.EngineHandle, &StreamEditSubLayer, NULL);
+
+ if (NT_SUCCESS(Status))
+ {
+ // Register second set of callouts at Flow-established V4 and V6 layers
+ //
+ DisplayData.name = L"Stream Editor Sample ALE Flow Established V4 Callout #2";
+ DisplayData.description = L"Flow Established V4 Callout to associate flow-contexts with flows";
+
+ Status = StreamEditRegisterFlowEstablishedCallouts(
+ DeviceObject,
+ &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4,
+ &STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V4_2,
+ &DisplayData,
+ &Globals.FlowEstablishedV4Callout2,
+ 2);
+
+ DisplayData.name = L"Stream Editor Sample ALE Flow Established V6 Callout #2";
+ DisplayData.description = L"Flow Established V6 Callout to associate flow-contexts with flows";
+
+ StatusV6 = StreamEditRegisterFlowEstablishedCallouts(
+ DeviceObject,
+ &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6,
+ &STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V6_2,
+ &DisplayData,
+ &Globals.FlowEstablishedV6Callout2,
+ 2);
+
+ if (NT_SUCCESS(Status) || NT_SUCCESS(StatusV6))
+ {
+ DisplayData.name = L"Stream Editor Sample Stream Layer V4 Callout #2";
+ DisplayData.description = L"Stream-Layer V4 Callout finds and replaces token(s) from a TCP stream";
+
+ Status = StreamEditRegisterStreamLayerCallouts(
+ DeviceObject,
+ &FWPM_LAYER_STREAM_V4,
+ &STREAM_EDITOR_STREAM_CALLOUT_V4_2,
+ &DisplayData,
+ &Globals.StreamLayerV4Callout2,
+ 2);
+
+ DisplayData.name = L"Stream Editor Sample Stream Layer V6 Callout #2";
+ DisplayData.description = L"Stream-Layer V6 Callout finds and replaces token(s) from a TCP stream";
+
+ StatusV6 = StreamEditRegisterStreamLayerCallouts(
+ DeviceObject,
+ &FWPM_LAYER_STREAM_V6,
+ &STREAM_EDITOR_STREAM_CALLOUT_V6_2,
+ &DisplayData,
+ &Globals.StreamLayerV6Callout2,
+ 2);
+
+ if (!(NT_SUCCESS(Status) || NT_SUCCESS(StatusV6)))
+ {
+ NT_ASSERT(FALSE);
+ }
+ }
+ }
+ } // MultiCallout.
+
+ Status = FwpmTransactionCommit(Globals.EngineHandle);
+
+ if (NT_SUCCESS(Status))
+ {
+ InTransaction = FALSE;
+ }
+ } // FwpmTransactionBegin
+ } // FwpmEngineOpen
+
+ if (!NT_SUCCESS(Status))
+ {
+ if (InTransaction)
+ {
+ NTSTATUS AbortStatus;
+ AbortStatus = FwpmTransactionAbort(Globals.EngineHandle);
+ _Analysis_assume_(NT_SUCCESS(AbortStatus));
+ }
+
+ if (EngineOpened)
+ {
+ FwpmEngineClose(Globals.EngineHandle);
+ Globals.EngineHandle = NULL;
+ }
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!, %!STATUS!", Status);
+ return Status;
+}
+
+VOID
+StreamEditUnregisterCallout(VOID)
+{
+ // Unregister the callouts for Callout #1
+ //
+ FwpsCalloutUnregisterById(Globals.FlowEstablishedV4Callout1);
+ FwpsCalloutUnregisterById(Globals.StreamLayerV4Callout1);
+
+ FwpsCalloutUnregisterById(Globals.FlowEstablishedV6Callout1);
+ FwpsCalloutUnregisterById(Globals.StreamLayerV6Callout1);
+
+ FwpmSubLayerDeleteByKey(Globals.EngineHandle, &STREAM_EDITOR_SUBLAYER_1);
+
+ // Unregister the callouts for Callout #2
+ //
+ if (Globals.MultipleCallouts)
+ {
+ FwpsCalloutUnregisterById(Globals.FlowEstablishedV4Callout2);
+ FwpsCalloutUnregisterById(Globals.StreamLayerV4Callout2);
+
+ FwpsCalloutUnregisterById(Globals.FlowEstablishedV6Callout2);
+ FwpsCalloutUnregisterById(Globals.StreamLayerV6Callout2);
+
+ FwpmSubLayerDeleteByKey(Globals.EngineHandle, &STREAM_EDITOR_SUBLAYER_2);
+ }
+
+ NT_ASSERT(Globals.EngineHandle != NULL);
+ FwpmEngineClose(Globals.EngineHandle);
+ Globals.EngineHandle = NULL;
+}
+
+_Function_class_(EVT_WDF_DRIVER_UNLOAD)
+_IRQL_requires_same_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+StreamEditEvtDriverUnload(
+ _In_ WDFDRIVER DriverObject
+ )
+{
+ ULONG nCount;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!: (DrvObj %p)", DriverObject);
+ InterlockedExchange8( &Globals.DriverUnloading, TRUE);
+
+ StreamEditSignalShutdown();
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "DriverUnload -- Waiting for all the flows to terminate");
+ KeWaitForSingleObject(&Globals.ZeroFlowCountEvent, Executive, KernelMode, FALSE, NULL);
+
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "DriverUnload -- Calling FwpsInjectionHandleDestroy");
+ // FwpsInjectionHandleDestroy will _not_ return to the
+ // caller until all pending injections are completed.
+ //
+ if (Globals.InjectionHandle != NULL)
+ FwpsInjectionHandleDestroy(Globals.InjectionHandle);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "DriverUnload -- Now, uninitializing LW Queues");
+ for (nCount = 0; nCount < NUM_WORKITEM_QUEUES; ++nCount)
+ {
+ LwUninitializeQueue(&Globals.ProcessingQueues[nCount]);
+ }
+
+ if (Globals.LookasideCreated)
+ ExDeleteLookasideListEx(&Globals.LookasideList);
+
+ if (Globals.EngineHandle != NULL)
+ StreamEditUnregisterCallout();
+
+ if (Globals.NetBufferListPool != NULL)
+ NdisFreeNetBufferListPool(Globals.NetBufferListPool);
+
+ if (Globals.NdisGenericObj != NULL)
+ NdisFreeGenericObject(Globals.NdisGenericObj);
+
+ if (Globals.StringToReplaceMdl != NULL)
+ IoFreeMdl(Globals.StringToReplaceMdl);
+
+ if (Globals.StringXMdl != NULL)
+ IoFreeMdl(Globals.StringXMdl);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!");
+ WPP_CLEANUP(DriverObject);
+}
+
+VOID
+StreamEditInitConfig(
+ _In_ const WDFDRIVER driver
+ )
+/*
+ This function loads the default StreamEditor configuration values,
+ then overrides any values specified in the registry.
+*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ DECLARE_CONST_UNICODE_STRING(stringToFindKey, L"StringToFind");
+ DECLARE_CONST_UNICODE_STRING(stringInMiddleKey, L"StringX");
+ DECLARE_CONST_UNICODE_STRING(stringToReplaceKey, L"StringToReplace");
+ DECLARE_CONST_UNICODE_STRING(inspectionLocalPortKey, L"InspectionLocalPort");
+ DECLARE_CONST_UNICODE_STRING(inspectionRemotePortKey, L"InspectionRemotePort");
+ DECLARE_CONST_UNICODE_STRING(multiCalloutKey, L"MultipleCallouts");
+ DECLARE_CONST_UNICODE_STRING(inspectionDirectionKey, L"InspectionDirection");
+ DECLARE_CONST_UNICODE_STRING(thresholdKey, L"BusyThreshold");
+
+ UNICODE_STRING stringValue;
+ WCHAR buffer[STR_MAX_SIZE];
+ USHORT requiredSize;
+ ULONG valueSize;
+ ULONG ulongValue;
+ WDFKEY hKey;
+
+ // Initialize with default values.
+ // String lengths will be initialized later (below).
+ //
+ Globals.InspectionLocalPort = CFG_LOCAL_PORT;
+ Globals.InspectionRemotePort = 0;
+ Globals.InspectionDirection = FWP_DIRECTION_MAX; // Inbound + outbound
+ Globals.BusyThreshold = 0x4000; // == 16K;
+ Globals.MultipleCallouts = TRUE;
+
+ Globals.StringToFind[0] = Globals.StringX[0] = Globals.StringToReplace[0] = '\0';
+
+ Status = WdfDriverOpenParametersRegistryKey(driver, KEY_READ, WDF_NO_OBJECT_ATTRIBUTES, &hKey);
+ if (NT_SUCCESS(Status))
+ {
+
+ if (NT_SUCCESS(WdfRegistryQueryULong(hKey, &inspectionLocalPortKey, &ulongValue)))
+ {
+ Globals.InspectionLocalPort = (USHORT)ulongValue;
+ }
+
+ if (NT_SUCCESS(WdfRegistryQueryULong(hKey, &inspectionRemotePortKey, &ulongValue)))
+ {
+ Globals.InspectionRemotePort = (USHORT)ulongValue;
+ }
+
+ if (NT_SUCCESS(WdfRegistryQueryULong(hKey, &inspectionDirectionKey, &ulongValue)))
+ {
+ Globals.InspectionDirection = (UCHAR)ulongValue;
+ NT_ASSERT((Globals.InspectionDirection >= 0 && Globals.InspectionDirection <= FWP_DIRECTION_MAX));
+
+ if (Globals.InspectionDirection > FWP_DIRECTION_MAX)
+ Globals.InspectionDirection = FWP_DIRECTION_MAX;
+ }
+
+ if (NT_SUCCESS(WdfRegistryQueryULong(hKey, &multiCalloutKey, &ulongValue)))
+ {
+ Globals.MultipleCallouts = !(ulongValue == 0);
+ }
+
+
+ // Attempt to read StringToFind value from registry
+ //
+ stringValue.Buffer = buffer;
+ stringValue.Length = 0;
+ stringValue.MaximumLength = sizeof(buffer);
+
+ Status = WdfRegistryQueryUnicodeString(hKey, &stringToFindKey, &requiredSize, &stringValue);
+ if (NT_SUCCESS(Status))
+ {
+ // stringValue is NULL terminated.
+
+ // Translate Unicode string
+ Status = RtlUnicodeToMultiByteN(
+ Globals.StringToFind,
+ sizeof(Globals.StringToFind),
+ &valueSize,
+ stringValue.Buffer,
+ (ULONG)requiredSize);
+
+ if (NT_SUCCESS(Status))
+ {
+ valueSize -= sizeof(char);
+ Globals.StringToFindLength = valueSize;
+ NT_ASSERT(Globals.StringToFind[valueSize] == '\0');
+ }
+ }
+
+ stringValue.MaximumLength = sizeof(buffer);
+ //Attempt to read StringX value from registry
+ //
+ Status = WdfRegistryQueryUnicodeString(hKey, &stringInMiddleKey, &requiredSize, &stringValue);
+ if (NT_SUCCESS(Status))
+ {
+ // Translate Unicode string
+ Status = RtlUnicodeToMultiByteN(
+ Globals.StringX,
+ sizeof(Globals.StringX),
+ &valueSize,
+ stringValue.Buffer,
+ (ULONG)requiredSize);
+
+ if (NT_SUCCESS(Status))
+ {
+ valueSize -= sizeof(char); // NULL terminator.
+ Globals.StringXLength = valueSize;
+ NT_ASSERT(Globals.StringX[valueSize] == '\0');
+ }
+ }
+
+ stringValue.MaximumLength = sizeof(buffer);
+ // Attempt to read StringToReplace value from registry
+ //
+ Status = WdfRegistryQueryUnicodeString(hKey, &stringToReplaceKey, &requiredSize, &stringValue);
+ if (NT_SUCCESS(Status))
+ {
+ // Translate Unicode string
+ Status = RtlUnicodeToMultiByteN(
+ Globals.StringToReplace,
+ sizeof(Globals.StringToReplace),
+ &valueSize,
+ stringValue.Buffer,
+ (ULONG)requiredSize);
+
+ if (NT_SUCCESS(Status))
+ {
+ valueSize -= sizeof(char);
+ Globals.StringToReplaceLength = valueSize;
+ NT_ASSERT(Globals.StringToReplace[valueSize] == '\0');
+ }
+ }
+
+ if (NT_SUCCESS(WdfRegistryQueryULong(hKey, &thresholdKey, &ulongValue)))
+ {
+ Globals.BusyThreshold = (size_t)ulongValue << 10;
+ NT_ASSERT(Globals.BusyThreshold != 0);
+ }
+
+ WdfRegistryClose(hKey);
+ }
+ else
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "WdfDriverOpenParametersRegistryKey failed with %!STATUS!", Status);
+ }
+
+ // Calculate the length of tokens to be found/replaced.
+ //
+ if (Globals.StringToFindLength == 0)
+ {
+ NT_ASSERT(Globals.StringToFind[0] == 0);
+
+ RtlStringCchCopyA(Globals.StringToFind, STR_MAX_SIZE, "rainy");
+ Status = RtlStringCchLengthA(Globals.StringToFind, STR_MAX_SIZE, &Globals.StringToFindLength);
+
+ //Handle Error.
+ NT_ASSERT(NT_SUCCESS(Status));
+ }
+
+ if (Globals.StringXLength == 0)
+ {
+ NT_ASSERT(Globals.StringX[0] == 0);
+
+ RtlStringCchCopyA(Globals.StringX, STR_MAX_SIZE, "cloudy");
+ Status = RtlStringCchLengthA(Globals.StringX, STR_MAX_SIZE, &Globals.StringXLength);
+
+ //Handle Error.
+ NT_ASSERT(NT_SUCCESS(Status));
+ }
+
+ if (Globals.StringToReplaceLength == 0)
+ {
+ NT_ASSERT(Globals.StringToReplace[0] == 0);
+
+ RtlStringCchCopyA(Globals.StringToReplace, STR_MAX_SIZE, "sunny");
+ Status = RtlStringCchLengthA(Globals.StringToReplace, STR_MAX_SIZE, &Globals.StringToReplaceLength);
+
+ //Handle Error.
+ NT_ASSERT(NT_SUCCESS(Status));
+ }
+
+ NT_ASSERT(Globals.StringToFindLength != 0);
+ NT_ASSERT(Globals.StringXLength != 0);
+ NT_ASSERT(Globals.StringToReplaceLength != 0);
+
+ // In this sample, we want to make sure that at least one port (either local or remote) is non-zero.
+ //
+ if ((Globals.InspectionLocalPort == 0) && (Globals.InspectionRemotePort == 0))
+ {
+ Globals.InspectionLocalPort = CFG_LOCAL_PORT;
+ }
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,"StreamEdit Configuration\r"
+ "\tStringToFind: %s\r\tStringToReplace: %s\r\tStringX: %s\r"
+ "\tInspectionLocalPort: %hu\r\tInspectionRemotePort: %hu\r"
+ "\tInspectionDirection: %!FWP_DIRECTION!\r\tBusyThreshold: 0x%IX\r\tMultiple Callouts: %!bool!",
+ Globals.StringToFind,
+ Globals.StringToReplace,
+ Globals.StringX,
+ Globals.InspectionLocalPort,
+ Globals.InspectionRemotePort,
+ Globals.InspectionDirection,
+ Globals.BusyThreshold,
+ Globals.MultipleCallouts );
+
+}
+
+NTSTATUS
+StreamEditInitDriverObjects(
+ _Inout_ DRIVER_OBJECT* driverObject,
+ _In_ const UNICODE_STRING* registryPath,
+ _Out_ WDFDRIVER* pDriver,
+ _Out_ WDFDEVICE* pDevice
+ )
+{
+ NTSTATUS Status;
+ WDF_DRIVER_CONFIG config;
+ PWDFDEVICE_INIT pInit = NULL;
+
+ WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
+
+ config.DriverInitFlags |= WdfDriverInitNonPnpDriver;
+ config.EvtDriverUnload = StreamEditEvtDriverUnload;
+
+ Status = WdfDriverCreate(
+ driverObject,
+ registryPath,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &config,
+ pDriver
+ );
+
+ if (NT_SUCCESS(Status))
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ pInit = WdfControlDeviceInitAllocate(*pDriver, &SDDL_DEVOBJ_KERNEL_ONLY);
+
+ if (pInit)
+ {
+ WdfDeviceInitSetCharacteristics(pInit, FILE_AUTOGENERATED_DEVICE_NAME, TRUE);
+ //WdfDeviceInitSetDeviceType(pInit, FILE_DEVICE_NETWORK);
+ WdfDeviceInitSetDeviceClass(pInit, &WFP_DRIVER_CLASS_GUID);
+ WdfDeviceInitSetCharacteristics(pInit, FILE_DEVICE_SECURE_OPEN, TRUE);
+
+ Status = WdfDeviceCreate(&pInit, WDF_NO_OBJECT_ATTRIBUTES, pDevice);
+
+ if (NT_SUCCESS(Status))
+ {
+ WdfControlFinishInitializing(*pDevice);
+ }
+ }
+ }
+
+ if (!NT_SUCCESS(Status))
+ {
+ if ( pInit)
+ WdfDeviceInitFree(pInit);
+ }
+
+ return Status;
+}
+
+NTSTATUS
+DriverEntry(
+ _In_ DRIVER_OBJECT* DriverObject,
+ _In_ UNICODE_STRING* RegistryPath
+ )
+{
+ NTSTATUS Status;
+ WDFDEVICE WdfDevice;
+ WDFDRIVER WdfDriver;
+ NET_BUFFER_LIST_POOL_PARAMETERS nblPoolParams = {0};
+
+ WPP_INIT_TRACING(DriverObject, RegistryPath);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,"--> %!FUNC!: DrvObj %p, Regpath %wZ", DriverObject, RegistryPath);
+
+ do {
+ //
+ // Initialize globals and Configuration structures.
+ //
+
+ RtlZeroMemory(&Globals, sizeof(Globals));
+ Globals.QueueIndex = (ULONG)-1;
+
+ InitializeListHead(&Globals.FlowContextList);
+ KeInitializeSpinLock(&Globals.FlowContextListLock);
+
+ // Initialize DriverUnload/Shutdown Event (to a signalled state)
+ //
+ KeInitializeEvent(&Globals.ZeroFlowCountEvent, NotificationEvent, TRUE);
+
+ Status = StreamEditInitDriverObjects(
+ DriverObject,
+ RegistryPath,
+ &WdfDriver,
+ &WdfDevice);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "StreamEditInitDriverObjects failed with 0x%X", Status);
+ break;
+ }
+
+ Globals.WdmDevice = WdfDeviceWdmGetDeviceObject(WdfDevice);
+
+ // Initialize and read driver configuration overrides.
+ //
+ StreamEditInitConfig(WdfDriver);
+
+ Globals.StringToReplaceMdl = IoAllocateMdl(
+ Globals.StringToReplace,
+ (ULONG)Globals.StringToReplaceLength,
+ FALSE,
+ FALSE,
+ NULL);
+
+ if (Globals.StringToReplaceMdl == NULL)
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Unable to allocate StringToReplace Mdl");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ MmBuildMdlForNonPagedPool(Globals.StringToReplaceMdl);
+
+ Globals.StringXMdl = IoAllocateMdl(
+ Globals.StringX,
+ (ULONG)Globals.StringXLength,
+ FALSE,
+ FALSE,
+ NULL);
+
+ if (Globals.StringXMdl == NULL)
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Unable to allocate Mdl#2");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+ MmBuildMdlForNonPagedPool(Globals.StringXMdl);
+
+ Globals.NdisGenericObj = NdisAllocateGenericObject(DriverObject, STMEDIT_TAG_NDIS_OBJ, 0);
+ if (Globals.NdisGenericObj == NULL)
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "NdisAllocateGenericObject failed.");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ //
+ // Allocate a NDIS/NBL Pool
+
+ nblPoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
+ nblPoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
+ nblPoolParams.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
+ nblPoolParams.fAllocateNetBuffer = TRUE;
+ nblPoolParams.DataSize = 0;
+ nblPoolParams.PoolTag = STMEDIT_TAG_NBL_POOL;
+
+ Globals.NetBufferListPool = NdisAllocateNetBufferListPool(
+ Globals.NdisGenericObj,
+ &nblPoolParams);
+
+ if (Globals.NetBufferListPool == NULL)
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "NdisAllocateNetBufferListPool failed.");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ Status = ExInitializeLookasideListEx(
+ &Globals.LookasideList,
+ NULL,
+ NULL,
+ NonPagedPool, // POOL_NX_OPTIN_AUTO ==> NonPagedPool := NonPagedPoolNx
+ 0,
+ max(sizeof(TASK_ENTRY), sizeof(OUTGOING_STREAM_DATA)),
+ STMEDIT_TAG_TASK_ENTRY,
+ 0);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Task LookasideList Creation failed with %!STATUS!", Status);
+ break;
+ }
+
+ Globals.LookasideCreated = TRUE;
+
+ Status = StreamEditInitializeWorkitemPool();
+
+ if (!NT_SUCCESS(Status))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "InitializeWorkerPool failed with %!STATUS!", Status);
+ break;
+ }
+
+ // Create WFP Injection handle
+ //
+ Status = FwpsInjectionHandleCreate(AF_UNSPEC, FWPS_INJECTION_TYPE_STREAM, &Globals.InjectionHandle);
+ if (!NT_SUCCESS(Status))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FwpsInjectionHandleCreate failed with %!STATUS!", Status);
+ break;
+ }
+
+ //
+ // Finally, register the sublayer(s) and callouts with WFP
+ //
+ Status = StreamEditRegisterCallouts(Globals.WdmDevice);
+ if (!NT_SUCCESS(Status))
+ {
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "StreamEditRegisterCallouts failed with %!STATUS!", Status);
+ break;
+ }
+
+ } while (FALSE);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!, %!STATUS!", Status);
+
+ if (!NT_SUCCESS(Status))
+ {
+ StreamEditEvtDriverUnload(WdfDriver);
+ }
+
+ return Status;
+}
+
+BOOLEAN
+StreamEditCopyDataForInspection(
+_In_ STREAM_FLOW_CONTEXT *FlowContext,
+_In_ const FWPS_STREAM_DATA* StreamData,
+_In_ SIZE_T BytesToCopy
+)
+/*
+ This function copies stream data described by the FWPS_STREAM_DATA
+ structure into a flat buffer.
+
+ Return : TRUE if able to copy stream-data to a flat buffer successfully, FALSE otherwise.
+
+*/
+{
+ SIZE_T BytesCopied;
+ size_t ExistingDataLength = FlowContext->ScratchDataLength;
+
+ NT_ASSERT(BytesToCopy > 0);
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "--> %!FUNC!: FlowCtx %p, streamData %p, copy %Iu of %Iu, old ScratchLength %Iu",
+ FlowContext,
+ StreamData,
+ BytesToCopy,
+ StreamData->dataLength,
+ ExistingDataLength);
+
+ NT_ASSERT(FlowContext->ScratchDataOffset == 0);
+
+ // If the existing scratch buffer is not sufficient to accommodate the new data
+ // try to allocate a bigger buffer (so that we don't have to keep allocating
+ // these for some time.
+ //
+ if (FlowContext->ScratchBufferSize - ExistingDataLength < BytesToCopy)
+ {
+ size_t NewBufferSize = BytesToCopy + ExistingDataLength;
+
+ PVOID NewBuffer = ExAllocatePoolWithTag(
+ NonPagedPool,
+ (NewBufferSize + (NewBufferSize >> 1) ), // 1.5 times the needed size.
+ STMEDIT_TAG_FLAT_BUFFER);
+
+ if (NewBuffer == NULL)
+ {
+
+ // We are not able to allocate a much bigger buffer ... lets try an exact fit.
+ //
+ NewBuffer = ExAllocatePoolWithTag(NonPagedPool, NewBufferSize, STMEDIT_TAG_FLAT_BUFFER);
+ }
+
+ if (NewBuffer != NULL)
+ {
+
+ // Move the existing contents of scratch buffer over to newly allocated buffer
+ //
+ if (ExistingDataLength > 0)
+ {
+
+ NT_ASSERT(FlowContext->ScratchBuffer != NULL);
+ RtlCopyMemory(NewBuffer, FlowContext->ScratchBuffer, ExistingDataLength);
+ }
+ }
+
+ // Free the old scratch buffer...
+ //
+ if (FlowContext->ScratchBuffer)
+ {
+
+ ExFreePoolWithTag(FlowContext->ScratchBuffer, STMEDIT_TAG_FLAT_BUFFER);
+
+ FlowContext->ScratchBuffer = NULL;
+ FlowContext->ScratchBufferSize = 0;
+ FlowContext->ScratchDataLength = 0;
+ }
+
+ if (NewBuffer)
+ {
+
+ FlowContext->ScratchBuffer = NewBuffer;
+ FlowContext->ScratchBufferSize = NewBufferSize;
+ FlowContext->ScratchDataLength = ExistingDataLength;
+ }
+ else
+ {
+
+ DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_ENTER_EXIT,
+ "<-- %!FUNC!: FlowCtx %p, Failed to allocate flat buffer for NBL %p",
+ FlowContext, StreamData->netBufferListChain);
+ return FALSE;
+ }
+ }
+
+ // Append the NBL chain data on to (any) existing data in scratch buffer
+ //
+ FwpsCopyStreamDataToBuffer(
+ StreamData,
+ (BYTE*)FlowContext->ScratchBuffer + FlowContext->ScratchDataLength,
+ BytesToCopy,
+ &BytesCopied
+ );
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,
+ "FlowCtx %p, FwpsCopyStreamDataToBuffer flattened %Iu of %Iu bytes",
+ FlowContext, BytesCopied, StreamData->dataLength);
+
+ NT_ASSERT(BytesCopied == BytesToCopy);
+ FlowContext->ScratchDataLength += BytesCopied;
+
+ DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,
+ "<-- %!FUNC!: FlowCtx %p, new ScratchLength %Iu, return TRUE", FlowContext, FlowContext->ScratchDataLength);
+ return TRUE;
+}
diff --git a/network/trans/stmedit/sys/StreamEdit.h b/network/trans/stmedit/sys/StreamEdit.h
new file mode 100644
index 00000000..8d203db7
--- /dev/null
+++ b/network/trans/stmedit/sys/StreamEdit.h
@@ -0,0 +1,558 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved
+
+Abstract:
+ Stream Edit Callout Driver Sample.
+
+ This sample demonstrates finding and replacing a string pattern from a
+ live TCP stream via the WFP stream API.
+
+Environment:
+ Kernel mode
+
+--*/
+
+#ifndef _STREAM_EDIT_H
+#define _STREAM_EDIT_H
+
+#include <ntifs.h>
+#include <wdf.h>
+
+#include <ntddk.h>
+
+#pragma warning(push)
+#pragma warning(disable:4201) // unnamed struct/union
+#include <fwpsk.h>
+#pragma warning(pop)
+
+#pragma warning(push)
+#pragma prefast(disable: 26061) // warning 26061: Range postcondition violation
+#pragma prefast(disable: 28196) // RtlUnicodeStringInitWorker restriction
+#include <ntstrsafe.h>
+#pragma warning(pop)
+
+#include <fwpmk.h>
+#include "LwQueue.h"
+
+//
+// Pool Tags used for allocations
+//
+#define STMEDIT_TAG_NDIS_OBJ 'oNeS'
+#define STMEDIT_TAG_NBL_POOL 'pNeS'
+#define STMEDIT_TAG_FLAT_BUFFER 'bSeS'
+#define STMEDIT_TAG_FLOWCTX 'cFeS'
+#define STMEDIT_TAG_TASK_ENTRY 'eTeS'
+#define STMEDIT_TAG_MDL_DATA 'dMeS'
+
+#define CFG_LOCAL_PORT 8888
+#define STR_MAX_SIZE 128
+#define NUM_WORKITEM_QUEUES 2
+#define INVALID_PROC_NUMBER -1
+
+#pragma warning(disable: 4127) // conditional expression is constant -- for do-while(true/false) loops!
+
+//
+// Inline editing states of a stream
+//
+typedef enum _INLINE_EDIT_STATE
+{
+ INLINE_EDIT_IDLE = 0,
+ INLINE_EDIT_SKIPPING,
+ INLINE_EDIT_MODIFYING,
+ INLINE_EDIT_SCANNING
+} INLINE_EDIT_STATE;
+
+//
+// Out-Of-Band editing states of a stream
+//
+typedef enum _OOB_EDIT_STATE
+{
+ OOB_EDIT_IDLE = 0,
+ OOB_EDIT_PROCESSING,
+ OOB_EDIT_BUSY,
+ OOB_EDIT_ERROR
+} OOB_EDIT_STATE;
+
+typedef struct _OUTGOING_STREAM_DATA
+{
+ // Link for placement on Queue.
+ LIST_ENTRY Link;
+
+ // NetBuffer List containing data
+ NET_BUFFER_LIST* NetBufferList;
+
+ // If the NetBufferList is a clone or did we allocate it
+ BOOLEAN isClone;
+
+ // Valid data length
+ size_t DataLength;
+
+ // Stream Flags to be used when injecting this segment of data
+ DWORD StreamFlags;
+
+ MDL* Mdl;
+} OUTGOING_STREAM_DATA, *POUTGOING_STREAM_DATA;
+
+#pragma warning(push)
+#pragma warning(disable:4201) // unnamed struct/union
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+
+//
+// Flow Context associated with a flow
+//
+typedef struct _STREAM_FLOW_CONTEXT
+{
+ // for putting on the global FlowContest list
+ LIST_ENTRY Link;
+
+ union
+ {
+ // Inline data editting state
+ struct
+ {
+ // State of inline stream edit processing
+ INLINE_EDIT_STATE InlineEditState;
+
+ // Current Processor on which inline classify function is invoked!
+ volatile LONG CurrentProcessor;
+ };
+
+ struct
+ {
+ // Internal Stream State for OoB Processing
+ OOB_EDIT_STATE EditState;
+
+ // Spin lock to sync editing
+ KSPIN_LOCK EditLock;
+
+ // Processed data ready to be injected back into the stream.
+ LIST_ENTRY OutgoingDataQueue;
+
+ // Index to LW Queue, assigned to the flow.
+ ULONG QueueNumber;
+
+ // Length of data classified, but not yet processed.
+ volatile size_t PendedDataLength;
+
+ // Number of data processing tasks still queued
+ volatile ULONG PendingTasks;
+
+ // Flags that specify characteristics of the data stream.
+ //
+ // A callout driver should specify the same stream flags that
+ // were set in the 'flags' member of the _STREAM_DATA structure
+ // that the filter engine passed to the callout driver's
+ // classifyFn callout function when the callout deferred the
+ // data stream.
+ //
+ UINT32 StreamFlags;
+
+ } OobInfo;
+ };
+
+ // Ref count - # of unprocessed task
+ volatile ULONG RefCount;
+
+ // IVP4 or V6 ? For troubleshooting only
+ UINT16 IpProto;
+
+ // Stream Flags for partially matching data.
+ UINT32 PartialSFlags;
+
+ //
+ // For copying stream data to a (flat)buffer for inspection
+ //
+ PVOID ScratchBuffer;
+ size_t ScratchBufferSize;
+ size_t ScratchDataOffset;
+ size_t ScratchDataLength;
+
+ //
+ // Flow information for data (re-)injection.
+ //
+
+ UINT64 FlowHandle;
+ UINT16 LayerId;
+ UINT32 CalloutId;
+
+ volatile char bFlowActive; // If the Flow is still active. true @FlowEstablished, false @FlowDelete
+
+ BOOLEAN bFlowTerminating : 1; // FIN/RST has been received.
+ BOOLEAN bNoMoreData : 1; // classifyOut->flags has FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA bit set
+ BOOLEAN bEntryRemoved : 1; // True when the context is not on the 'global context list'.
+ BOOLEAN bEditInline : 1;
+ BOOLEAN bUnused : 4;
+
+} STREAM_FLOW_CONTEXT, *PSTREAM_FLOW_CONTEXT;
+
+#pragma warning(pop)
+
+
+// Task Entry structure
+// For dispatching tasks to threads!
+//
+typedef struct _TASK_ENTRY
+{
+ // Link for placement on Queues
+ LW_ENTRY LwQLink;
+
+ // Target Flow Context
+ PSTREAM_FLOW_CONTEXT FlowCtx;
+
+ // NetBufferList Chain to be processed
+ NET_BUFFER_LIST *NetBufferList;
+
+ // Length of NBL Chain
+ size_t DataLength;
+
+ // Flags..
+ DWORD StreamFlags;
+
+} TASK_ENTRY, *PTASK_ENTRY;
+
+//
+// Stream Editor Globals block
+//
+typedef struct _STMEDIT_GLOBALS
+{
+ // Lookaside list for Task entries and OutgoingData structs.
+ // On 64-bit platforms, this structure must be 16-byte aligned.
+ //
+ DECLSPEC_CACHEALIGN LOOKASIDE_LIST_EX LookasideList;
+
+
+ // ... For Callout #1
+ //
+ // IPV4 callout for FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 layer
+ UINT32 FlowEstablishedV4Callout1;
+
+ // IPV4 callout for FWPM_LAYER_STREAM_V4 layer
+ UINT32 StreamLayerV4Callout1;
+
+ // IPV4 callout for FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 layer
+ UINT32 FlowEstablishedV6Callout1;
+
+ // IPV4 callout for FWPM_LAYER_STREAM_V6 layer
+ UINT32 StreamLayerV6Callout1;
+
+ //
+ // ...For Callout #2
+ //
+ // IPV4 callout for FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 layer
+ UINT32 FlowEstablishedV4Callout2;
+
+ // IPV4 callout for FWPM_LAYER_STREAM_V4 layer
+ UINT32 StreamLayerV4Callout2;
+
+ // IPV4 callout for FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 layer
+ UINT32 FlowEstablishedV6Callout2;
+
+ // IPV4 callout for FWPM_LAYER_STREAM_V6 layer
+ UINT32 StreamLayerV6Callout2;
+
+ // WFP injection handles
+ HANDLE InjectionHandle;
+
+ // WFP Engine handle
+ HANDLE EngineHandle;
+
+ // NDIS uses a generic object to manage resources that are allocated
+ // by a component that does not otherwise have an NDIS handle.
+ NDIS_GENERIC_OBJECT* NdisGenericObj;
+
+ // To allocate NetBuffer and NetBufferLists for injection
+ NDIS_HANDLE NetBufferListPool;
+
+ // MDL chain used to initialize the preallocated NET_BUFFER
+ // structure for injecting replacement string.
+ MDL* StringToReplaceMdl;
+ MDL* StringXMdl;
+
+ // Number of flow-context structures allocated (mostly for troubleshooting)
+ ULONG FlowContextCount;
+
+ // A linked list of flow-context structures
+ LIST_ENTRY FlowContextList;
+
+ // Spin lock for synchronizing FlowContextList access
+ KSPIN_LOCK FlowContextListLock;
+
+ // Event to signal if Flow Context count is zero!
+ KEVENT ZeroFlowCountEvent;
+
+ DEVICE_OBJECT* WdmDevice; // Device object for StreamEdit
+
+ //
+ // Configurable parameters block
+ //
+
+ // Register two sets of callouts ?
+ BOOLEAN MultipleCallouts;
+
+ // Local port for inspecting network traffic
+ USHORT InspectionLocalPort;
+
+ // Remote port for inspecting network traffic
+ USHORT InspectionRemotePort;
+
+ // What traffic direction to inspect
+ UCHAR InspectionDirection;
+
+ // Busy Threshold (to Defer/Resume stream)
+ size_t BusyThreshold;
+
+ // String to search for in the stream
+ CHAR StringToFind[STR_MAX_SIZE];
+
+ // Replacement string!
+ CHAR StringX[STR_MAX_SIZE];
+
+ // Replacement string!
+ CHAR StringToReplace[STR_MAX_SIZE];
+
+ // Length of StringToFind
+ size_t StringToFindLength;
+
+ // Length of StringToReplace
+ size_t StringXLength;
+
+ // Length of StringToReplace
+ size_t StringToReplaceLength;
+
+ // True if the driver is unloading/shutting down
+ volatile char DriverUnloading;
+
+ // To generate workitem queue array index to be associated to a flow.
+ volatile ULONG QueueIndex;
+
+ // Queues for processing task workitems
+ LW_QUEUE ProcessingQueues[NUM_WORKITEM_QUEUES];
+
+ // True if TaskEntry look aside list is successfully initialized.
+ BOOLEAN LookasideCreated;
+
+} STMEDIT_GLOBALS;
+
+
+extern STMEDIT_GLOBALS Globals;
+
+typedef
+VOID
+WFP_CLASSIFY_FUNCTION(
+ _In_ const FWPS_INCOMING_VALUES*,
+ _In_ const FWPS_INCOMING_METADATA_VALUES*,
+ _In_ PVOID,
+#if(NTDDI_VERSION >= NTDDI_WIN7)
+ _In_ const void*,
+#endif
+ _In_ const FWPS_FILTER*,
+ _In_ UINT64,
+ _Inout_ FWPS_CLASSIFY_OUT*
+);
+
+
+IO_WORKITEM_ROUTINE StreamEditOobPoolWorker;
+
+NTSTATUS
+StreamEditInitializeWorkitemPool(
+ );
+
+NTSTATUS
+StreamOobFlushOutgoingData(
+ _Inout_ PSTREAM_FLOW_CONTEXT
+ );
+
+NTSTATUS
+StreamEditRemoveFlowCtx(
+ _In_ STREAM_FLOW_CONTEXT*
+ );
+
+VOID
+StreamEditSignalShutdown(
+ );
+
+BOOLEAN
+StreamEditCopyDataForInspection(
+ _In_ PSTREAM_FLOW_CONTEXT,
+ _In_ const FWPS_STREAM_DATA*,
+ _In_ SIZE_T BytesToCopy
+ );
+
+VOID
+NTAPI
+StreamEditInjectCompletionFn(
+ _In_ VOID*,
+ _Inout_ NET_BUFFER_LIST*,
+ _In_ BOOLEAN
+ );
+
+VOID
+NTAPI
+StreamOobCloneInjectCompletionFn(
+ _In_ VOID*,
+ _Inout_ NET_BUFFER_LIST*,
+ _In_ BOOLEAN
+ );
+
+NTSTATUS
+OobEditCreateThread(
+ _Inout_ PSTREAM_FLOW_CONTEXT
+ );
+
+VOID
+InlineEditClassify(
+ _In_ const FWPS_INCOMING_VALUES*,
+ _In_ const FWPS_INCOMING_METADATA_VALUES*,
+ _Inout_ PVOID,
+ _In_ const FWPS_FILTER*,
+ _In_ UINT64,
+ _Inout_ FWPS_CLASSIFY_OUT*
+ );
+
+VOID
+OobEditClassify(
+ _In_ const FWPS_INCOMING_VALUES*,
+ _In_ const FWPS_INCOMING_METADATA_VALUES*,
+ _Inout_ PVOID,
+ _In_ const FWPS_FILTER*,
+ _In_ UINT64,
+ _Inout_ FWPS_CLASSIFY_OUT*
+ );
+
+WFP_CLASSIFY_FUNCTION StreamEditFlowEstablishedClassify;
+WFP_CLASSIFY_FUNCTION StreamEditCommonStreamClassify;
+
+NTSTATUS
+InlineEditFlushData(
+ _In_ PSTREAM_FLOW_CONTEXT,
+ _In_ ULONG,
+ _In_ UINT
+ );
+
+FORCEINLINE
+VOID
+StreamEditFreeFlowCtxCommon(
+ _Inout_ PSTREAM_FLOW_CONTEXT
+ );
+
+VOID
+StmEditReferenceFlow(
+ _Inout_ PSTREAM_FLOW_CONTEXT,
+ _In_ char,
+ _In_ UINT
+ );
+
+VOID
+StmEditDeReferenceFlow(
+ _Inout_ PSTREAM_FLOW_CONTEXT,
+ _In_ char,
+ _In_ UINT
+ );
+
+FORCEINLINE
+ULONG NetBufferListLength(PNET_BUFFER_LIST Nbl)
+{
+ ULONG Length = 0;
+ PNET_BUFFER nb = NET_BUFFER_LIST_FIRST_NB(Nbl);
+
+ while (nb)
+ {
+ Length += NET_BUFFER_DATA_LENGTH(nb);
+ nb = NET_BUFFER_NEXT_NB(nb);
+ }
+
+ return Length;
+}
+
+//
+// Callout driver keys
+//
+
+/* 524F4849-5420-5241-494E-41205A9EEF0B */
+__declspec (selectany) const GUID STREAM_EDITOR_SUBLAYER_1 = {
+ 0x524F4849,
+ 0x5420,
+ 0x5241,
+ { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x0A }
+};
+
+/* 524F4849-5420-5241-494E-41205A9EEF0B */
+__declspec (selectany) const GUID STREAM_EDITOR_SUBLAYER_2 = {
+ 0x524F4849,
+ 0x5420,
+ 0x5241,
+ { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x0B }
+};
+
+
+/* 524F4849-5420-5241-494E-41205A9EEF0C */
+__declspec (selectany) const GUID STREAM_EDITOR_STREAM_CALLOUT_V4 = {
+ 0x524F4849,
+ 0x5420,
+ 0x5241,
+ { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x0C }
+};
+
+/* 524F4849-5420-5241-494E-41205A9EEF0D */
+__declspec (selectany) const GUID STREAM_EDITOR_STREAM_CALLOUT_V6 = {
+ 0x524F4849,
+ 0x5420,
+ 0x5241,
+ { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x0D }
+};
+
+/* 524F4849-5420-5241-494E-41205A9EEF0E */
+__declspec (selectany) const GUID STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V4 = {
+ 0x524F4849,
+ 0x5420,
+ 0x5241,
+ { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x0E }
+};
+
+/* 524F4849-5420-5241-494E-41205A9EEF0F */
+__declspec (selectany) const GUID STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V6 = {
+ 0x524F4849,
+ 0x5420,
+ 0x5241,
+ { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x0F }
+};
+
+/* 524F4849-5420-5241-494E-41205A9EEF01 */
+__declspec (selectany) const GUID STREAM_EDITOR_STREAM_CALLOUT_V4_2 = {
+ 0x524F4849,
+ 0x5420,
+ 0x5241,
+ { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x01 }
+};
+
+/* 524F4849-5420-5241-494E-41205A9EEF02 */
+__declspec (selectany) const GUID STREAM_EDITOR_STREAM_CALLOUT_V6_2 = {
+ 0x524F4849,
+ 0x5420,
+ 0x5241,
+ { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x02 }
+};
+
+/* 524F4849-5420-5241-494E-41205A9EEF03 */
+__declspec (selectany) const GUID STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V4_2 = {
+ 0x524F4849,
+ 0x5420,
+ 0x5241,
+ { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x03 }
+};
+
+/* 524F4849-5420-5241-494E-41205A9EEF04 */
+__declspec (selectany) const GUID STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V6_2 = {
+ 0x524F4849,
+ 0x5420,
+ 0x5241,
+ { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x04 }
+};
+
+// {C1EA91DC-A37F-453E-BFD5-EF68E36EEDAA}
+__declspec (selectany) const GUID WFP_DRIVER_CLASS_GUID =
+{ 0xc1ea91dc, 0xa37f, 0x453e,{ 0xbf, 0xd5, 0xef, 0x68, 0xe3, 0x6e, 0xed, 0xaa } };
+
+
+#endif // _STREAM_EDIT_H
diff --git a/network/trans/stmedit/sys/Trace.h b/network/trans/stmedit/sys/Trace.h
new file mode 100644
index 00000000..b52033cd
--- /dev/null
+++ b/network/trans/stmedit/sys/Trace.h
@@ -0,0 +1,61 @@
+
+#ifndef _STREAM_TRACE_H
+#define _STREAM_TRACE_H
+
+// WPP Software Tracing Definitions
+
+
+/* 524F4849-5420-5241-494E-41205A9EEF01 */
+
+// You can have up to 32 defines. If you want more than that,
+// you have to provide another trace control GUID
+
+// Don't use 0x suffix in hex numbers in the guid
+
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID(CtlGuid, \
+ (524F4849, 5420, 5241, 494E, 41205A9EEF01),\
+ WPP_DEFINE_BIT(CO_ENTER_EXIT) /* bit 0 = 0x00000001 */ \
+ WPP_DEFINE_BIT(CO_GENERAL) /* bit 1 = 0x00000002 */ \
+ WPP_DEFINE_BIT(CO_REFCOUNT) /* bit 2 = 0x00000004 */ \
+ )
+
+// For DoTraceLevelMessage
+#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) (WPP_FLAG_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl)
+#define WPP_LEVEL_FLAGS_LOGGER(lvl, flags) WPP_LEVEL_LOGGER(flags)
+
+
+#pragma warning(disable:4204) // C4204 nonstandard extension used : non-constant aggregate initializer
+
+//
+// Define the 'xstr' structure for logging buffer and length pairs
+// and the 'log_xstr' function which returns it to create one in-place.
+// this enables logging of complex data types.
+//
+typedef struct xstr { char * _buf; short _len; } xstr_t;
+__inline xstr_t log_xstr(void * p, short l) { xstr_t xs = { (char*)p, l }; return xs; }
+
+#pragma warning(default:4204)
+
+//
+// Define the macro required for a hexdump use as:
+//
+// DebugTraceEx((LEVEL, FLAG,"%!HEXDUMP!\n", log_xstr(buffersize,(char *)buffer) ));
+//
+//
+#define WPP_LOGHEXDUMP(x) WPP_LOGPAIR(2, &((x)._len)) WPP_LOGPAIR((x)._len, (x)._buf)
+
+// begin_wpp config
+// CUSTOM_TYPE(InlineState, ItemListLong(Idle, Skipping, Modifying, Scanning));
+// CUSTOM_TYPE(OobState, ItemListLong(Idle, Processing, Busy, Error));
+// CUSTOM_TYPE(FWP_DIRECTION, ItemListLong(Outbound, Inbound, Outbound+Inbound));
+//
+// CUSTOM_TYPE(FWPS_NOTIFY, ItemEnum(FWPS_CALLOUT_NOTIFY_TYPE));
+// CUSTOM_TYPE(SACTION, ItemEnum(FWPS_STREAM_ACTION_TYPE));
+//
+// DEFINE_CPLX_TYPE(HEXDUMP, WPP_LOGHEXDUMP, xstr_t, ItemHEXDump, "s", _HEX_, 0, 2);
+//
+// end_wpp
+
+
+#endif // _STREAM_TRACE_H
diff --git a/network/trans/stmedit/sys/inline_edit.c b/network/trans/stmedit/sys/inline_edit.c
deleted file mode 100644
index 16685a9e..00000000
--- a/network/trans/stmedit/sys/inline_edit.c
+++ /dev/null
@@ -1,546 +0,0 @@
-/*++
-
-Copyright (c) Microsoft Corporation. All rights reserved
-
-Abstract:
-
- Stream Edit Callout Driver Sample.
-
- This sample demonstrates inline stream inspection/editing
- via the WFP stream API.
-
-Environment:
-
- Kernel mode
-
---*/
-
-#include <ntddk.h>
-
-#pragma warning(push)
-#pragma warning(disable:4201) // unnamed struct/union
-
-#include <fwpsk.h>
-
-#pragma warning(pop)
-
-#include <fwpmk.h>
-
-#include "inline_edit.h"
-#include "oob_edit.h"
-#include "stream_callout.h"
-
-void
-InlineEditInit(
- _Out_ STREAM_EDITOR* streamEditor
- )
-{
- streamEditor->editInline = TRUE;
- streamEditor->inlineEditState = INLINE_EDIT_WAITING_FOR_DATA;
-}
-
-void
-NTAPI StreamInjectCompletionFn(
- _Inout_ void* context,
- _Inout_ NET_BUFFER_LIST* netBufferList,
- _In_ BOOLEAN dispatchLevel
- )
-{
- MDL* mdl = (MDL*)context;
-
- UNREFERENCED_PARAMETER(dispatchLevel);
-
- if (mdl != NULL)
- {
- IoFreeMdl(mdl);
- }
-
- FwpsFreeNetBufferList(netBufferList);
-}
-
-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;
-}
-
-#if(NTDDI_VERSION >= NTDDI_WIN7)
-
-void
-NTAPI
-StreamInlineEditClassify(
- _In_ const FWPS_INCOMING_VALUES* inFixedValues,
- _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
- _Inout_ void* layerData,
- _In_ const void* classifyContext,
- _In_ const FWPS_FILTER* filter,
- _In_ UINT64 flowContext,
- _Inout_ FWPS_CLASSIFY_OUT* classifyOut
- )
-
-#else
-
-void
-NTAPI
-StreamInlineEditClassify(
- _In_ const FWPS_INCOMING_VALUES* inFixedValues,
- _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
- _Inout_ void* layerData,
- _In_ const FWPS_FILTER* filter,
- _In_ UINT64 flowContext,
- _Out_ FWPS_CLASSIFY_OUT* classifyOut
- )
-
-#endif /// (NTDDI_VERSION >= NTDDI_WIN7)
-
-/* ++
-
- This is the ClassifyFn function registered by the inline stream edit callout.
-
- An inline stream modification callout performs editing from within the
- ClassifyFn call by permitting sections of the content and replacing other
- sections by removing them and injecting new content.
-
--- */
-{
- FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket;
- FWPS_STREAM_DATA* streamData;
-
- ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)layerData;
- NT_ASSERT(ioPacket != NULL);
-
- streamData = ioPacket->streamData;
- NT_ASSERT(streamData != NULL);
-
-#if(NTDDI_VERSION >= NTDDI_WIN7)
- UNREFERENCED_PARAMETER(classifyContext);
-#endif /// (NTDDI_VERSION >= NTDDI_WIN7)
- UNREFERENCED_PARAMETER(flowContext);
-
- RtlZeroMemory(classifyOut, sizeof(FWPS_CLASSIFY_OUT));
-
- //
- // Let go the traffic that the editor does not care about.
- //
-
- if ((configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_RECEIVE)) ||
- (!configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_SEND)))
- {
- 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;
- }
-
- //
- // In this sample we don't edit TCP urgent data
- //
-
- if ((streamData->flags & FWPS_STREAM_FLAG_SEND_EXPEDITED) ||
- (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_EXPEDITED))
- {
- 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;
- }
-
- StreamInlineEdit(
- &gStreamEditor,
- inFixedValues,
- inMetaValues,
- filter,
- streamData,
- ioPacket,
- classifyOut
- );
-
-Exit:
-
- return;
-}
diff --git a/network/trans/stmedit/sys/inline_edit.h b/network/trans/stmedit/sys/inline_edit.h
deleted file mode 100644
index a9345324..00000000
--- a/network/trans/stmedit/sys/inline_edit.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*++
-
-Copyright (c) Microsoft Corporation. All rights reserved
-
-Abstract:
-
- Stream Edit Callout Driver Sample.
-
- This sample demonstrates inline stream inspection/editing
- via the WFP stream API.
-
-Environment:
-
- Kernel mode
-
---*/
-
-#ifndef _INLINE_EDIT_H
-#define _INLINE_EDIT_H
-
-typedef enum INLINE_EDIT_STATE_
-{
- INLINE_EDIT_WAITING_FOR_DATA,
- INLINE_EDIT_SKIPPING,
- INLINE_EDIT_MODIFYING,
- INLINE_EDIT_SCANNING
-} INLINE_EDIT_STATE;
-
-typedef struct STREAM_EDITOR_ STREAM_EDITOR;
-
-void
-InlineEditInit(
- _Out_ STREAM_EDITOR*
- );
-
-#if(NTDDI_VERSION >= NTDDI_WIN7)
-
-void
-NTAPI
-StreamInlineEditClassify(
- _In_ const FWPS_INCOMING_VALUES* inFixedValues,
- _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
- _Inout_ void* layerData,
- _In_ const void* classifyContext,
- _In_ const FWPS_FILTER* filter,
- _In_ UINT64 flowContext,
- _Inout_ FWPS_CLASSIFY_OUT* classifyOut
- );
-
-#else
-
-void
-NTAPI
-StreamInlineEditClassify(
- _In_ const FWPS_INCOMING_VALUES* inFixedValues,
- _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
- _Inout_ void* layerData,
- _In_ const FWPS_FILTER* filter,
- _In_ UINT64 flowContext,
- _Inout_ FWPS_CLASSIFY_OUT* classifyOut
- );
-
-#endif /// (NTDDI_VERSION >= NTDDI_WIN7)
-
-#endif // _INLINE_EDIT_H
diff --git a/network/trans/stmedit/sys/oob_edit.c b/network/trans/stmedit/sys/oob_edit.c
deleted file mode 100644
index ee53bc5e..00000000
--- a/network/trans/stmedit/sys/oob_edit.c
+++ /dev/null
@@ -1,1363 +0,0 @@
-/*++
-
-Copyright (c) Microsoft Corporation. All rights reserved
-
-Abstract:
-
- Stream Edit Callout Driver Sample.
-
- This sample demonstrates Out-of-band (OOB) stream inspection/editing
- via the WFP stream API.
-
-Environment:
-
- Kernel mode
-
---*/
-
-#include <ntddk.h>
-
-#pragma warning(push)
-#pragma warning(disable:4201) // unnamed struct/union
-
-#include <fwpsk.h>
-
-#pragma warning(pop)
-
-#include <fwpmk.h>
-
-#include "inline_edit.h"
-#include "oob_edit.h"
-#include "stream_callout.h"
-
-#define STREAM_EDITOR_OUTGOING_DATA_TAG 'doeS'
-#define STREAM_EDITOR_MDL_DATA_TAG 'dmeS'
-
-void* gThreadObj;
-
-KSTART_ROUTINE StreamOobEditWorker;
-
-NTSTATUS
-OobEditInit(
- _Out_ STREAM_EDITOR* streamEditor
- )
-{
- NTSTATUS status = STATUS_SUCCESS;
-
- HANDLE threadHandle;
-
- streamEditor->editInline = FALSE;
-
- KeInitializeSpinLock(&streamEditor->oobEditInfo.editLock);
-
- KeInitializeEvent(
- &streamEditor->oobEditInfo.editEvent,
- NotificationEvent,
- FALSE
- );
-
- streamEditor->oobEditInfo.busyThreshold = 32 * 1024;
- streamEditor->oobEditInfo.editState = OOB_EDIT_IDLE;
-
- InitializeListHead(&streamEditor->oobEditInfo.outgoingDataQueue);
-
-
- status = PsCreateSystemThread(
- &threadHandle,
- THREAD_ALL_ACCESS,
- NULL,
- NULL,
- NULL,
- StreamOobEditWorker,
- &gStreamEditor
- );
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- status = ObReferenceObjectByHandle(
- threadHandle,
- 0,
- NULL,
- KernelMode,
- &gThreadObj,
- NULL
- );
- NT_ASSERT(NT_SUCCESS(status));
-
- ZwClose(threadHandle);
-
-Exit:
-
- return status;
-}
-
-void
-OobEditShutdown(
- _Out_ STREAM_EDITOR* streamEditor
- )
-{
- KLOCK_QUEUE_HANDLE editLockHandle;
-
- KeAcquireInStackQueuedSpinLock(
- &streamEditor->oobEditInfo.editLock,
- &editLockHandle
- );
-
- streamEditor->oobEditInfo.shuttingDown = TRUE;
-
- switch (streamEditor->oobEditInfo.editState)
- {
- case OOB_EDIT_IDLE:
- {
- streamEditor->oobEditInfo.editState = OOB_EDIT_SHUT_DOWN;
-
- KeSetEvent(
- &gStreamEditor.oobEditInfo.editEvent,
- IO_NO_INCREMENT,
- FALSE
- );
-
- break;
- }
- default:
- break;
- };
-
- KeReleaseInStackQueuedSpinLock(&editLockHandle);
-
- NT_ASSERT(gThreadObj != NULL);
-
- KeWaitForSingleObject(
- gThreadObj,
- Executive,
- KernelMode,
- FALSE,
- NULL
- );
-
- ObDereferenceObject(gThreadObj);
-}
-
-__inline
-NET_BUFFER_LIST*
-TailOfNetBufferListChain(
- _In_ NET_BUFFER_LIST* netBufferListChain
- )
-{
- NT_ASSERT(netBufferListChain != NULL);
-
- while (netBufferListChain->Next != NULL)
- {
- netBufferListChain = netBufferListChain->Next;
- }
-
- return netBufferListChain;
-}
-
-void
-NTAPI
-StreamOobInjectCompletionFn(
- _Inout_ void* context,
- _Inout_ NET_BUFFER_LIST* netBufferList,
- BOOLEAN dispatchLevel
- )
-/* ++
-
- Injection completion function for injecting an NBL created using
- FwpsAllocateNetBufferAndNetBufferList. This function frees up
- resources allocated during StreamOobReinjectData().
-
--- */
-{
- MDL* mdl = (MDL*)context;
-
- UNREFERENCED_PARAMETER(dispatchLevel);
-
- FwpsFreeNetBufferList(netBufferList);
-
- if (mdl != NULL)
- {
- IoFreeMdl(mdl);
-
- //
- // The MDL mapped over a pool alloc which we need to free here.
- //
-
- ExFreePoolWithTag(
- mdl->MappedSystemVa,
- STREAM_EDITOR_MDL_DATA_TAG
- );
- }
-}
-
-void
-NTAPI StreamOobInjectCloneCompletionFn(
- _Inout_ void* context,
- _Inout_ NET_BUFFER_LIST* netBufferList,
- BOOLEAN dispatchLevel
- )
-/* ++
-
- Injection completion function for injecting one of the NBLs cloned
- via FwpsCloneStreamData.
-
- FwpsCloneStreamData can return a chain of cloned NBLs; each NBL will
- complete separately.
-
--- */
-{
- UNREFERENCED_PARAMETER(context);
- UNREFERENCED_PARAMETER(dispatchLevel);
-
- FwpsFreeCloneNetBufferList(netBufferList, 0);
-}
-
-NTSTATUS
-StreamOobQueueUpIncomingData(
- _Inout_ STREAM_EDITOR* streamEditor,
- _Inout_ FWPS_STREAM_DATA* streamData
- )
-/* ++
-
- This function clones the indicated stream data into a NBL chain and
- appends the chain at the end of the existing chain (if exists) inside
- the streamEditor.
-
- This function assumes that the oobEditInfo lock inside streamEditor is
- being held.
-
--- */
-{
- NTSTATUS status;
-
- NET_BUFFER_LIST* clonedNetBufferListChain;
-
- status = FwpsCloneStreamData(
- streamData,
- NULL,
- NULL,
- 0,
- &clonedNetBufferListChain
- );
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- //
- // TCP Fin (EOF) is indicated by an empty NBL with disconnect flag
- // set, since it does not contain any data we queue it up separately.
- //
-
- if ((streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) ||
- (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT))
- {
- NT_ASSERT(streamEditor->oobEditInfo.noMoreData);
- NT_ASSERT(streamEditor->oobEditInfo.nblEof == NULL);
- NT_ASSERT(streamData->dataLength == 0);
-
- streamEditor->oobEditInfo.nblEof = clonedNetBufferListChain;
-
- status = STATUS_SUCCESS;
- goto Exit;
- }
-
- if (streamEditor->oobEditInfo.nblTail != NULL)
- {
- NT_ASSERT(streamEditor->oobEditInfo.nblHead != NULL);
- NT_ASSERT(streamEditor->oobEditInfo.nblTail->Next == NULL);
-
- streamEditor->oobEditInfo.nblTail->Next = clonedNetBufferListChain;
- }
- else
- {
- NT_ASSERT(streamEditor->oobEditInfo.nblHead == NULL);
-
- streamEditor->oobEditInfo.nblHead = clonedNetBufferListChain;
- }
-
- streamEditor->oobEditInfo.nblTail =
- TailOfNetBufferListChain(clonedNetBufferListChain);
-
- streamEditor->oobEditInfo.totalDataLength += streamData->dataLength;
-
- streamEditor->oobEditInfo.streamFlags = streamData->flags;
-
-Exit:
-
- return status;
-}
-
-void
-StreamOobEdit(
- _Inout_ STREAM_EDITOR* streamEditor,
- const FWPS_INCOMING_VALUES* inFixedValues,
- const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
- const FWPS_FILTER* filter,
- _Inout_ FWPS_STREAM_DATA* streamData,
- _Inout_ FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket,
- _Inout_ FWPS_CLASSIFY_OUT* classifyOut
- )
-/* ++
-
- This function queues up incoming data and notifies the worker thread
- to process them. The incoming data is blocked and removed from the
- stream while data is pending.
-
- If the editor is shutdown (e.g. during driverUnload) as indicated
- by OOB_EDIT_SHUT_DOWN state, it permits the indicated data inline after
- flushing all pended data (to be carried out by the caller).
-
--- */
-
-{
- NTSTATUS status;
-
- KLOCK_QUEUE_HANDLE editLockHandle;
-
- KeAcquireInStackQueuedSpinLock(
- &streamEditor->oobEditInfo.editLock,
- &editLockHandle
- );
-
- if (streamEditor->oobEditInfo.nblEof != NULL)
- {
- //
- // A new flow arrives before we finish processing an earlier flow. Production
- // code should create 1:1 between streamEditor and flow to handle this
- // condition. See the "MSN Monitor sample" for how that can be implemented.
- //
-
- ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION;
- classifyOut->actionType = FWP_ACTION_NONE;
-
- goto Exit;
- }
-
- if (classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA)
- {
- NT_ASSERT(streamEditor->oobEditInfo.nblEof == NULL);
-
- streamEditor->oobEditInfo.noMoreData = TRUE;
- }
-
- //
- // Record needed flow information etc for data (re-)injection.
- //
-
- streamEditor->oobEditInfo.calloutId = filter->action.calloutId;
- streamEditor->oobEditInfo.flowId = inMetaValues->flowHandle;
- streamEditor->oobEditInfo.layerId = inFixedValues->layerId;
-
- switch (streamEditor->oobEditInfo.editState)
- {
- case OOB_EDIT_PROCESSING:
- {
- if ((streamEditor->oobEditInfo.totalDataLength + streamData->dataLength) >
- streamEditor->oobEditInfo.busyThreshold)
- {
- ioPacket->streamAction = FWPS_STREAM_ACTION_DEFER;
- classifyOut->actionType = FWP_ACTION_NONE;
-
- streamEditor->oobEditInfo.editState = OOB_EDIT_BUSY;
- }
- else
- {
- status = StreamOobQueueUpIncomingData(
- streamEditor,
- streamData
- );
-
- if (!NT_SUCCESS(status))
- {
- streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR;
-
- ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION;
- classifyOut->actionType = FWP_ACTION_NONE;
- }
- else
- {
- //
- // State remains at OOB_EDIT_PROCESSING state. Since the worker thread
- // is active there is no need to set the event (to wake it up)
- //
-
- ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;
- ioPacket->countBytesEnforced = 0;
- classifyOut->actionType = FWP_ACTION_BLOCK;
- classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
- }
- }
-
- break;
- }
- case OOB_EDIT_IDLE:
- {
- status = StreamOobQueueUpIncomingData(
- streamEditor,
- streamData
- );
-
- if (!NT_SUCCESS(status))
- {
- streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR;
-
- ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION;
- classifyOut->actionType = FWP_ACTION_NONE;
- }
- else
- {
- streamEditor->oobEditInfo.editState = OOB_EDIT_PROCESSING;
-
- //
- // The worker thread is idle waiting for more work, now wake it up.
- //
- KeSetEvent(
- &streamEditor->oobEditInfo.editEvent,
- 0,
- FALSE
- );
-
- ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;
- ioPacket->countBytesEnforced = 0;
- classifyOut->actionType = FWP_ACTION_BLOCK;
- classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
- }
-
- break;
- }
- case OOB_EDIT_SHUT_DOWN:
- {
- 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;
- }
-
- break;
- }
- case OOB_EDIT_ERROR:
- {
- ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION;
- classifyOut->actionType = FWP_ACTION_NONE;
- break;
- }
- default:
- NT_ASSERT(FALSE);
- };
-
-Exit:
-
- KeReleaseInStackQueuedSpinLock(&editLockHandle);
-}
-
-NTSTATUS
-StreamOobQueueUpOutgoingData(
- _Inout_ STREAM_EDITOR* streamEditor,
- _Inout_ NET_BUFFER_LIST* netBufferList,
- BOOLEAN isClone,
- size_t dataLength,
- DWORD streamFlags,
- _In_opt_ MDL* mdl
- )
-/* ++
-
- This function queues up processed data (either sections of the indicated
- data or newly created data) such that they can be (re-)injected back to
- the data stream during the following context.
-
- 1. Before FWP_ACTION_BLOCK is returned from the ClassifyFn, or
- 2. After EOF is indicated.
-
- Under the conditions above, the incoming data (which we pend) and the
- outgoing data (which we (re-)inject) can be synchronized properly).
-
--- */
-{
- NTSTATUS status = STATUS_SUCCESS;
- KLOCK_QUEUE_HANDLE editLockHandle;
- OUTGOING_STREAM_DATA* outgoingStreamData;
-
- outgoingStreamData = (OUTGOING_STREAM_DATA*) ExAllocatePoolWithTag(
- NonPagedPool,
- sizeof(OUTGOING_STREAM_DATA),
- STREAM_EDITOR_OUTGOING_DATA_TAG
- );
-
- if (outgoingStreamData == NULL)
- {
- status = STATUS_NO_MEMORY;
- return status;
- }
-
- RtlZeroMemory(outgoingStreamData, sizeof(OUTGOING_STREAM_DATA));
-
- outgoingStreamData->netBufferList = netBufferList;
- outgoingStreamData->isClone = isClone;
- outgoingStreamData->dataLength = dataLength;
- outgoingStreamData->streamFlags = streamFlags;
- outgoingStreamData->mdl = mdl;
-
- KeAcquireInStackQueuedSpinLock(
- &streamEditor->oobEditInfo.editLock,
- &editLockHandle
- );
-
- InsertTailList(
- &streamEditor->oobEditInfo.outgoingDataQueue,
- &outgoingStreamData->listEntry
- );
-
- KeReleaseInStackQueuedSpinLock(&editLockHandle);
-
- return status;
-}
-
-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
-StreamOobReinjectData(
- _Inout_ STREAM_EDITOR* streamEditor,
- UINT32 streamFlags,
- const void* data,
- size_t length
- )
-/* ++
-
- This function injects a section of the original indicated data back
- to the data stream.
-
- An MDL is allocated to describe the data section.
-
--- */
-{
- NTSTATUS status;
-
- void* dataCopy = NULL;
- MDL* mdl = NULL;
- NET_BUFFER_LIST* netBufferList = NULL;
-
- dataCopy = ExAllocatePoolWithTag(
- NonPagedPool,
- length,
- STREAM_EDITOR_MDL_DATA_TAG
- );
-
- if (dataCopy == NULL)
- {
- status = STATUS_NO_MEMORY;
- goto Exit;
- }
-
- RtlCopyMemory(dataCopy, data, length);
-
- mdl = IoAllocateMdl(
- dataCopy,
- (ULONG)length,
- FALSE,
- FALSE,
- NULL
- );
- if (mdl == NULL)
- {
- status = STATUS_NO_MEMORY;
- goto Exit;
- }
-
- MmBuildMdlForNonPagedPool(mdl);
-
- status = FwpsAllocateNetBufferAndNetBufferList(
- gNetBufferListPool,
- 0,
- 0,
- mdl,
- 0,
- length,
- &netBufferList
- );
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- NT_ASSERT(!(streamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) &&
- !(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT));
-
- status = StreamOobQueueUpOutgoingData(
- streamEditor,
- netBufferList,
- FALSE,
- length,
- streamFlags,
- mdl
- );
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- dataCopy = NULL;
- mdl = NULL;
- netBufferList = NULL;
-
-Exit:
-
- if (netBufferList != NULL)
- {
- FwpsFreeNetBufferList(netBufferList);
- }
- if (mdl != NULL)
- {
- IoFreeMdl(mdl);
- }
- if (dataCopy != NULL)
- {
- ExFreePoolWithTag(
- dataCopy,
- STREAM_EDITOR_MDL_DATA_TAG
- );
- }
- return status;
-}
-
-NTSTATUS
-StreamOobInjectReplacement(
- _Inout_ STREAM_EDITOR* streamEditor,
- UINT32 streamFlags,
- _In_opt_ MDL* data,
- size_t length
- )
-/* ++
-
- This function injects a section of replacement data (in place of data
- removed from the stream) into the data stream.
-
- The MDL describes the replacement data is allocated during DriverEntry
- and does not need to be freed during injection completion.
-
--- */
-{
- NTSTATUS status;
-
- NET_BUFFER_LIST* netBufferList = NULL;
-
- status = FwpsAllocateNetBufferAndNetBufferList(
- gNetBufferListPool,
- 0,
- 0,
- data,
- 0,
- length,
- &netBufferList
- );
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- NT_ASSERT(!(streamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) &&
- !(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT));
-
- status = StreamOobQueueUpOutgoingData(
- streamEditor,
- netBufferList,
- FALSE,
- length,
- streamFlags,
- NULL
- );
-
- if (!NT_SUCCESS(status))
- {
- FwpsFreeNetBufferList(netBufferList);
- goto Exit;
- }
-
- netBufferList = NULL;
-
-Exit:
-
- if (netBufferList != NULL)
- {
- FwpsFreeNetBufferList(netBufferList);
- }
-
- return status;
-}
-
-NTSTATUS
-StreamOobCopyDataToFlatBuffer(
- _Inout_ STREAM_EDITOR* streamEditor,
- _Inout_ NET_BUFFER_LIST* netBufferListChain,
- size_t totalDataLength,
- DWORD streamFlags
- )
-/* ++
-
- This function copies the data described by NBL(s) into a flat buffer.
-
- It reuses the FwpsCopyStreamDataToBuffer API (via StreamCopyDataForInspection)
- by creating a FWPS_STREAM_DATA struct.
-
--- */
-{
- NTSTATUS status = STATUS_SUCCESS;
-
- FWPS_STREAM_DATA streamData = {0};
-
- if (totalDataLength > 0)
- {
- streamData.netBufferListChain = netBufferListChain;
- streamData.dataLength = totalDataLength;
- streamData.flags = streamFlags;
-
- streamData.dataOffset.netBufferList = netBufferListChain;
- streamData.dataOffset.netBuffer =
- NET_BUFFER_LIST_FIRST_NB(streamData.dataOffset.netBufferList);
- streamData.dataOffset.mdl =
- NET_BUFFER_CURRENT_MDL(streamData.dataOffset.netBuffer);
- streamData.dataOffset.mdlOffset =
- NET_BUFFER_CURRENT_MDL_OFFSET(streamData.dataOffset.netBuffer);
-
- if (StreamCopyDataForInspection(
- streamEditor,
- &streamData
- ) == FALSE)
- {
- status = STATUS_NO_MEMORY;
- }
- }
-
- 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;
-}
-
-_IRQL_requires_same_
-_Function_class_(KSTART_ROUTINE)
-void
-StreamOobEditWorker(
- _In_ void* StartContext
- )
-/* ++
-
- This function waits for an event which gets signalled when there is data
- waiting to be inspected.
-
- Once awaken, the worker thread edits the stream until all stream data is
- processed (and then it waits for more work again).
-
- When requested to shutdown, it will finish the editing task and enters
- "shutdown" state.
-
--- */
-{
- NTSTATUS status = STATUS_SUCCESS;
-
- NET_BUFFER_LIST* netBufferListChain = NULL;
- size_t totalDataLength;
- STREAM_EDITOR* streamEditor = (STREAM_EDITOR*)StartContext;
- DWORD streamFlags;
-
- for(;;)
- {
- KLOCK_QUEUE_HANDLE editLockHandle;
-
- KeWaitForSingleObject(
- &streamEditor->oobEditInfo.editEvent,
- Executive,
- KernelMode,
- FALSE,
- NULL
- );
-
- if (streamEditor->oobEditInfo.editState == OOB_EDIT_ERROR ||
- streamEditor->oobEditInfo.editState == OOB_EDIT_SHUT_DOWN)
- {
- break;
- }
-
- KeAcquireInStackQueuedSpinLock(
- &streamEditor->oobEditInfo.editLock,
- &editLockHandle
- );
-
- NT_ASSERT(streamEditor->oobEditInfo.editState == OOB_EDIT_PROCESSING ||
- streamEditor->oobEditInfo.editState == OOB_EDIT_BUSY);
-
- netBufferListChain = streamEditor->oobEditInfo.nblHead;
- totalDataLength = streamEditor->oobEditInfo.totalDataLength;
- streamFlags = streamEditor->oobEditInfo.streamFlags;
-
- streamEditor->oobEditInfo.nblHead = NULL;
- streamEditor->oobEditInfo.nblTail = NULL;
- streamEditor->oobEditInfo.totalDataLength = 0;
-
- KeReleaseInStackQueuedSpinLock(&editLockHandle);
-
- _Analysis_assume_(netBufferListChain != NULL);
- status = StreamOobEditData(
- streamEditor,
- netBufferListChain,
- totalDataLength,
- streamFlags
- );
-
- if (!NT_SUCCESS(status))
- {
- streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR;
- break;
- }
-
- if (streamEditor->oobEditInfo.editState == OOB_EDIT_BUSY)
- {
- NTSTATUS streamContinueStatus;
- streamEditor->oobEditInfo.editState = OOB_EDIT_PROCESSING;
-
- streamContinueStatus = FwpsStreamContinue(
- streamEditor->oobEditInfo.flowId,
- streamEditor->oobEditInfo.calloutId,
- streamEditor->oobEditInfo.layerId,
- streamEditor->oobEditInfo.streamFlags
- );
-
- if (!NT_SUCCESS(streamContinueStatus))
- {
- streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR;
- break;
- }
- }
-
- KeAcquireInStackQueuedSpinLock(
- &streamEditor->oobEditInfo.editLock,
- &editLockHandle
- );
-
- if (streamEditor->oobEditInfo.nblHead == NULL)
- {
- if (!streamEditor->oobEditInfo.shuttingDown)
- {
- streamEditor->oobEditInfo.editState = OOB_EDIT_IDLE;
- KeClearEvent(&streamEditor->oobEditInfo.editEvent);
- }
- else
- {
- streamEditor->oobEditInfo.editState = OOB_EDIT_SHUT_DOWN;
- }
- }
-
- KeReleaseInStackQueuedSpinLock(&editLockHandle);
- }
-
- PsTerminateSystemThread(status);
-}
-
-#if(NTDDI_VERSION >= NTDDI_WIN7)
-
-void
-NTAPI
-StreamOobEditClassify(
- _In_ const FWPS_INCOMING_VALUES* inFixedValues,
- _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
- _Inout_ void* layerData,
- _In_ const void* classifyContext,
- _In_ const FWPS_FILTER* filter,
- _In_ UINT64 flowContext,
- _Inout_ FWPS_CLASSIFY_OUT* classifyOut
- )
-
-#else
-
-void
-NTAPI
-StreamOobEditClassify(
- _In_ const FWPS_INCOMING_VALUES* inFixedValues,
- _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
- _Inout_ void* layerData,
- _In_ const FWPS_FILTER* filter,
- _In_ UINT64 flowContext,
- _Inout_ FWPS_CLASSIFY_OUT* classifyOut
- )
-
-#endif /// (NTDDI_VERSION >= NTDDI_WIN7)
-/* ++
-
- This is the ClassifyFn function registered by the OOB stream edit callout.
-
- An OOB stream modification callout blocks all indicated data after cloning
- them for processing by a kernel mode worker thread (or marshalling the data
- to user mode for inspection); the resultant/edited data will then be put
- back to the stream via the stream injection API.
-
- For such a callout, the processed data must be (re-)injected back to the
- stream from within the ClassifyFn.
-
--- */
-{
- FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket;
- FWPS_STREAM_DATA* streamData;
-
- UINT findLength = (UINT) strlen(configStringToFind);
-
- ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)layerData;
- NT_ASSERT(ioPacket != NULL);
-
- streamData = ioPacket->streamData;
- NT_ASSERT(streamData != NULL);
-
-#if(NTDDI_VERSION >= NTDDI_WIN7)
- UNREFERENCED_PARAMETER(classifyContext);
-#endif /// (NTDDI_VERSION >= NTDDI_WIN7)
- UNREFERENCED_PARAMETER(flowContext);
-
- RtlZeroMemory(classifyOut, sizeof(FWPS_CLASSIFY_OUT));
-
- //
- // Let go the traffic that the editor does not care about.
- //
-
- if ((configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_RECEIVE)) ||
- (!configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_SEND)))
- {
- 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;
- }
-
- //
- // In this sample we don't edit TCP urgent data
- //
-
- if ((streamData->flags & FWPS_STREAM_FLAG_SEND_EXPEDITED) ||
- (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_EXPEDITED))
- {
- 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 < 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;
- }
-
- StreamOobEdit(
- &gStreamEditor,
- inFixedValues,
- inMetaValues,
- filter,
- streamData,
- ioPacket,
- classifyOut
- );
-
- if (classifyOut->actionType == FWP_ACTION_BLOCK ||
- classifyOut->actionType == FWP_ACTION_PERMIT)
- {
- if (!(streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) &&
- !(streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT))
- {
- NTSTATUS status = StreamOobFlushOutgoingData( &gStreamEditor);
- if (!NT_SUCCESS(status))
- {
- ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION;
- classifyOut->actionType = FWP_ACTION_NONE;
- }
- }
- }
-
-Exit:
-
- return;
-}
diff --git a/network/trans/stmedit/sys/oob_edit.h b/network/trans/stmedit/sys/oob_edit.h
deleted file mode 100644
index e5a0e506..00000000
--- a/network/trans/stmedit/sys/oob_edit.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*++
-
-Copyright (c) Microsoft Corporation. All rights reserved
-
-Abstract:
-
- Stream Edit Callout Driver Sample.
-
- This sample demonstrates Out-of-band (OOB) stream inspection/editing
- via the WFP stream API.
-
-Environment:
-
- Kernel mode
-
---*/
-
-#ifndef _OOB_EDIT_H
-#define _OOB_EDIT_H
-
-typedef enum OOB_EDIT_STATE_
-{
- OOB_EDIT_IDLE,
- OOB_EDIT_PROCESSING,
- OOB_EDIT_BUSY,
- OOB_EDIT_SHUT_DOWN,
- OOB_EDIT_ERROR
-} OOB_EDIT_STATE;
-
-typedef struct OUTGOING_STREAM_DATA_
-{
- LIST_ENTRY listEntry;
-
- NET_BUFFER_LIST* netBufferList;
- BOOLEAN isClone;
- size_t dataLength;
- DWORD streamFlags;
- MDL* mdl;
-} OUTGOING_STREAM_DATA;
-
-typedef struct STREAM_EDITOR_ STREAM_EDITOR;
-
-NTSTATUS
-OobEditInit(
- _Out_ STREAM_EDITOR*
- );
-
-void
-OobEditShutdown(
- _Out_ STREAM_EDITOR* streamEditor
- );
-
-#if(NTDDI_VERSION >= NTDDI_WIN7)
-
-void
-NTAPI
-StreamOobEditClassify(
- _In_ const FWPS_INCOMING_VALUES* inFixedValues,
- _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
- _Inout_ void* layerData,
- _In_ const void* classifyContext,
- _In_ const FWPS_FILTER* filter,
- _In_ UINT64 flowContext,
- _Inout_ FWPS_CLASSIFY_OUT* classifyOut
- );
-
-#else
-
-void
-NTAPI
-StreamOobEditClassify(
- _In_ const FWPS_INCOMING_VALUES* inFixedValues,
- _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
- _Inout_ void* layerData,
- _In_ const FWPS_FILTER* filter,
- _In_ UINT64 flowContext,
- _Inout_ FWPS_CLASSIFY_OUT* classifyOut
- );
-
-#endif /// (NTDDI_VERSION >= NTDDI_WIN7)
-
-
-#endif // _OOB_EDIT_H
diff --git a/network/trans/stmedit/sys/stmedit.inf b/network/trans/stmedit/sys/stmedit.inf
index b9ee15ea..fac93c26 100644
--- a/network/trans/stmedit/sys/stmedit.inf
+++ b/network/trans/stmedit/sys/stmedit.inf
@@ -9,9 +9,9 @@
Signature = "$Windows NT$"
Class = WFPCALLOUTS
ClassGuid = {57465043-616C-6C6F-7574-5F636C617373}
- Provider = %ProviderString%
+ Provider = %Contoso%
CatalogFile = StmEdit.cat
- DriverVer = 11/24/2014,14.24.55.836
+ DriverVer = 01/01/2015,1.0.09.15
[SourceDisksNames]
1 = %StmEditDisk%,,,""
@@ -50,17 +50,17 @@
AddReg = StmEdit.AddRegistry
[StmEdit.AddRegistry]
- HKR,"Parameters","EditInline",0x00010001,0 ; FLG_ADDREG_TYPE_DWORD
- HKR,"Parameters","StringToFind",0x00000000,"rainy" ; FLG_ADDREG_TYPE_SZ
- HKR,"Parameters","StringToReplace",0x00000000,"sunny" ; FLG_ADDREG_TYPE_SZ
- HKR,"Parameters","InspectionPort",0x00010001,5001 ; FLG_ADDREG_TYPE_DWORD
- HKR,"Parameters","InspectOutbound",0x00010001,0 ; FLG_ADDREG_TYPE_DWORD
+ HKR,"Parameters","MultipleCallouts", 0x00010001, 1 ; FLG_ADDREG_TYPE_DWORD
+ HKR,"Parameters","StringToFind", 0x00000000, "rainy" ; FLG_ADDREG_TYPE_SZ
+ HKR,"Parameters","InspectionRemotePort", 0x00010001, 0 ; FLG_ADDREG_TYPE_DWORD
+ HKR,"Parameters","InspectionLocalPort", 0x00010001, 8888 ; FLG_ADDREG_TYPE_DWORD
+
[StmEdit.DelRegistry]
HKR,"Parameters",,,
[Strings]
- ProviderString = "TODO-Set-Provider"
+ Contoso = "Contoso Ltd."
StmEditDisk = "Stream Edit Installation Disk"
StmEditServiceDesc = "Stream Edit Callout Driver"
StmEditServiceName = "StmEdit" \ No newline at end of file
diff --git a/network/trans/stmedit/sys/stmedit.vcxproj b/network/trans/stmedit/sys/stmedit.vcxproj
index 6194a510..7617ad5d 100644
--- a/network/trans/stmedit/sys/stmedit.vcxproj
+++ b/network/trans/stmedit/sys/stmedit.vcxproj
@@ -19,12 +19,12 @@
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
- <ProjectGuid>{00B26024-D8C5-40FD-A6C7-BA15FD324FD0}</ProjectGuid>
+ <ProjectGuid>{9CE912A5-6210-4EF8-B22D-611D13254D4C}</ProjectGuid>
<RootNamespace>$(MSBuildProjectName)</RootNamespace>
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
- <SampleGuid>{4DEE9F36-71EC-4057-8531-96C8B4624E91}</SampleGuid>
+ <SampleGuid>{8FEDC4BC-EFA4-4BF4-91B6-E33FA555EB15}</SampleGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -36,12 +36,15 @@
<ConfigurationType>Driver</ConfigurationType>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <TargetVersion>Windows10</TargetVersion>
+ <TargetVersion>
+ </TargetVersion>
<UseDebugLibraries>True</UseDebugLibraries>
- <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverTargetPlatform>
+ </DriverTargetPlatform>
<DriverType>KMDF</DriverType>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
+ <SupportsPackaging>false</SupportsPackaging>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<TargetVersion>Windows10</TargetVersion>
@@ -98,6 +101,10 @@
<PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
<ExceptionHandling>
</ExceptionHandling>
+ <WppEnabled>true</WppEnabled>
+ <WppTraceFunction>DoTraceLevelMessage(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppModuleName>StmEdit</WppModuleName>
+ <WppScanConfigurationData>Trace.h</WppScanConfigurationData>
</ClCompile>
<Midl>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
@@ -117,6 +124,10 @@
<PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
<ExceptionHandling>
</ExceptionHandling>
+ <WppEnabled>true</WppEnabled>
+ <WppTraceFunction>DoTraceLevelMessage(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppModuleName>StmEdit</WppModuleName>
+ <WppScanConfigurationData>Trace.h</WppScanConfigurationData>
</ClCompile>
<Midl>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
@@ -136,6 +147,10 @@
<PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
<ExceptionHandling>
</ExceptionHandling>
+ <WppTraceFunction>DoTraceLevelMessage(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppEnabled>true</WppEnabled>
+ <WppModuleName>StmEdit</WppModuleName>
+ <WppScanConfigurationData>Trace.h</WppScanConfigurationData>
</ClCompile>
<Midl>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
@@ -155,6 +170,10 @@
<PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
<ExceptionHandling>
</ExceptionHandling>
+ <WppEnabled>true</WppEnabled>
+ <WppTraceFunction>DoTraceLevelMessage(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppModuleName>StmEdit</WppModuleName>
+ <WppScanConfigurationData>Trace.h</WppScanConfigurationData>
</ClCompile>
<Midl>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
@@ -165,9 +184,10 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
- <ClCompile Include="inline_edit.c" />
- <ClCompile Include="oob_edit.c" />
- <ClCompile Include="stream_callout.c" />
+ <ClCompile Include="InlineEdit.c" />
+ <ClCompile Include="LwQueue.c" />
+ <ClCompile Include="OobEdit.c" />
+ <ClCompile Include="StreamEdit.c" />
</ItemGroup>
<ItemGroup>
<Inf Exclude="@(Inf)" Include="*.inf" />
diff --git a/network/trans/stmedit/sys/stmedit.vcxproj.Filters b/network/trans/stmedit/sys/stmedit.vcxproj.Filters
index 671a946a..9a0c9d49 100644
--- a/network/trans/stmedit/sys/stmedit.vcxproj.Filters
+++ b/network/trans/stmedit/sys/stmedit.vcxproj.Filters
@@ -3,30 +3,55 @@
<ItemGroup>
<Filter Include="Source Files">
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
- <UniqueIdentifier>{4EB0EC22-2845-4C9E-B03A-227073F198E7}</UniqueIdentifier>
+ <UniqueIdentifier>{B982E3FA-FE5E-446B-BF39-65C533B117A9}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files">
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
- <UniqueIdentifier>{3953E48D-12FA-4B3E-ABA3-BEE2AFF7C284}</UniqueIdentifier>
+ <UniqueIdentifier>{92648371-83D5-4CCE-AA54-9B689B706227}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
- <UniqueIdentifier>{D5A362D5-FA75-4E10-9D64-22B6CCB57052}</UniqueIdentifier>
+ <UniqueIdentifier>{163FDC8F-1EFF-4545-99A5-B189C0A6F18A}</UniqueIdentifier>
</Filter>
<Filter Include="Driver Files">
<Extensions>inf;inv;inx;mof;mc;</Extensions>
- <UniqueIdentifier>{9B77DAC7-BF0A-4BAD-B6E0-C663693F98BB}</UniqueIdentifier>
+ <UniqueIdentifier>{E7E65CD5-C0F5-4962-AAB1-B9A9BA383DD9}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
- <ClCompile Include="inline_edit.c">
+ <ClCompile Include="InlineEdit.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="oob_edit.c">
+ <ClCompile Include="LwQueue.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="stream_callout.c">
+ <ClCompile Include="OobEdit.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="StreamEdit.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="LwQueue.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StreamEdit.h" />
+ <ClInclude Include="Trace.h" />
+ <ClInclude Include="LwQueue.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StreamEdit.h" />
+ <ClInclude Include="Trace.h" />
+ <ClInclude Include="LwQueue.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StreamEdit.h" />
+ <ClInclude Include="Trace.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Include="*.inf">
+ <Filter>Driver Files</Filter>
+ </Inf>
</ItemGroup>
</Project> \ No newline at end of file
diff --git a/network/trans/stmedit/sys/stream_callout.c b/network/trans/stmedit/sys/stream_callout.c
deleted file mode 100644
index 7816c46d..00000000
--- a/network/trans/stmedit/sys/stream_callout.c
+++ /dev/null
@@ -1,772 +0,0 @@
-/*++
-
-Copyright (c) Microsoft Corporation. All rights reserved
-
-Abstract:
-
- Stream Edit Callout Driver Sample.
-
- This sample demonstrates finding and replacing a string pattern from a
- live TCP stream via the WFP stream API.
-
- The driver can function in one of the two modes --
-
- o Inline Editing where all modification is carried out within the
- WFP ClassifyFn callout function.
-
- o Out-of-band (OOB) Editing where all modification is done by a
- worker thread. (this is the default)
-
- The mode setting, along with other inspection parameters are configurable
- via the following registry values
-
- HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\stmedit\Parameters
-
- o StringToFind (REG_SZ, default = "rainy")
- o StringToReplace (REG_SZ, default = "sunny")
- o InspectionPort (REG_DWORD, default = 5001)
- o InspectOutbound (REG_DWORD, default = 0)
- o EditInline (REG_DWORD, default = 0)
-
- The sample is IP version agnostic. It performs inspection on both IPv4 and
- IPv6 data streams.
-
- Before experimenting with the sample, please be sure to add an exception for
- the InspectionPort configured to the firewall.
-
-Environment:
-
- Kernel mode
-
---*/
-
-#include <ntifs.h>
-#include <wdf.h>
-
-#pragma warning(push)
-#pragma warning(disable:4201) // unnamed struct/union
-
-#include <fwpsk.h>
-
-#pragma warning(pop)
-
-#include <fwpmk.h>
-
-#include "inline_edit.h"
-#include "oob_edit.h"
-#include "stream_callout.h"
-
-#define INITGUID
-#include <guiddef.h>
-
-//
-// Configurable parameters
-//
-
-USHORT configInspectionPort = 5001;
-BOOLEAN configInspectionOutbound = FALSE;
-BOOLEAN configEditInline = FALSE;
-
-CHAR configStringToFind[128] = "rainy";
-CHAR configStringToReplace[128] = "sunny";
-
-//
-// Callout driver keys
-//
-
-// e6011cdc-440b-4a6f-8499-6fdb55fb1f92
-DEFINE_GUID(
- STREAM_EDITOR_STREAM_CALLOUT_V4,
- 0xe6011cdc,
- 0x440b,
- 0x4a6f,
- 0x84, 0x99, 0x6f, 0xdb, 0x55, 0xfb, 0x1f, 0x92
-);
-// c0bc07b4-aaf6-4242-a3dc-3ef341ffde5d
-DEFINE_GUID(
- STREAM_EDITOR_STREAM_CALLOUT_V6,
- 0xc0bc07b4,
- 0xaaf6,
- 0x4242,
- 0xa3, 0xdc, 0x3e, 0xf3, 0x41, 0xff, 0xde, 0x5d
-);
-
-//
-// Callout driver global variables
-//
-
-MDL* gStringToReplaceMdl;
-
-STREAM_EDITOR gStreamEditor;
-
-HANDLE gEngineHandle;
-UINT32 gCalloutIdV4;
-UINT32 gCalloutIdV6;
-
-DEVICE_OBJECT* gWdmDevice;
-
-HANDLE gInjectionHandle;
-
-NDIS_GENERIC_OBJECT* gNdisGenericObj;
-NDIS_HANDLE gNetBufferListPool;
-
-#define STREAM_EDITOR_NDIS_OBJ_TAG 'oneS'
-#define STREAM_EDITOR_NBL_POOL_TAG 'pneS'
-#define STREAM_EDITOR_FLAT_BUFFER_TAG 'bfeS'
-
-
-DRIVER_INITIALIZE DriverEntry;
-EVT_WDF_DRIVER_UNLOAD StreamEditEvtDriverUnload;
-
-NTSTATUS
-StreamEditNotify(
- FWPS_CALLOUT_NOTIFY_TYPE notifyType,
- const GUID* filterKey,
- const FWPS_FILTER* filter
- )
-{
- UNREFERENCED_PARAMETER(notifyType);
- UNREFERENCED_PARAMETER(filterKey);
- UNREFERENCED_PARAMETER(filter);
-
- return STATUS_SUCCESS;
-}
-
-NTSTATUS
-RegisterCalloutForLayer(
- const GUID* layerKey,
- const GUID* calloutKey,
- _Inout_ void* deviceObject,
- _Out_ UINT32* calloutId
- )
-/* ++
-
- This function registers callouts and filters that intercept TCP
- traffic at WFP FWPM_LAYER_STREAM_V4 or FWPM_LAYER_STREAM_V6 layer.
-
--- */
-{
- NTSTATUS status = STATUS_SUCCESS;
-
- FWPS_CALLOUT sCallout = {0};
-
- FWPM_FILTER filter = {0};
- FWPM_FILTER_CONDITION filterConditions[1] = {0};
-
- FWPM_CALLOUT mCallout = {0};
- FWPM_DISPLAY_DATA displayData = {0};
-
- BOOLEAN calloutRegistered = FALSE;
-
- sCallout.calloutKey = *calloutKey;
- sCallout.classifyFn = (configEditInline ? StreamInlineEditClassify :
- StreamOobEditClassify);
- sCallout.notifyFn = StreamEditNotify;
-
- status = FwpsCalloutRegister(
- deviceObject,
- &sCallout,
- calloutId
- );
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
- calloutRegistered = TRUE;
-
- displayData.name = L"Stream Edit Callout";
- displayData.description = L"Callout that finds and replaces a token from a TCP stream";
-
- mCallout.calloutKey = *calloutKey;
- mCallout.displayData = displayData;
- mCallout.applicableLayer = *layerKey;
- status = FwpmCalloutAdd(
- gEngineHandle,
- &mCallout,
- NULL,
- NULL
- );
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- filter.layerKey = *layerKey;
- filter.displayData.name = L"Stream Edit Filter";
- filter.displayData.description = L"Filter that finds and replaces a token from a TCP stream";
-
- filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
- filter.action.calloutKey = *calloutKey;
- filter.filterCondition = filterConditions;
- filter.numFilterConditions = 1;
- filter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL;
- filter.weight.type = FWP_EMPTY; // auto-weight.
-
- filterConditions[0].fieldKey = (configInspectionOutbound ? FWPM_CONDITION_IP_REMOTE_PORT :
- FWPM_CONDITION_IP_LOCAL_PORT);
- filterConditions[0].matchType = FWP_MATCH_EQUAL;
- filterConditions[0].conditionValue.type = FWP_UINT16;
- filterConditions[0].conditionValue.uint16 = configInspectionPort;
-
- status = FwpmFilterAdd(
- gEngineHandle,
- &filter,
- NULL,
- NULL);
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
-Exit:
-
- if (!NT_SUCCESS(status))
- {
- if (calloutRegistered)
- {
- FwpsCalloutUnregisterById(*calloutId);
- }
- }
-
- return status;
-}
-
-NTSTATUS
-StreamEditRegisterCallout(
- const STREAM_EDITOR* streamEditor,
- _Inout_ void* deviceObject
- )
-/* ++
-
- This function registers dynamic callouts and filters that intercept
- TCP traffic at WFP FWPM_LAYER_STREAM_V4 and FWPM_LAYER_STREAM_V6
- layer.
-
- Callouts and filters will be removed during DriverUnload.
-
--- */
-{
- NTSTATUS status = STATUS_SUCCESS;
-
- BOOLEAN engineOpened = FALSE;
- BOOLEAN inTransaction = FALSE;
-
- FWPM_SESSION session = {0};
-
- UNREFERENCED_PARAMETER(streamEditor);
-
- session.flags = FWPM_SESSION_FLAG_DYNAMIC;
-
- status = FwpmEngineOpen(
- NULL,
- RPC_C_AUTHN_WINNT,
- NULL,
- &session,
- &gEngineHandle
- );
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
- engineOpened = TRUE;
-
- status = FwpmTransactionBegin(gEngineHandle, 0);
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
- inTransaction = TRUE;
-
- status = RegisterCalloutForLayer(
- &FWPM_LAYER_STREAM_V4,
- &STREAM_EDITOR_STREAM_CALLOUT_V4,
- deviceObject,
- &gCalloutIdV4
- );
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- status = RegisterCalloutForLayer(
- &FWPM_LAYER_STREAM_V6,
- &STREAM_EDITOR_STREAM_CALLOUT_V6,
- deviceObject,
- &gCalloutIdV6
- );
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- status = FwpmTransactionCommit(gEngineHandle);
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
- inTransaction = FALSE;
-
-Exit:
-
- if (!NT_SUCCESS(status))
- {
- if (inTransaction)
- {
- NTSTATUS abortStatus;
- abortStatus = FwpmTransactionAbort(gEngineHandle);
- _Analysis_assume_(NT_SUCCESS(abortStatus));
- }
- if (engineOpened)
- {
- FwpmEngineClose(gEngineHandle);
- gEngineHandle = NULL;
- }
- }
-
- return status;
-}
-
-void
-StreamEditUnregisterCallout(void)
-{
- FwpmEngineClose(gEngineHandle);
- gEngineHandle = NULL;
-
- FwpsCalloutUnregisterById(gCalloutIdV6);
- FwpsCalloutUnregisterById(gCalloutIdV4);
-}
-
-_Function_class_(EVT_WDF_DRIVER_UNLOAD)
-_IRQL_requires_same_
-_IRQL_requires_max_(PASSIVE_LEVEL)
-void
-StreamEditEvtDriverUnload(
- _In_ WDFDRIVER driverObject
- )
-{
-
- UNREFERENCED_PARAMETER(driverObject);
-
- if (!configEditInline)
- {
- OobEditShutdown(&gStreamEditor);
- }
-
- if (gStreamEditor.scratchBuffer != NULL)
- {
- ExFreePoolWithTag(
- gStreamEditor.scratchBuffer,
- STREAM_EDITOR_FLAT_BUFFER_TAG
- );
-
- }
-
- StreamEditUnregisterCallout();
-
- FwpsInjectionHandleDestroy(gInjectionHandle);
-
- NdisFreeNetBufferListPool(gNetBufferListPool);
- NdisFreeGenericObject(gNdisGenericObj);
-
- IoFreeMdl(gStringToReplaceMdl);
-}
-
-NTSTATUS
-StreamEditLoadConfig(
- const WDFKEY key
- )
-{
- NTSTATUS status = STATUS_SUCCESS;
- DECLARE_CONST_UNICODE_STRING(stringToFindKey, L"StringToFind");
- DECLARE_CONST_UNICODE_STRING(stringToReplaceKey, L"StringToReplace");
- DECLARE_CONST_UNICODE_STRING(inspectionPortKey, L"InspectionPort");
- DECLARE_CONST_UNICODE_STRING(editInlineKey, L"EditInline");
- DECLARE_CONST_UNICODE_STRING(inspectOutboundKey, L"InspectOutbound");
-
- UNICODE_STRING stringValue;
- WCHAR buffer[128];
- USHORT requiredSize;
- ULONG valueSize;
- ULONG ulongValue;
-
- stringValue.Buffer = buffer;
- stringValue.Length = 0;
- stringValue.MaximumLength = sizeof(buffer) - sizeof(buffer[0]);
-
- if (NT_SUCCESS(
- WdfRegistryQueryUnicodeString(
- key,
- &stringToFindKey,
- &requiredSize,
- &stringValue
- )))
- {
- stringValue.Buffer[stringValue.Length/sizeof(stringValue.Buffer[0])] =
- UNICODE_NULL;
-
- status = RtlUnicodeToMultiByteN(
- configStringToFind,
- sizeof(configStringToFind) - 1,
- &valueSize,
- stringValue.Buffer,
- (ULONG)requiredSize
- );
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- configStringToFind[valueSize] = '\0';
- }
-
- if (NT_SUCCESS(
- WdfRegistryQueryUnicodeString(
- key,
- &stringToReplaceKey,
- &requiredSize,
- &stringValue
- )))
- {
- status = RtlUnicodeToMultiByteN(
- configStringToReplace,
- sizeof(configStringToReplace) - 1,
- &valueSize,
- stringValue.Buffer,
- (ULONG)requiredSize
- );
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- configStringToReplace[valueSize] = '\0';
- }
-
- if (NT_SUCCESS(
- WdfRegistryQueryULong(
- key,
- &inspectionPortKey,
- &ulongValue
- )))
- {
- configInspectionPort = (USHORT) ulongValue;
- }
-
- if (NT_SUCCESS(
- WdfRegistryQueryULong(
- key,
- &editInlineKey,
- &ulongValue
- )))
- {
- configEditInline = (ulongValue != 0);
- }
-
- if (NT_SUCCESS(
- WdfRegistryQueryULong(
- key,
- &inspectOutboundKey,
- &ulongValue
- )))
- {
- configInspectionOutbound = (ulongValue != 0);
- }
-
-Exit:
- return status;
-}
-
-NTSTATUS
-StreamEditInitDriverObjects(
- _Inout_ DRIVER_OBJECT* driverObject,
- const UNICODE_STRING* registryPath,
- _Out_ WDFDRIVER* pDriver,
- _Out_ WDFDEVICE* pDevice
- )
-{
- NTSTATUS status;
- WDF_DRIVER_CONFIG config;
- PWDFDEVICE_INIT pInit = NULL;
-
- WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
-
- config.DriverInitFlags |= WdfDriverInitNonPnpDriver;
- config.EvtDriverUnload = StreamEditEvtDriverUnload;
-
- status = WdfDriverCreate(
- driverObject,
- registryPath,
- WDF_NO_OBJECT_ATTRIBUTES,
- &config,
- pDriver
- );
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- pInit = WdfControlDeviceInitAllocate(*pDriver, &SDDL_DEVOBJ_KERNEL_ONLY);
-
- if (!pInit)
- {
- status = STATUS_INSUFFICIENT_RESOURCES;
- goto Exit;
- }
-
- WdfDeviceInitSetCharacteristics(pInit, FILE_AUTOGENERATED_DEVICE_NAME, TRUE);
- WdfDeviceInitSetDeviceType(pInit, FILE_DEVICE_NETWORK);
- WdfDeviceInitSetCharacteristics(pInit, FILE_DEVICE_SECURE_OPEN, TRUE);
- status = WdfDeviceCreate(&pInit, WDF_NO_OBJECT_ATTRIBUTES, pDevice);
-
- if (!NT_SUCCESS(status))
- {
- WdfDeviceInitFree(pInit);
- goto Exit;
- }
-
- WdfControlFinishInitializing(*pDevice);
-
-Exit:
- return status;
-}
-
-NTSTATUS
-DriverEntry(
- DRIVER_OBJECT* driverObject,
- UNICODE_STRING* registryPath
- )
-{
- NTSTATUS status;
- WDFDEVICE device;
- WDFDRIVER driver;
- WDFKEY configKey;
- NET_BUFFER_LIST_POOL_PARAMETERS nblPoolParams = {0};
-
- // Request NX Non-Paged Pool when available
- ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
-
- status = StreamEditInitDriverObjects(
- driverObject,
- registryPath,
- &driver,
- &device
- );
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- status = WdfDriverOpenParametersRegistryKey(
- driver,
- KEY_READ,
- WDF_NO_OBJECT_ATTRIBUTES,
- &configKey
- );
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- status = StreamEditLoadConfig(configKey);
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- gStringToReplaceMdl = IoAllocateMdl(
- configStringToReplace,
- (ULONG) strlen(configStringToReplace),
- FALSE,
- FALSE,
- NULL
- );
- if (gStringToReplaceMdl == NULL)
- {
- status = STATUS_NO_MEMORY;
- goto Exit;
- }
-
- MmBuildMdlForNonPagedPool(gStringToReplaceMdl);
-
- gNdisGenericObj = NdisAllocateGenericObject(
- driverObject,
- STREAM_EDITOR_NDIS_OBJ_TAG,
- 0
- );
-
- if (gNdisGenericObj == NULL)
- {
- status = STATUS_NO_MEMORY;
- goto Exit;
- }
-
- nblPoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
- nblPoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
- nblPoolParams.Header.Size = sizeof(nblPoolParams);
-
- nblPoolParams.fAllocateNetBuffer = TRUE;
- nblPoolParams.DataSize = 0;
-
- nblPoolParams.PoolTag = STREAM_EDITOR_NBL_POOL_TAG;
-
- gNetBufferListPool = NdisAllocateNetBufferListPool(
- gNdisGenericObj,
- &nblPoolParams
- );
-
- if (gNetBufferListPool == NULL)
- {
- status = STATUS_NO_MEMORY;
- goto Exit;
- }
-
- status = FwpsInjectionHandleCreate(
- AF_UNSPEC,
- FWPS_INJECTION_TYPE_STREAM,
- &gInjectionHandle
- );
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- gWdmDevice = WdfDeviceWdmGetDeviceObject(device);
-
- status = StreamEditRegisterCallout(
- &gStreamEditor,
- gWdmDevice
- );
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
-
- if (configEditInline)
- {
- InlineEditInit(&gStreamEditor);
- }
- else
- {
-
- status = OobEditInit(&gStreamEditor);
-
- if (!NT_SUCCESS(status))
- {
- goto Exit;
- }
- }
-
-Exit:
-
- if (!NT_SUCCESS(status))
- {
- if (gEngineHandle != NULL)
- {
- StreamEditUnregisterCallout();
- }
- if (gInjectionHandle != NULL)
- {
- FwpsInjectionHandleDestroy(gInjectionHandle);
- }
- if (gNetBufferListPool != NULL)
- {
- NdisFreeNetBufferListPool(gNetBufferListPool);
- }
- if (gNdisGenericObj != NULL)
- {
- NdisFreeGenericObject(gNdisGenericObj);
- }
- if (gStringToReplaceMdl != NULL)
- {
- IoFreeMdl(gStringToReplaceMdl);
- }
- }
-
- return status;
-}
-
-BOOLEAN
-StreamCopyDataForInspection(
- _Inout_ STREAM_EDITOR* streamEditor,
- const FWPS_STREAM_DATA* streamData
- )
-/* ++
-
- This function copies stream data described by the FWPS_STREAM_DATA
- structure into a flat buffer.
-
--- */
-
-{
- SIZE_T bytesCopied;
-
- size_t existingDataLength = streamEditor->dataLength;
- NT_ASSERT(streamEditor->dataOffset == 0);
- if (streamEditor->bufferSize - existingDataLength < streamData->dataLength)
- {
- size_t newBufferSize = (streamData->dataLength + existingDataLength) * 2;
- void* newBuffer = ExAllocatePoolWithTag(
- NonPagedPool,
- newBufferSize,
- STREAM_EDITOR_FLAT_BUFFER_TAG
- );
-
- if (newBuffer != NULL)
- {
- if (existingDataLength > 0)
- {
- NT_ASSERT(streamEditor->scratchBuffer != NULL);
- RtlCopyMemory(
- newBuffer,
- streamEditor->scratchBuffer,
- existingDataLength
- );
- }
- }
-
- if (streamEditor->scratchBuffer != NULL)
- {
- ExFreePoolWithTag(
- streamEditor->scratchBuffer,
- STREAM_EDITOR_FLAT_BUFFER_TAG
- );
-
- streamEditor->scratchBuffer = NULL;
- streamEditor->bufferSize = 0;
- streamEditor->dataLength = 0;
- }
-
- if (newBuffer != NULL)
- {
- streamEditor->scratchBuffer = newBuffer;
- streamEditor->bufferSize = newBufferSize;
- streamEditor->dataLength = existingDataLength;
- }
- else
- {
- return FALSE;
- }
- }
-
- FwpsCopyStreamDataToBuffer(
- streamData,
- (BYTE*)streamEditor->scratchBuffer + streamEditor->dataLength,
- streamData->dataLength,
- &bytesCopied
- );
-
- NT_ASSERT(bytesCopied == streamData->dataLength);
-
- streamEditor->dataLength += bytesCopied;
-
- return TRUE;
-}
diff --git a/network/trans/stmedit/sys/stream_callout.h b/network/trans/stmedit/sys/stream_callout.h
deleted file mode 100644
index 7fff047f..00000000
--- a/network/trans/stmedit/sys/stream_callout.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*++
-
-Copyright (c) Microsoft Corporation. All rights reserved
-
-Abstract:
-
- Stream Edit Callout Driver Sample.
-
- This sample demonstrates finding and replacing a string pattern from a
- live TCP stream via the WFP stream API.
-
---*/
-
-#ifndef _STREAM_CALLOUT_H
-#define _STREAM_CALLOUT_H
-
-extern MDL* gStringToReplaceMdl;
-extern HANDLE gInjectionHandle;
-extern NDIS_HANDLE gNetBufferListPool;
-extern STREAM_EDITOR gStreamEditor;
-
-//
-// Configurable parameters
-//
-
-extern USHORT configInspectionPort;
-extern BOOLEAN configInspectionOutbound;
-extern BOOLEAN configEditInline;
-
-extern CHAR configStringToFind[];
-extern CHAR configStringToReplace[];
-
-#pragma warning(push)
-#pragma warning(disable:4201) // unnamed struct/union
-
-typedef struct STREAM_EDITOR_
-{
- BOOLEAN editInline;
-
- union
- {
- INLINE_EDIT_STATE inlineEditState;
- struct
- {
- OOB_EDIT_STATE editState;
- BOOLEAN shuttingDown;
-
- KSPIN_LOCK editLock;
- NET_BUFFER_LIST* nblHead;
- NET_BUFFER_LIST* nblTail;
- size_t totalDataLength;
- BOOLEAN noMoreData;
- NET_BUFFER_LIST* nblEof;
- size_t busyThreshold;
- UINT64 flowId;
- UINT32 calloutId;
- UINT16 layerId;
- DWORD streamFlags;
- KEVENT editEvent;
- LIST_ENTRY outgoingDataQueue;
- } oobEditInfo;
- };
-
- void* scratchBuffer;
- size_t bufferSize;
- size_t dataOffset;
- size_t dataLength;
-
-}STREAM_EDITOR;
-
-#pragma warning(pop)
-
-BOOLEAN
-StreamCopyDataForInspection(
- _Inout_ STREAM_EDITOR* streamEditor,
- const FWPS_STREAM_DATA* streamData
- );
-
-#endif // _STREAM_CALLOUT_H