summaryrefslogtreecommitdiffhomepage
path: root/windows
diff options
context:
space:
mode:
Diffstat (limited to 'windows')
-rw-r--r--windows/winfw/extras.sln73
-rw-r--r--windows/winfw/src/extras/cli/cli.cpp173
-rw-r--r--windows/winfw/src/extras/cli/cli.vcxproj230
-rw-r--r--windows/winfw/src/extras/cli/cli.vcxproj.filters127
-rw-r--r--windows/winfw/src/extras/cli/commands/icommand.h17
-rw-r--r--windows/winfw/src/extras/cli/commands/list/events.cpp51
-rw-r--r--windows/winfw/src/extras/cli/commands/list/events.h25
-rw-r--r--windows/winfw/src/extras/cli/commands/list/filters.cpp51
-rw-r--r--windows/winfw/src/extras/cli/commands/list/filters.h25
-rw-r--r--windows/winfw/src/extras/cli/commands/list/layers.cpp51
-rw-r--r--windows/winfw/src/extras/cli/commands/list/layers.h25
-rw-r--r--windows/winfw/src/extras/cli/commands/list/providercontexts.cpp51
-rw-r--r--windows/winfw/src/extras/cli/commands/list/providercontexts.h25
-rw-r--r--windows/winfw/src/extras/cli/commands/list/providers.cpp48
-rw-r--r--windows/winfw/src/extras/cli/commands/list/providers.h25
-rw-r--r--windows/winfw/src/extras/cli/commands/list/sessions.cpp48
-rw-r--r--windows/winfw/src/extras/cli/commands/list/sessions.h25
-rw-r--r--windows/winfw/src/extras/cli/commands/list/sublayers.cpp51
-rw-r--r--windows/winfw/src/extras/cli/commands/list/sublayers.h25
-rw-r--r--windows/winfw/src/extras/cli/commands/monitor/m_events.cpp68
-rw-r--r--windows/winfw/src/extras/cli/commands/monitor/m_events.h28
-rw-r--r--windows/winfw/src/extras/cli/commands/winfw/deinit.cpp35
-rw-r--r--windows/winfw/src/extras/cli/commands/winfw/deinit.h25
-rw-r--r--windows/winfw/src/extras/cli/commands/winfw/init.cpp54
-rw-r--r--windows/winfw/src/extras/cli/commands/winfw/init.h28
-rw-r--r--windows/winfw/src/extras/cli/commands/winfw/policy.cpp170
-rw-r--r--windows/winfw/src/extras/cli/commands/winfw/policy.h35
-rw-r--r--windows/winfw/src/extras/cli/filterengineprovider.h39
-rw-r--r--windows/winfw/src/extras/cli/inlineformatter.h30
-rw-r--r--windows/winfw/src/extras/cli/ipropertydecorator.h32
-rw-r--r--windows/winfw/src/extras/cli/modules/imodule.h19
-rw-r--r--windows/winfw/src/extras/cli/modules/list.h33
-rw-r--r--windows/winfw/src/extras/cli/modules/module.cpp70
-rw-r--r--windows/winfw/src/extras/cli/modules/module.h50
-rw-r--r--windows/winfw/src/extras/cli/modules/monitor.h21
-rw-r--r--windows/winfw/src/extras/cli/modules/winfw.h25
-rw-r--r--windows/winfw/src/extras/cli/objectproperties.cpp709
-rw-r--r--windows/winfw/src/extras/cli/objectproperties.h15
-rw-r--r--windows/winfw/src/extras/cli/propertydecorator.cpp92
-rw-r--r--windows/winfw/src/extras/cli/propertydecorator.h27
-rw-r--r--windows/winfw/src/extras/cli/propertylist.h55
-rw-r--r--windows/winfw/src/extras/cli/stdafx.cpp8
-rw-r--r--windows/winfw/src/extras/cli/stdafx.h15
-rw-r--r--windows/winfw/src/extras/cli/subcommanddispatcher.cpp27
-rw-r--r--windows/winfw/src/extras/cli/subcommanddispatcher.h20
-rw-r--r--windows/winfw/src/extras/cli/targetver.h12
-rw-r--r--windows/winfw/src/extras/cli/util.cpp62
-rw-r--r--windows/winfw/src/extras/cli/util.h20
-rw-r--r--windows/winfw/src/winfw/dllmain.cpp11
-rw-r--r--windows/winfw/src/winfw/fwcontext.cpp164
-rw-r--r--windows/winfw/src/winfw/fwcontext.h35
-rw-r--r--windows/winfw/src/winfw/iobjectinstaller.h17
-rw-r--r--windows/winfw/src/winfw/mullvadguids.cpp380
-rw-r--r--windows/winfw/src/winfw/mullvadguids.h45
-rw-r--r--windows/winfw/src/winfw/mullvadobjects.cpp46
-rw-r--r--windows/winfw/src/winfw/mullvadobjects.h18
-rw-r--r--windows/winfw/src/winfw/rules/blockall.cpp73
-rw-r--r--windows/winfw/src/winfw/rules/blockall.h18
-rw-r--r--windows/winfw/src/winfw/rules/ifirewallrule.h25
-rw-r--r--windows/winfw/src/winfw/rules/permitdhcp.cpp74
-rw-r--r--windows/winfw/src/winfw/rules/permitdhcp.h18
-rw-r--r--windows/winfw/src/winfw/rules/permitlan.cpp96
-rw-r--r--windows/winfw/src/winfw/rules/permitlan.h18
-rw-r--r--windows/winfw/src/winfw/rules/permitlanservice.cpp76
-rw-r--r--windows/winfw/src/winfw/rules/permitlanservice.h18
-rw-r--r--windows/winfw/src/winfw/rules/permitloopback.cpp96
-rw-r--r--windows/winfw/src/winfw/rules/permitloopback.h18
-rw-r--r--windows/winfw/src/winfw/rules/permitvpnrelay.cpp80
-rw-r--r--windows/winfw/src/winfw/rules/permitvpnrelay.h30
-rw-r--r--windows/winfw/src/winfw/rules/permitvpntunnel.cpp62
-rw-r--r--windows/winfw/src/winfw/rules/permitvpntunnel.h22
-rw-r--r--windows/winfw/src/winfw/rules/restrictdns.cpp148
-rw-r--r--windows/winfw/src/winfw/rules/restrictdns.h24
-rw-r--r--windows/winfw/src/winfw/sessioncontroller.cpp248
-rw-r--r--windows/winfw/src/winfw/sessioncontroller.h55
-rw-r--r--windows/winfw/src/winfw/sessionrecord.cpp58
-rw-r--r--windows/winfw/src/winfw/sessionrecord.h37
-rw-r--r--windows/winfw/src/winfw/stdafx.cpp8
-rw-r--r--windows/winfw/src/winfw/stdafx.h16
-rw-r--r--windows/winfw/src/winfw/targetver.h12
-rw-r--r--windows/winfw/src/winfw/winfw.cpp208
-rw-r--r--windows/winfw/src/winfw/winfw.def9
-rw-r--r--windows/winfw/src/winfw/winfw.h150
-rw-r--r--windows/winfw/src/winfw/winfw.vcxproj226
-rw-r--r--windows/winfw/src/winfw/winfw.vcxproj.filters83
-rw-r--r--windows/winfw/winfw.sln58
86 files changed, 5750 insertions, 0 deletions
diff --git a/windows/winfw/extras.sln b/windows/winfw/extras.sln
new file mode 100644
index 0000000000..948a3f9104
--- /dev/null
+++ b/windows/winfw/extras.sln
@@ -0,0 +1,73 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27130.2027
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cli", "src\extras\cli\cli.vcxproj", "{242A58CA-8E54-4170-A8C5-6F91CC4F7DB2}"
+ ProjectSection(ProjectDependencies) = postProject
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF}
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B} = {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB} = {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcommon", "..\windows-libraries\src\libcommon\libcommon.vcxproj", "{B52E2D10-A94A-4605-914A-2DCEF6A757EF}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwfp", "..\libwfp\src\libwfp\libwfp.vcxproj", "{2164E6D9-6023-4932-A08F-7A5C15E2CA0B}"
+ ProjectSection(ProjectDependencies) = postProject
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winfw", "src\winfw\winfw.vcxproj", "{801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}"
+ ProjectSection(ProjectDependencies) = postProject
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF}
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B} = {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {242A58CA-8E54-4170-A8C5-6F91CC4F7DB2}.Debug|x64.ActiveCfg = Debug|x64
+ {242A58CA-8E54-4170-A8C5-6F91CC4F7DB2}.Debug|x64.Build.0 = Debug|x64
+ {242A58CA-8E54-4170-A8C5-6F91CC4F7DB2}.Debug|x86.ActiveCfg = Debug|Win32
+ {242A58CA-8E54-4170-A8C5-6F91CC4F7DB2}.Debug|x86.Build.0 = Debug|Win32
+ {242A58CA-8E54-4170-A8C5-6F91CC4F7DB2}.Release|x64.ActiveCfg = Release|x64
+ {242A58CA-8E54-4170-A8C5-6F91CC4F7DB2}.Release|x64.Build.0 = Release|x64
+ {242A58CA-8E54-4170-A8C5-6F91CC4F7DB2}.Release|x86.ActiveCfg = Release|Win32
+ {242A58CA-8E54-4170-A8C5-6F91CC4F7DB2}.Release|x86.Build.0 = Release|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x64.ActiveCfg = Debug|x64
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x64.Build.0 = Debug|x64
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x86.ActiveCfg = Debug|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x86.Build.0 = Debug|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x64.ActiveCfg = Release|x64
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x64.Build.0 = Release|x64
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x86.ActiveCfg = Release|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x86.Build.0 = Release|Win32
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Debug|x64.ActiveCfg = Debug|x64
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Debug|x64.Build.0 = Debug|x64
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Debug|x86.ActiveCfg = Debug|Win32
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Debug|x86.Build.0 = Debug|Win32
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Release|x64.ActiveCfg = Release|x64
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Release|x64.Build.0 = Release|x64
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Release|x86.ActiveCfg = Release|Win32
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Release|x86.Build.0 = Release|Win32
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Debug|x64.ActiveCfg = Debug|x64
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Debug|x64.Build.0 = Debug|x64
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Debug|x86.ActiveCfg = Debug|Win32
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Debug|x86.Build.0 = Debug|Win32
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Release|x64.ActiveCfg = Release|x64
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Release|x64.Build.0 = Release|x64
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Release|x86.ActiveCfg = Release|Win32
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {73987DE0-E127-433C-B3DA-1EEA0654FA6C}
+ EndGlobalSection
+EndGlobal
diff --git a/windows/winfw/src/extras/cli/cli.cpp b/windows/winfw/src/extras/cli/cli.cpp
new file mode 100644
index 0000000000..a1758df8ee
--- /dev/null
+++ b/windows/winfw/src/extras/cli/cli.cpp
@@ -0,0 +1,173 @@
+// cli.cpp : Defines the entry point for the console application.
+//
+
+#include "stdafx.h"
+#include "util.h"
+#include "filterengineprovider.h"
+#include "modules/imodule.h"
+#include "modules/list.h"
+#include "modules/monitor.h"
+#include "modules/winfw.h"
+#include "libcommon/string.h"
+#include <iostream>
+#include <conio.h>
+
+// Should set key comparison operator to compare lowercase strings but meh.
+std::map<std::wstring, std::unique_ptr<modules::IModule> > g_modules;
+
+void OutputConsole(const std::wstring &str)
+{
+ std::wcout << str.c_str() << std::endl;
+}
+
+void InitializeFilterEngine()
+{
+ FilterEngineProvider::Instance().set(std::move(wfp::FilterEngine::DynamicSession()));
+}
+
+void InitializeModules()
+{
+ auto list = std::make_unique<modules::List>(OutputConsole);
+ g_modules.insert(std::make_pair(common::string::Lower(list->name()), std::move(list)));
+
+ auto monitor = std::make_unique<modules::Monitor>(OutputConsole);
+ g_modules.insert(std::make_pair(common::string::Lower(monitor->name()), std::move(monitor)));
+
+ auto winfw = std::make_unique<modules::WinFw>(OutputConsole);
+ g_modules.insert(std::make_pair(common::string::Lower(winfw->name()), std::move(winfw)));
+}
+
+void ProcessHelp(const std::wstring &request)
+{
+ auto tokens = common::string::Tokenize(request, L" ");
+
+ if (tokens.empty())
+ {
+ std::wcout << L"Unable to interpret request." << std::endl;
+ return;
+ }
+
+ if (tokens.size() == 1)
+ {
+ PropertyList list;
+
+ for (auto &module : g_modules)
+ {
+ list.add(common::string::Lower(module.second->name()), module.second->description());
+ }
+
+ list.add(L"help", L"List available modules.");
+ list.add(L"help /module/", L"Show module specific help.");
+ list.add(L"reset", L"Reset the Filter Engine session.");
+ list.add(L"quit", L"Exit the application.");
+
+ PrettyPrintOptions options;
+
+ options.indent = 0;
+ options.useSeparator = false;
+
+ PrettyPrintProperties(OutputConsole, options, list);
+
+ return;
+ }
+
+ if (tokens.size() != 2)
+ {
+ std::wcout << L"Unable to interpret request." << std::endl;
+ return;
+ }
+
+ auto wanted = common::string::Lower(tokens[1]);
+ auto found = g_modules.find(wanted);
+
+ if (found == g_modules.end())
+ {
+ std::wcout << L"No such module: " << wanted << L"." << std::endl;
+ return;
+ }
+
+ auto moduleCommands = found->second->commands();
+
+ PrettyPrintOptions options;
+
+ options.indent = 0;
+ options.useSeparator = false;
+
+ PrettyPrintProperties(OutputConsole, options, moduleCommands);
+}
+
+void ProcessRequest(const std::wstring &request)
+{
+ auto tokens = common::string::Tokenize(request, L" ");
+
+ if (tokens.empty())
+ {
+ std::wcout << L"Unable to interpret request." << std::endl;
+ return;
+ }
+
+ auto wanted = common::string::Lower(tokens[0]);
+ auto found = g_modules.find(wanted);
+
+ if (found == g_modules.end())
+ {
+ std::wcout << L"No such module: " << wanted << L"." << std::endl;
+ return;
+ }
+
+ tokens.erase(tokens.begin());
+
+ found->second->handleRequest(tokens);
+}
+
+int main(int, wchar_t **)
+{
+ InitializeFilterEngine();
+ InitializeModules();
+
+ for (;;)
+ {
+ std::wcout << L"wfp> ";
+
+ std::wstring request;
+ std::getline(std::wcin, request);
+
+ if (0 == _wcsicmp(request.c_str(), L"quit"))
+ {
+ break;
+ }
+
+ if (0 == _wcsicmp(request.c_str(), L"reset"))
+ {
+ InitializeFilterEngine();
+ std::wcout << std::endl;
+
+ continue;
+ }
+
+ if (0 == _wcsnicmp(request.c_str(), L"help", 4))
+ {
+ ProcessHelp(request);
+ std::wcout << std::endl;
+
+ continue;
+ }
+
+ try
+ {
+ ProcessRequest(request);
+ }
+ catch (std::exception &error)
+ {
+ std::cout << "Error: " << error.what() << std::endl;
+ }
+ catch (...)
+ {
+ std::cout << "Unknown error, caught exception!" << std::endl;
+ }
+
+ std::wcout << std::endl;
+ }
+
+ return 0;
+}
diff --git a/windows/winfw/src/extras/cli/cli.vcxproj b/windows/winfw/src/extras/cli/cli.vcxproj
new file mode 100644
index 0000000000..93ade5ea56
--- /dev/null
+++ b/windows/winfw/src/extras/cli/cli.vcxproj
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{242A58CA-8E54-4170-A8C5-6F91CC4F7DB2}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>cli</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\;$(ProjectDir)..\..\..\..\libwfp\src\;$(ProjectDir)..\..\..\..\windows-libraries\src\</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>winfw.lib;libcommon.lib;libwfp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\;$(ProjectDir)..\..\..\..\libwfp\src\;$(ProjectDir)..\..\..\..\windows-libraries\src\</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>winfw.lib;libcommon.lib;libwfp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\;$(ProjectDir)..\..\..\..\libwfp\src\;$(ProjectDir)..\..\..\..\windows-libraries\src\</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>winfw.lib;libcommon.lib;libwfp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\;$(ProjectDir)..\..\..\..\libwfp\src\;$(ProjectDir)..\..\..\..\windows-libraries\src\</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>winfw.lib;libcommon.lib;libwfp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="commands\icommand.h" />
+ <ClInclude Include="commands\list\events.h" />
+ <ClInclude Include="commands\list\filters.h" />
+ <ClInclude Include="commands\list\layers.h" />
+ <ClInclude Include="commands\list\providercontexts.h" />
+ <ClInclude Include="commands\list\providers.h" />
+ <ClInclude Include="commands\list\sessions.h" />
+ <ClInclude Include="commands\list\sublayers.h" />
+ <ClInclude Include="commands\monitor\m_events.h" />
+ <ClInclude Include="commands\winfw\deinit.h" />
+ <ClInclude Include="commands\winfw\init.h" />
+ <ClInclude Include="commands\winfw\policy.h" />
+ <ClInclude Include="filterengineprovider.h" />
+ <ClInclude Include="inlineformatter.h" />
+ <ClInclude Include="ipropertydecorator.h" />
+ <ClInclude Include="modules\imodule.h" />
+ <ClInclude Include="modules\list.h" />
+ <ClInclude Include="modules\module.h" />
+ <ClInclude Include="modules\monitor.h" />
+ <ClInclude Include="modules\winfw.h" />
+ <ClInclude Include="objectproperties.h" />
+ <ClInclude Include="propertydecorator.h" />
+ <ClInclude Include="propertylist.h" />
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="subcommanddispatcher.h" />
+ <ClInclude Include="targetver.h" />
+ <ClInclude Include="util.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="cli.cpp" />
+ <ClCompile Include="commands\list\events.cpp" />
+ <ClCompile Include="commands\list\filters.cpp" />
+ <ClCompile Include="commands\list\layers.cpp" />
+ <ClCompile Include="commands\list\providercontexts.cpp" />
+ <ClCompile Include="commands\list\providers.cpp" />
+ <ClCompile Include="commands\list\sessions.cpp" />
+ <ClCompile Include="commands\list\sublayers.cpp" />
+ <ClCompile Include="commands\monitor\m_events.cpp" />
+ <ClCompile Include="commands\winfw\deinit.cpp" />
+ <ClCompile Include="commands\winfw\init.cpp" />
+ <ClCompile Include="commands\winfw\policy.cpp" />
+ <ClCompile Include="modules\module.cpp" />
+ <ClCompile Include="objectproperties.cpp" />
+ <ClCompile Include="propertydecorator.cpp" />
+ <ClCompile Include="stdafx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="subcommanddispatcher.cpp" />
+ <ClCompile Include="util.cpp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/windows/winfw/src/extras/cli/cli.vcxproj.filters b/windows/winfw/src/extras/cli/cli.vcxproj.filters
new file mode 100644
index 0000000000..4f737b5a67
--- /dev/null
+++ b/windows/winfw/src/extras/cli/cli.vcxproj.filters
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="commands">
+ <UniqueIdentifier>{da097053-def0-4a93-a7a3-b62cc761ac4c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="commands\list">
+ <UniqueIdentifier>{25335122-c3ac-492b-bec2-827f73a5269f}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="modules">
+ <UniqueIdentifier>{e88bc8bf-763d-4c21-b558-18c18c3d01aa}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="commands\monitor">
+ <UniqueIdentifier>{ff50eaf3-6e11-41ad-bae9-24427a025d33}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="commands\winfw">
+ <UniqueIdentifier>{571487f4-437d-4ad1-a409-f2b143f6803e}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="modules\imodule.h">
+ <Filter>modules</Filter>
+ </ClInclude>
+ <ClInclude Include="modules\list.h">
+ <Filter>modules</Filter>
+ </ClInclude>
+ <ClInclude Include="commands\icommand.h">
+ <Filter>commands</Filter>
+ </ClInclude>
+ <ClInclude Include="commands\list\sessions.h">
+ <Filter>commands\list</Filter>
+ </ClInclude>
+ <ClInclude Include="modules\module.h">
+ <Filter>modules</Filter>
+ </ClInclude>
+ <ClInclude Include="commands\list\providers.h">
+ <Filter>commands\list</Filter>
+ </ClInclude>
+ <ClInclude Include="commands\list\events.h">
+ <Filter>commands\list</Filter>
+ </ClInclude>
+ <ClInclude Include="commands\list\filters.h">
+ <Filter>commands\list</Filter>
+ </ClInclude>
+ <ClInclude Include="commands\list\layers.h">
+ <Filter>commands\list</Filter>
+ </ClInclude>
+ <ClInclude Include="commands\list\providercontexts.h">
+ <Filter>commands\list</Filter>
+ </ClInclude>
+ <ClInclude Include="commands\list\sublayers.h">
+ <Filter>commands\list</Filter>
+ </ClInclude>
+ <ClInclude Include="filterengineprovider.h" />
+ <ClInclude Include="inlineformatter.h" />
+ <ClInclude Include="objectproperties.h" />
+ <ClInclude Include="propertylist.h" />
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="targetver.h" />
+ <ClInclude Include="util.h" />
+ <ClInclude Include="modules\monitor.h">
+ <Filter>modules</Filter>
+ </ClInclude>
+ <ClInclude Include="commands\monitor\m_events.h">
+ <Filter>commands\monitor</Filter>
+ </ClInclude>
+ <ClInclude Include="ipropertydecorator.h" />
+ <ClInclude Include="propertydecorator.h" />
+ <ClInclude Include="commands\winfw\policy.h">
+ <Filter>commands\winfw</Filter>
+ </ClInclude>
+ <ClInclude Include="subcommanddispatcher.h" />
+ <ClInclude Include="modules\winfw.h">
+ <Filter>modules</Filter>
+ </ClInclude>
+ <ClInclude Include="commands\winfw\init.h">
+ <Filter>commands\winfw</Filter>
+ </ClInclude>
+ <ClInclude Include="commands\winfw\deinit.h">
+ <Filter>commands\winfw</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="commands\list\sessions.cpp">
+ <Filter>commands\list</Filter>
+ </ClCompile>
+ <ClCompile Include="modules\module.cpp">
+ <Filter>modules</Filter>
+ </ClCompile>
+ <ClCompile Include="commands\list\providers.cpp">
+ <Filter>commands\list</Filter>
+ </ClCompile>
+ <ClCompile Include="commands\list\events.cpp">
+ <Filter>commands\list</Filter>
+ </ClCompile>
+ <ClCompile Include="commands\list\filters.cpp">
+ <Filter>commands\list</Filter>
+ </ClCompile>
+ <ClCompile Include="commands\list\layers.cpp">
+ <Filter>commands\list</Filter>
+ </ClCompile>
+ <ClCompile Include="commands\list\providercontexts.cpp">
+ <Filter>commands\list</Filter>
+ </ClCompile>
+ <ClCompile Include="commands\list\sublayers.cpp">
+ <Filter>commands\list</Filter>
+ </ClCompile>
+ <ClCompile Include="cli.cpp" />
+ <ClCompile Include="objectproperties.cpp" />
+ <ClCompile Include="stdafx.cpp" />
+ <ClCompile Include="util.cpp" />
+ <ClCompile Include="commands\monitor\m_events.cpp">
+ <Filter>commands\monitor</Filter>
+ </ClCompile>
+ <ClCompile Include="propertydecorator.cpp" />
+ <ClCompile Include="commands\winfw\policy.cpp">
+ <Filter>commands\winfw</Filter>
+ </ClCompile>
+ <ClCompile Include="subcommanddispatcher.cpp" />
+ <ClCompile Include="commands\winfw\init.cpp">
+ <Filter>commands\winfw</Filter>
+ </ClCompile>
+ <ClCompile Include="commands\winfw\deinit.cpp">
+ <Filter>commands\winfw</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/windows/winfw/src/extras/cli/commands/icommand.h b/windows/winfw/src/extras/cli/commands/icommand.h
new file mode 100644
index 0000000000..a9503a205b
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/icommand.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace commands
+{
+
+struct ICommand
+{
+ virtual std::wstring name() = 0;
+ virtual std::wstring description() = 0;
+
+ virtual void handleRequest(const std::vector<std::wstring> &arguments) = 0;
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/events.cpp b/windows/winfw/src/extras/cli/commands/list/events.cpp
new file mode 100644
index 0000000000..4396eff980
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/events.cpp
@@ -0,0 +1,51 @@
+#include "stdafx.h"
+#include "events.h"
+#include "cli/objectproperties.h"
+#include "cli/filterengineprovider.h"
+#include "cli/propertydecorator.h"
+#include "libwfp/objectenumerator.h"
+
+namespace commands::list
+{
+
+Events::Events(MessageSink messageSink)
+ : m_messageSink(messageSink)
+{
+}
+
+std::wstring Events::name()
+{
+ return L"events";
+
+}
+
+std::wstring Events::description()
+{
+ return L"Provides a listing of all recent events in the system.";
+}
+
+void Events::handleRequest(const std::vector<std::wstring> &arguments)
+{
+ if (false == arguments.empty())
+ {
+ throw std::runtime_error("Unsupported argument(s). Cannot complete request.");
+ }
+
+ PrettyPrintOptions options;
+
+ options.indent = 2;
+ options.useSeparator = true;
+
+ PropertyDecorator decorator(FilterEngineProvider::Instance().get());
+
+ wfp::ObjectEnumerator::Events(*FilterEngineProvider::Instance().get(), [&](const FWPM_NET_EVENT0 &event)
+ {
+ m_messageSink(L"Event");
+
+ PrettyPrintProperties(m_messageSink, options, EventProperties(event, &decorator));
+
+ return true;
+ });
+}
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/events.h b/windows/winfw/src/extras/cli/commands/list/events.h
new file mode 100644
index 0000000000..cc727712f0
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/events.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "cli/commands/icommand.h"
+#include "cli/util.h"
+
+namespace commands::list
+{
+
+class Events : public ICommand
+{
+public:
+
+ Events(MessageSink messageSink);
+
+ std::wstring name() override;
+ std::wstring description() override;
+
+ void handleRequest(const std::vector<std::wstring> &arguments) override;
+
+private:
+
+ MessageSink m_messageSink;
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/filters.cpp b/windows/winfw/src/extras/cli/commands/list/filters.cpp
new file mode 100644
index 0000000000..9c7917bf66
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/filters.cpp
@@ -0,0 +1,51 @@
+#include "stdafx.h"
+#include "filters.h"
+#include "cli/objectproperties.h"
+#include "cli/filterengineprovider.h"
+#include "cli/propertydecorator.h"
+#include "libwfp/objectenumerator.h"
+
+namespace commands::list
+{
+
+Filters::Filters(MessageSink messageSink)
+ : m_messageSink(messageSink)
+{
+}
+
+std::wstring Filters::name()
+{
+ return L"filters";
+
+}
+
+std::wstring Filters::description()
+{
+ return L"Provides a listing of all filters in the system.";
+}
+
+void Filters::handleRequest(const std::vector<std::wstring> &arguments)
+{
+ if (false == arguments.empty())
+ {
+ throw std::runtime_error("Unsupported argument(s). Cannot complete request.");
+ }
+
+ PrettyPrintOptions options;
+
+ options.indent = 2;
+ options.useSeparator = true;
+
+ PropertyDecorator decorator(FilterEngineProvider::Instance().get());
+
+ wfp::ObjectEnumerator::Filters(*FilterEngineProvider::Instance().get(), [&](const FWPM_FILTER0 &filter)
+ {
+ m_messageSink(L"Filter");
+
+ PrettyPrintProperties(m_messageSink, options, FilterProperties(filter, &decorator));
+
+ return true;
+ });
+}
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/filters.h b/windows/winfw/src/extras/cli/commands/list/filters.h
new file mode 100644
index 0000000000..4c0345f3b3
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/filters.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "cli/commands/icommand.h"
+#include "cli/util.h"
+
+namespace commands::list
+{
+
+class Filters : public ICommand
+{
+public:
+
+ Filters(MessageSink messageSink);
+
+ std::wstring name() override;
+ std::wstring description() override;
+
+ void handleRequest(const std::vector<std::wstring> &arguments) override;
+
+private:
+
+ MessageSink m_messageSink;
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/layers.cpp b/windows/winfw/src/extras/cli/commands/list/layers.cpp
new file mode 100644
index 0000000000..3dc4368894
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/layers.cpp
@@ -0,0 +1,51 @@
+#include "stdafx.h"
+#include "layers.h"
+#include "cli/objectproperties.h"
+#include "cli/filterengineprovider.h"
+#include "cli/propertydecorator.h"
+#include "libwfp/objectenumerator.h"
+
+namespace commands::list
+{
+
+Layers::Layers(MessageSink messageSink)
+ : m_messageSink(messageSink)
+{
+}
+
+std::wstring Layers::name()
+{
+ return L"layers";
+
+}
+
+std::wstring Layers::description()
+{
+ return L"Provides a listing of all layers in the system.";
+}
+
+void Layers::handleRequest(const std::vector<std::wstring> &arguments)
+{
+ if (false == arguments.empty())
+ {
+ throw std::runtime_error("Unsupported argument(s). Cannot complete request.");
+ }
+
+ PrettyPrintOptions options;
+
+ options.indent = 2;
+ options.useSeparator = true;
+
+ PropertyDecorator decorator(FilterEngineProvider::Instance().get());
+
+ wfp::ObjectEnumerator::Layers(*FilterEngineProvider::Instance().get(), [&](const FWPM_LAYER0 &layer)
+ {
+ m_messageSink(L"Layer");
+
+ PrettyPrintProperties(m_messageSink, options, LayerProperties(layer, &decorator));
+
+ return true;
+ });
+}
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/layers.h b/windows/winfw/src/extras/cli/commands/list/layers.h
new file mode 100644
index 0000000000..92a62c58e0
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/layers.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "cli/commands/icommand.h"
+#include "cli/util.h"
+
+namespace commands::list
+{
+
+class Layers : public ICommand
+{
+public:
+
+ Layers(MessageSink messageSink);
+
+ std::wstring name() override;
+ std::wstring description() override;
+
+ void handleRequest(const std::vector<std::wstring> &arguments) override;
+
+private:
+
+ MessageSink m_messageSink;
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/providercontexts.cpp b/windows/winfw/src/extras/cli/commands/list/providercontexts.cpp
new file mode 100644
index 0000000000..6612ce03dc
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/providercontexts.cpp
@@ -0,0 +1,51 @@
+#include "stdafx.h"
+#include "providercontexts.h"
+#include "cli/objectproperties.h"
+#include "cli/filterengineprovider.h"
+#include "cli/propertydecorator.h"
+#include "libwfp/objectenumerator.h"
+
+namespace commands::list
+{
+
+ProviderContexts::ProviderContexts(MessageSink messageSink)
+ : m_messageSink(messageSink)
+{
+}
+
+std::wstring ProviderContexts::name()
+{
+ return L"providercontexts";
+
+}
+
+std::wstring ProviderContexts::description()
+{
+ return L"Provides a listing of all provider contexts in the system.";
+}
+
+void ProviderContexts::handleRequest(const std::vector<std::wstring> &arguments)
+{
+ if (false == arguments.empty())
+ {
+ throw std::runtime_error("Unsupported argument(s). Cannot complete request.");
+ }
+
+ PrettyPrintOptions options;
+
+ options.indent = 2;
+ options.useSeparator = true;
+
+ PropertyDecorator decorator(FilterEngineProvider::Instance().get());
+
+ wfp::ObjectEnumerator::ProviderContexts(*FilterEngineProvider::Instance().get(), [&](const FWPM_PROVIDER_CONTEXT0 &context)
+ {
+ m_messageSink(L"Provider context");
+
+ PrettyPrintProperties(m_messageSink, options, ProviderContextProperties(context, &decorator));
+
+ return true;
+ });
+}
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/providercontexts.h b/windows/winfw/src/extras/cli/commands/list/providercontexts.h
new file mode 100644
index 0000000000..b87886eac7
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/providercontexts.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "cli/commands/icommand.h"
+#include "cli/util.h"
+
+namespace commands::list
+{
+
+class ProviderContexts : public ICommand
+{
+public:
+
+ ProviderContexts(MessageSink messageSink);
+
+ std::wstring name() override;
+ std::wstring description() override;
+
+ void handleRequest(const std::vector<std::wstring> &arguments) override;
+
+private:
+
+ MessageSink m_messageSink;
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/providers.cpp b/windows/winfw/src/extras/cli/commands/list/providers.cpp
new file mode 100644
index 0000000000..a06318d74b
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/providers.cpp
@@ -0,0 +1,48 @@
+#include "stdafx.h"
+#include "providers.h"
+#include "cli/objectproperties.h"
+#include "cli/filterengineprovider.h"
+#include "libwfp/objectenumerator.h"
+
+namespace commands::list
+{
+
+Providers::Providers(MessageSink messageSink)
+ : m_messageSink(messageSink)
+{
+}
+
+std::wstring Providers::name()
+{
+ return L"providers";
+
+}
+
+std::wstring Providers::description()
+{
+ return L"Provides a listing of all providers in the system.";
+}
+
+void Providers::handleRequest(const std::vector<std::wstring> &arguments)
+{
+ if (false == arguments.empty())
+ {
+ throw std::runtime_error("Unsupported argument(s). Cannot complete request.");
+ }
+
+ PrettyPrintOptions options;
+
+ options.indent = 2;
+ options.useSeparator = true;
+
+ wfp::ObjectEnumerator::Providers(*FilterEngineProvider::Instance().get(), [&](const FWPM_PROVIDER0 &provider)
+ {
+ m_messageSink(L"Provider");
+
+ PrettyPrintProperties(m_messageSink, options, ProviderProperties(provider));
+
+ return true;
+ });
+}
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/providers.h b/windows/winfw/src/extras/cli/commands/list/providers.h
new file mode 100644
index 0000000000..2a09a88b67
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/providers.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "cli/commands/icommand.h"
+#include "cli/util.h"
+
+namespace commands::list
+{
+
+class Providers : public ICommand
+{
+public:
+
+ Providers(MessageSink messageSink);
+
+ std::wstring name() override;
+ std::wstring description() override;
+
+ void handleRequest(const std::vector<std::wstring> &arguments) override;
+
+private:
+
+ MessageSink m_messageSink;
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/sessions.cpp b/windows/winfw/src/extras/cli/commands/list/sessions.cpp
new file mode 100644
index 0000000000..27000c903d
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/sessions.cpp
@@ -0,0 +1,48 @@
+#include "stdafx.h"
+#include "sessions.h"
+#include "cli/objectproperties.h"
+#include "cli/filterengineprovider.h"
+#include "libwfp/objectenumerator.h"
+
+namespace commands::list
+{
+
+Sessions::Sessions(MessageSink messageSink)
+ : m_messageSink(messageSink)
+{
+}
+
+std::wstring Sessions::name()
+{
+ return L"sessions";
+
+}
+
+std::wstring Sessions::description()
+{
+ return L"Provides a listing of all active sessions in the system.";
+}
+
+void Sessions::handleRequest(const std::vector<std::wstring> &arguments)
+{
+ if (false == arguments.empty())
+ {
+ throw std::runtime_error("Unsupported argument(s). Cannot complete request.");
+ }
+
+ PrettyPrintOptions options;
+
+ options.indent = 2;
+ options.useSeparator = true;
+
+ wfp::ObjectEnumerator::Sessions(*FilterEngineProvider::Instance().get(), [&](const FWPM_SESSION0 &session)
+ {
+ m_messageSink(L"Session");
+
+ PrettyPrintProperties(m_messageSink, options, SessionProperties(session));
+
+ return true;
+ });
+}
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/sessions.h b/windows/winfw/src/extras/cli/commands/list/sessions.h
new file mode 100644
index 0000000000..b9d8f04ea2
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/sessions.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "cli/commands/icommand.h"
+#include "cli/util.h"
+
+namespace commands::list
+{
+
+class Sessions : public ICommand
+{
+public:
+
+ Sessions(MessageSink messageSink);
+
+ std::wstring name() override;
+ std::wstring description() override;
+
+ void handleRequest(const std::vector<std::wstring> &arguments) override;
+
+private:
+
+ MessageSink m_messageSink;
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/sublayers.cpp b/windows/winfw/src/extras/cli/commands/list/sublayers.cpp
new file mode 100644
index 0000000000..0c8a5f2f58
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/sublayers.cpp
@@ -0,0 +1,51 @@
+#include "stdafx.h"
+#include "sublayers.h"
+#include "cli/objectproperties.h"
+#include "cli/filterengineprovider.h"
+#include "cli/propertydecorator.h"
+#include "libwfp/objectenumerator.h"
+
+namespace commands::list
+{
+
+Sublayers::Sublayers(MessageSink messageSink)
+ : m_messageSink(messageSink)
+{
+}
+
+std::wstring Sublayers::name()
+{
+ return L"sublayers";
+
+}
+
+std::wstring Sublayers::description()
+{
+ return L"Provides a listing of all sublayers in the system.";
+}
+
+void Sublayers::handleRequest(const std::vector<std::wstring> &arguments)
+{
+ if (false == arguments.empty())
+ {
+ throw std::runtime_error("Unsupported argument(s). Cannot complete request.");
+ }
+
+ PrettyPrintOptions options;
+
+ options.indent = 2;
+ options.useSeparator = true;
+
+ PropertyDecorator decorator(FilterEngineProvider::Instance().get());
+
+ wfp::ObjectEnumerator::Sublayers(*FilterEngineProvider::Instance().get(), [&](const FWPM_SUBLAYER0 &sublayer)
+ {
+ m_messageSink(L"Sublayer");
+
+ PrettyPrintProperties(m_messageSink, options, SublayerProperties(sublayer, &decorator));
+
+ return true;
+ });
+}
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/list/sublayers.h b/windows/winfw/src/extras/cli/commands/list/sublayers.h
new file mode 100644
index 0000000000..e9b34bbd52
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/list/sublayers.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "cli/commands/icommand.h"
+#include "cli/util.h"
+
+namespace commands::list
+{
+
+class Sublayers : public ICommand
+{
+public:
+
+ Sublayers(MessageSink messageSink);
+
+ std::wstring name() override;
+ std::wstring description() override;
+
+ void handleRequest(const std::vector<std::wstring> &arguments) override;
+
+private:
+
+ MessageSink m_messageSink;
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/monitor/m_events.cpp b/windows/winfw/src/extras/cli/commands/monitor/m_events.cpp
new file mode 100644
index 0000000000..294ae38e9c
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/monitor/m_events.cpp
@@ -0,0 +1,68 @@
+#include "stdafx.h"
+#include "m_events.h"
+#include "cli/objectproperties.h"
+#include "cli/propertydecorator.h"
+#include "cli/filterengineprovider.h"
+#include "libwfp/objectmonitor.h"
+#include <conio.h>
+
+namespace commands::monitor
+{
+
+Events::Events(MessageSink messageSink)
+ : m_messageSink(messageSink)
+{
+}
+
+std::wstring Events::name()
+{
+ return L"events";
+
+}
+
+std::wstring Events::description()
+{
+ return L"Provides monitoring of drop/allow events.";
+}
+
+void Events::handleRequest(const std::vector<std::wstring> &arguments)
+{
+ if (false == arguments.empty())
+ {
+ throw std::runtime_error("Unsupported argument(s). Cannot complete request.");
+ }
+
+ wfp::ObjectMonitor objectMonitor(FilterEngineProvider::Instance().get());
+
+ objectMonitor.monitorEvents(std::bind(&Events::eventCallback, this, std::placeholders::_1));
+
+ m_messageSink(L"Successfully enabled monitor. Press any key to abort monitoring.");
+
+ //
+ // This assumes we're in a console environment, but the alternative is to have
+ // the command (this class) communicate to the outside world that we're in
+ // a monitoring state.
+ //
+ // Hence, this is sufficient for now.
+ //
+
+ _getwch();
+
+ objectMonitor.monitorEventsStop();
+}
+
+void Events::eventCallback(const FWPM_NET_EVENT1 &event)
+{
+ m_messageSink(L"Event");
+
+ PrettyPrintOptions options;
+
+ options.indent = 2;
+ options.useSeparator = true;
+
+ PropertyDecorator decorator(FilterEngineProvider::Instance().get());
+
+ PrettyPrintProperties(m_messageSink, options, EventProperties(event, &decorator));
+}
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/monitor/m_events.h b/windows/winfw/src/extras/cli/commands/monitor/m_events.h
new file mode 100644
index 0000000000..e8724129e8
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/monitor/m_events.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "cli/commands/icommand.h"
+#include "cli/util.h"
+#include <memory>
+
+namespace commands::monitor
+{
+
+class Events : public ICommand
+{
+public:
+
+ Events(MessageSink messageSink);
+
+ std::wstring name() override;
+ std::wstring description() override;
+
+ void handleRequest(const std::vector<std::wstring> &arguments) override;
+
+private:
+
+ MessageSink m_messageSink;
+
+ void eventCallback(const FWPM_NET_EVENT1 &event);
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/winfw/deinit.cpp b/windows/winfw/src/extras/cli/commands/winfw/deinit.cpp
new file mode 100644
index 0000000000..d0fc5cfffb
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/winfw/deinit.cpp
@@ -0,0 +1,35 @@
+#include "stdafx.h"
+#include "deinit.h"
+#include "winfw/winfw.h"
+
+namespace commands::winfw
+{
+
+Deinit::Deinit(MessageSink messageSink)
+ : m_messageSink(messageSink)
+{
+}
+
+std::wstring Deinit::name()
+{
+ return L"deinit";
+}
+
+std::wstring Deinit::description()
+{
+ return L"Deinitialize winfw; Destroy session and all associated objects";
+}
+
+void Deinit::handleRequest(const std::vector<std::wstring> &arguments)
+{
+ if (false == arguments.empty())
+ {
+ throw std::runtime_error("Invalid argument(s). Cannot complete request.");
+ }
+
+ m_messageSink((WinFw_Deinitialize()
+ ? L"Deinitialization completed successfully."
+ : L"Deinitialization failed. See above for details, if any."));
+}
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/winfw/deinit.h b/windows/winfw/src/extras/cli/commands/winfw/deinit.h
new file mode 100644
index 0000000000..2395f381de
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/winfw/deinit.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "cli/commands/icommand.h"
+#include "cli/util.h"
+
+namespace commands::winfw
+{
+
+class Deinit : public ICommand
+{
+public:
+
+ Deinit(MessageSink messageSink);
+
+ std::wstring name() override;
+ std::wstring description() override;
+
+ void handleRequest(const std::vector<std::wstring> &arguments) override;
+
+private:
+
+ MessageSink m_messageSink;
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/winfw/init.cpp b/windows/winfw/src/extras/cli/commands/winfw/init.cpp
new file mode 100644
index 0000000000..6236027725
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/winfw/init.cpp
@@ -0,0 +1,54 @@
+#include "stdafx.h"
+#include "init.h"
+#include "libcommon/string.h"
+
+namespace commands::winfw
+{
+
+Init::Init(MessageSink messageSink)
+ : m_messageSink(messageSink)
+{
+}
+
+std::wstring Init::name()
+{
+ return L"init";
+}
+
+std::wstring Init::description()
+{
+ return L"Initialize winfw; Create session and fundamental objects";
+}
+
+void Init::handleRequest(const std::vector<std::wstring> &arguments)
+{
+ uint32_t timeout = 0;
+
+ if (false == arguments.empty())
+ {
+ auto keyvalue = common::string::SplitKeyValuePairs(arguments);
+
+ if (keyvalue.empty() || 0 != keyvalue.begin()->first.compare(L"timeout"))
+ {
+ throw std::runtime_error("Invalid argument. Cannot complete request.");
+ }
+
+ timeout = wcstoul(keyvalue.begin()->second.c_str(), nullptr, 10);
+ }
+
+ auto success = WinFw_Initialize(timeout, &Init::ErrorForwarder, this);
+
+ m_messageSink((success
+ ? L"Initialization completed successfully."
+ : L"Initialization failed. See above for details, if any."));
+}
+
+//static
+void WINFW_API Init::ErrorForwarder(const char *errorMessage, void *context)
+{
+ auto thiz = reinterpret_cast<Init *>(context);
+
+ thiz->m_messageSink(common::string::ToWide(errorMessage));
+}
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/winfw/init.h b/windows/winfw/src/extras/cli/commands/winfw/init.h
new file mode 100644
index 0000000000..f3589535bf
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/winfw/init.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "cli/commands/icommand.h"
+#include "cli/util.h"
+#include "winfw/winfw.h"
+
+namespace commands::winfw
+{
+
+class Init : public ICommand
+{
+public:
+
+ Init(MessageSink messageSink);
+
+ std::wstring name() override;
+ std::wstring description() override;
+
+ void handleRequest(const std::vector<std::wstring> &arguments) override;
+
+private:
+
+ MessageSink m_messageSink;
+
+ static void WINFW_API ErrorForwarder(const char *errorMessage, void *context);
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/winfw/policy.cpp b/windows/winfw/src/extras/cli/commands/winfw/policy.cpp
new file mode 100644
index 0000000000..04906332aa
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/winfw/policy.cpp
@@ -0,0 +1,170 @@
+#include "stdafx.h"
+#include "policy.h"
+#include "libcommon/string.h"
+#include "winfw/winfw.h"
+#include <functional>
+
+namespace commands::winfw
+{
+
+namespace detail
+{
+
+WinFwSettings CreateSettings(const std::wstring &dhcp, const std::wstring &lan)
+{
+ WinFwSettings s;
+
+ s.permitDhcp = (0 == _wcsicmp(dhcp.c_str(), L"yes"));
+ s.permitLan = (0 == _wcsicmp(lan.c_str(), L"yes"));
+
+ return s;
+}
+
+WinFwProtocol TranslateProtocol(const std::wstring &protocol)
+{
+ return (0 == _wcsicmp(protocol.c_str(), L"tcp") ? WinFwProtocol::Tcp : WinFwProtocol::Udp);
+}
+
+WinFwRelay CreateRelay(const wchar_t *ip, const std::wstring &port, const std::wstring &protocol)
+{
+ WinFwRelay r;
+
+ r.ip = ip;
+ r.port = common::string::LexicalCast<uint16_t>(port);
+ r.protocol = TranslateProtocol(protocol);
+
+ return r;
+}
+
+} // namespace detail
+
+Policy::Policy(MessageSink messageSink)
+ : m_messageSink(messageSink)
+{
+ m_dispatcher.addSubcommand
+ (
+ L"connecting",
+ std::bind(&Policy::processConnecting, this, std::placeholders::_1)
+ );
+
+ m_dispatcher.addSubcommand
+ (
+ L"connected",
+ std::bind(&Policy::processConnected, this, std::placeholders::_1)
+ );
+
+ m_dispatcher.addSubcommand
+ (
+ L"netblocked",
+ std::bind(&Policy::processNetBlocked, this)
+ );
+
+ m_dispatcher.addSubcommand
+ (
+ L"reset",
+ std::bind(&Policy::processReset, this)
+ );
+}
+
+std::wstring Policy::name()
+{
+ return L"policy";
+}
+
+std::wstring Policy::description()
+{
+ return L"Activate and reset policies.";
+}
+
+void Policy::handleRequest(const std::vector<std::wstring> &arguments)
+{
+ if (arguments.empty())
+ {
+ throw std::runtime_error("Missing subcommand. Cannot complete request.");
+ }
+
+ auto subcommand = arguments[0];
+
+ auto actualArguments(arguments);
+ actualArguments.erase(actualArguments.begin());
+
+ m_dispatcher.dispatch(subcommand, actualArguments);
+}
+
+void Policy::processConnecting(const KeyValuePairs &arguments)
+{
+ auto settings = detail::CreateSettings
+ (
+ GetArgumentValue(arguments, L"dhcp"),
+ GetArgumentValue(arguments, L"lan")
+ );
+
+ auto r = GetArgumentValue(arguments, L"relay");
+
+ auto relay = detail::CreateRelay
+ (
+ r.c_str(),
+ GetArgumentValue(arguments, L"port"),
+ GetArgumentValue(arguments, L"protocol")
+ );
+
+ auto success = WinFw_ApplyPolicyConnecting
+ (
+ settings,
+ relay
+ );
+
+ m_messageSink((success
+ ? L"Successfully applied policy."
+ : L"Failed to apply policy."));
+}
+
+void Policy::processConnected(const KeyValuePairs &arguments)
+{
+ auto settings = detail::CreateSettings
+ (
+ GetArgumentValue(arguments, L"dhcp"),
+ GetArgumentValue(arguments, L"lan")
+ );
+
+ auto r = GetArgumentValue(arguments, L"relay");
+
+ auto relay = detail::CreateRelay
+ (
+ r.c_str(),
+ GetArgumentValue(arguments, L"port"),
+ GetArgumentValue(arguments, L"protocol")
+ );
+
+ auto success = WinFw_ApplyPolicyConnected
+ (
+ settings,
+ relay,
+ GetArgumentValue(arguments, L"tunnel").c_str(),
+ GetArgumentValue(arguments, L"dns").c_str()
+ );
+
+ m_messageSink((success
+ ? L"Successfully applied policy."
+ : L"Failed to apply policy."));
+}
+
+void Policy::processNetBlocked()
+{
+ auto success = WinFw_ApplyPolicyNetBlocked();
+
+ m_messageSink((success
+ ? L"Successfully applied policy."
+ : L"Failed to apply policy."));
+}
+
+void Policy::processReset()
+{
+ auto success = WinFw_Reset();
+
+ m_messageSink((success
+ ? L"Successfully reset policy."
+ : L"Failed to reset policy."));
+}
+
+}
diff --git a/windows/winfw/src/extras/cli/commands/winfw/policy.h b/windows/winfw/src/extras/cli/commands/winfw/policy.h
new file mode 100644
index 0000000000..5d4d15e6b1
--- /dev/null
+++ b/windows/winfw/src/extras/cli/commands/winfw/policy.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "cli/commands/icommand.h"
+#include "cli/util.h"
+#include "cli/subcommanddispatcher.h"
+#include "libcommon/string.h"
+
+namespace commands::winfw
+{
+
+class Policy : public ICommand
+{
+public:
+
+ Policy(MessageSink messageSink);
+
+ std::wstring name() override;
+ std::wstring description() override;
+
+ void handleRequest(const std::vector<std::wstring> &arguments) override;
+
+private:
+
+ MessageSink m_messageSink;
+ SubcommandDispatcher m_dispatcher;
+
+ using KeyValuePairs = common::string::KeyValuePairs;
+
+ void processConnecting(const KeyValuePairs &arguments);
+ void processConnected(const KeyValuePairs &arguments);
+ void processNetBlocked();
+ void processReset();
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/filterengineprovider.h b/windows/winfw/src/extras/cli/filterengineprovider.h
new file mode 100644
index 0000000000..50382c7a6c
--- /dev/null
+++ b/windows/winfw/src/extras/cli/filterengineprovider.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "libwfp/filterengine.h"
+#include <memory>
+
+class FilterEngineProvider
+{
+ FilterEngineProvider()
+ {
+ }
+
+public:
+
+ static FilterEngineProvider &Instance()
+ {
+ static FilterEngineProvider provider;
+ return provider;
+ }
+
+ std::shared_ptr<wfp::FilterEngine> get()
+ {
+ return m_engine;
+ }
+
+ //
+ // naive set(), good for now
+ //
+ void set(std::shared_ptr<wfp::FilterEngine> engine)
+ {
+ m_engine = engine;
+ }
+
+private:
+
+ FilterEngineProvider(const FilterEngineProvider &);
+ FilterEngineProvider &operator=(const FilterEngineProvider &);
+
+ std::shared_ptr<wfp::FilterEngine> m_engine;
+};
diff --git a/windows/winfw/src/extras/cli/inlineformatter.h b/windows/winfw/src/extras/cli/inlineformatter.h
new file mode 100644
index 0000000000..f00aa6d504
--- /dev/null
+++ b/windows/winfw/src/extras/cli/inlineformatter.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <sstream>
+#include <utility>
+
+class InlineFormatter
+{
+public:
+
+ template<typename T>
+ InlineFormatter &operator<<(const T &t)
+ {
+ m_ss << t;
+ return *this;
+ }
+
+ std::wstring str()
+ {
+ auto s = m_ss.str();
+
+ m_ss.str(L"");
+ m_ss.clear();
+
+ return s;
+ }
+
+private:
+
+ std::wstringstream m_ss;
+};
diff --git a/windows/winfw/src/extras/cli/ipropertydecorator.h b/windows/winfw/src/extras/cli/ipropertydecorator.h
new file mode 100644
index 0000000000..01a6021ba1
--- /dev/null
+++ b/windows/winfw/src/extras/cli/ipropertydecorator.h
@@ -0,0 +1,32 @@
+#pragma once
+
+//
+// These methods should be used during property extraction in order to translate
+// identifiers (references) into something more meaningful.
+//
+// Ideally we would nest a PropertyList inside a PropertyList, but that's a rather
+// large update since each element in the PropertyList would need to be wrapped.
+// If such an update is made one day, be sure to also add a "group" element so
+// we can have better structuring within a PropertyList
+//
+
+#include <windows.h>
+#include <string>
+
+struct IPropertyDecorator
+{
+ //
+ // These methods should return a short string that adds
+ // value for human operators/analysts.
+ //
+
+ virtual std::wstring FilterDecoration(UINT64 id) = 0;
+ virtual std::wstring LayerDecoration(UINT16 id) = 0;
+ virtual std::wstring LayerDecoration(const GUID &key) = 0;
+ virtual std::wstring ProviderDecoration(const GUID &key) = 0;
+ virtual std::wstring SublayerDecoration(const GUID &key) = 0;
+
+ virtual ~IPropertyDecorator() = 0
+ {
+ }
+};
diff --git a/windows/winfw/src/extras/cli/modules/imodule.h b/windows/winfw/src/extras/cli/modules/imodule.h
new file mode 100644
index 0000000000..82221f284f
--- /dev/null
+++ b/windows/winfw/src/extras/cli/modules/imodule.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "cli/propertylist.h"
+#include <string>
+#include <vector>
+
+namespace modules
+{
+
+struct IModule
+{
+ virtual std::wstring name() = 0;
+ virtual std::wstring description() = 0;
+ virtual PropertyList commands() = 0;
+
+ virtual void handleRequest(const std::vector<std::wstring> &request) = 0;
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/modules/list.h b/windows/winfw/src/extras/cli/modules/list.h
new file mode 100644
index 0000000000..23751c2a26
--- /dev/null
+++ b/windows/winfw/src/extras/cli/modules/list.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "module.h"
+#include "cli/util.h"
+#include "cli/commands/list/sessions.h"
+#include "cli/commands/list/providers.h"
+#include "cli/commands/list/events.h"
+#include "cli/commands/list/filters.h"
+#include "cli/commands/list/layers.h"
+#include "cli/commands/list/providercontexts.h"
+#include "cli/commands/list/sublayers.h"
+
+namespace modules
+{
+
+class List : public Module
+{
+public:
+
+ List(MessageSink messageSink)
+ : Module(L"list", L"List various objects in the WFP universe.")
+ {
+ addCommand(std::make_unique<commands::list::Sessions>(messageSink));
+ addCommand(std::make_unique<commands::list::Providers>(messageSink));
+ addCommand(std::make_unique<commands::list::Events>(messageSink));
+ addCommand(std::make_unique<commands::list::Filters>(messageSink));
+ addCommand(std::make_unique<commands::list::Layers>(messageSink));
+ addCommand(std::make_unique<commands::list::ProviderContexts>(messageSink));
+ addCommand(std::make_unique<commands::list::Sublayers>(messageSink));
+ }
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/modules/module.cpp b/windows/winfw/src/extras/cli/modules/module.cpp
new file mode 100644
index 0000000000..f66ae5a084
--- /dev/null
+++ b/windows/winfw/src/extras/cli/modules/module.cpp
@@ -0,0 +1,70 @@
+#include "stdafx.h"
+#include "module.h"
+#include "cli/util.h"
+#include "libcommon/string.h"
+#include <sstream>
+#include <utility>
+
+namespace modules
+{
+
+PropertyList Module::commands()
+{
+ PropertyList c;
+
+ for (auto &command : m_commands)
+ {
+ c.add(common::string::Lower(command.second->name()), command.second->description());
+ }
+
+ return c;
+}
+
+void Module::handleRequest(const std::vector<std::wstring> &request)
+{
+ //
+ // The request has the form of:
+ //
+ // [0] command
+ // [1] arg1
+ // [2] arg2
+ // ...
+ //
+
+ if (request.empty())
+ {
+ std::wstringstream ss;
+
+ ss << L"Command missing. Try 'help " << m_name << "'.";
+
+ throw std::runtime_error(common::string::ToAnsi(ss.str()));
+ }
+
+ auto wanted = common::string::Lower(request[0]);
+ auto found = m_commands.find(wanted);
+
+ if (found == m_commands.end())
+ {
+ std::wstringstream ss;
+
+ ss << L"Module '" << m_name << "' doesn't support the command '" << request[0] << "'.";
+
+ throw std::runtime_error(common::string::ToAnsi(ss.str()));
+ }
+
+ auto args = request;
+
+ args.erase(args.begin());
+
+ found->second->handleRequest(args);
+}
+
+void Module::addCommand(std::unique_ptr<commands::ICommand> command)
+{
+ m_commands.insert(std::make_pair(
+ common::string::Lower(command->name()),
+ std::move(command)
+ ));
+}
+
+}
diff --git a/windows/winfw/src/extras/cli/modules/module.h b/windows/winfw/src/extras/cli/modules/module.h
new file mode 100644
index 0000000000..45587ab7e5
--- /dev/null
+++ b/windows/winfw/src/extras/cli/modules/module.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "imodule.h"
+#include "cli/commands/icommand.h"
+#include <map>
+#include <memory>
+
+namespace modules
+{
+
+class Module : public IModule
+{
+public:
+
+ Module(const std::wstring &name, const std::wstring &description)
+ : m_name(name)
+ , m_description(description)
+ {
+ }
+
+ std::wstring name() override
+ {
+ return m_name;
+ }
+
+ std::wstring description() override
+ {
+ return m_description;
+ }
+
+ // Collect name and description from commands.
+ PropertyList commands() override;
+
+ // Identify requested command and dispatch to it.
+ void handleRequest(const std::vector<std::wstring> &request) override;
+
+ void addCommand(std::unique_ptr<commands::ICommand> command);
+
+private:
+
+ Module(const Module &);
+ Module &operator=(const Module &);
+
+ std::wstring m_name;
+ std::wstring m_description;
+
+ std::map<std::wstring, std::unique_ptr<commands::ICommand> > m_commands;
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/modules/monitor.h b/windows/winfw/src/extras/cli/modules/monitor.h
new file mode 100644
index 0000000000..e227192e82
--- /dev/null
+++ b/windows/winfw/src/extras/cli/modules/monitor.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "module.h"
+#include "cli/util.h"
+#include "cli/commands/monitor/m_events.h"
+
+namespace modules
+{
+
+class Monitor : public Module
+{
+public:
+
+ Monitor(MessageSink messageSink)
+ : Module(L"monitor", L"Real-time monitoring of events and object creation/deletion in WFP.")
+ {
+ addCommand(std::make_unique<commands::monitor::Events>(messageSink));
+ }
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/modules/winfw.h b/windows/winfw/src/extras/cli/modules/winfw.h
new file mode 100644
index 0000000000..33d5621fe9
--- /dev/null
+++ b/windows/winfw/src/extras/cli/modules/winfw.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "module.h"
+#include "cli/util.h"
+#include "cli/commands/winfw/init.h"
+#include "cli/commands/winfw/deinit.h"
+#include "cli/commands/winfw/policy.h"
+
+namespace modules
+{
+
+class WinFw : public Module
+{
+public:
+
+ WinFw(MessageSink messageSink)
+ : Module(L"winfw", L"Exercise functionality provided by \"winfw.dll\".")
+ {
+ addCommand(std::make_unique<commands::winfw::Init>(messageSink));
+ addCommand(std::make_unique<commands::winfw::Deinit>(messageSink));
+ addCommand(std::make_unique<commands::winfw::Policy>(messageSink));
+ }
+};
+
+}
diff --git a/windows/winfw/src/extras/cli/objectproperties.cpp b/windows/winfw/src/extras/cli/objectproperties.cpp
new file mode 100644
index 0000000000..8b7b5a9c04
--- /dev/null
+++ b/windows/winfw/src/extras/cli/objectproperties.cpp
@@ -0,0 +1,709 @@
+#include "stdafx.h"
+#include "objectproperties.h"
+#include "inlineformatter.h"
+#include "propertylist.h"
+#include "libcommon/string.h"
+#include <string>
+#include <utility>
+#include <vector>
+#include <cwchar>
+
+// Add missing constants
+// These are documented in MSDN but not defined in any header?
+#define FWP_DIRECTION_IN 0x00003900L
+#define FWP_DIRECTION_OUT 0x00003901L
+#define FWP_DIRECTION_FORWARD 0x00003902L
+
+namespace detail
+{
+
+std::wstring SessionFlags(UINT32 flags)
+{
+ std::vector<std::pair<UINT32, std::wstring> > definitions =
+ {
+ std::make_pair(FWPM_SESSION_FLAG_DYNAMIC, L"FWPM_SESSION_FLAG_DYNAMIC"),
+ std::make_pair(FWPM_SESSION_FLAG_RESERVED, L"FWPM_SESSION_FLAG_RESERVED")
+ };
+
+ return common::string::FormatFlags(definitions, flags);
+}
+
+std::wstring ProviderFlags(UINT32 flags)
+{
+ std::vector<std::pair<UINT32, std::wstring> > definitions =
+ {
+ std::make_pair(FWPM_PROVIDER_FLAG_PERSISTENT, L"FWPM_PROVIDER_FLAG_PERSISTENT"),
+ std::make_pair(FWPM_PROVIDER_FLAG_DISABLED, L"FWPM_PROVIDER_FLAG_DISABLED")
+ };
+
+ return common::string::FormatFlags(definitions, flags);
+}
+
+std::wstring FormatIpProtocol(UINT8 protocol)
+{
+ switch (protocol)
+ {
+ case 0: return L"IPPROTO_HOPOPTS";
+ case 1: return L"IPPROTO_ICMP";
+ case 2: return L"IPPROTO_IGMP";
+ case 3: return L"IPPROTO_GGP";
+ case 4: return L"IPPROTO_IPV4";
+ case 5: return L"IPPROTO_ST";
+ case 6: return L"IPPROTO_TCP";
+ case 7: return L"IPPROTO_CBT";
+ case 8: return L"IPPROTO_EGP";
+ case 9: return L"IPPROTO_IGP";
+ case 12: return L"IPPROTO_PUP";
+ case 17: return L"IPPROTO_UDP";
+ case 22: return L"IPPROTO_IDP";
+ case 27: return L"IPPROTO_RDP";
+ case 41: return L"IPPROTO_IPV6";
+ case 43: return L"IPPROTO_ROUTING";
+ case 44: return L"IPPROTO_FRAGMENT";
+ case 50: return L"IPPROTO_ESP";
+ case 51: return L"IPPROTO_AH";
+ case 58: return L"IPPROTO_ICMPV6";
+ case 59: return L"IPPROTO_NONE";
+ case 60: return L"IPPROTO_DSTOPTS";
+ case 77: return L"IPPROTO_ND";
+ case 78: return L"IPPROTO_ICLFXBM";
+ case 103: return L"IPPROTO_PIM";
+ case 113: return L"IPPROTO_PGM";
+ case 115: return L"IPPROTO_L2TP";
+ case 132: return L"IPPROTO_SCTP";
+ case 255: return L"IPPROTO_RAW";
+ default: return L"Unknown";
+ };
+}
+
+std::wstring FilterFlags(UINT32 flags)
+{
+ std::vector<std::pair<UINT32, std::wstring> > definitions =
+ {
+ std::make_pair(FWPM_FILTER_FLAG_PERSISTENT, L"FWPM_FILTER_FLAG_PERSISTENT"),
+ std::make_pair(FWPM_FILTER_FLAG_BOOTTIME, L"FWPM_FILTER_FLAG_BOOTTIME"),
+ std::make_pair(FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT, L"FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT"),
+ std::make_pair(FWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED, L"FWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED"),
+ std::make_pair(FWPM_FILTER_FLAG_DISABLED, L"FWPM_FILTER_FLAG_DISABLED"),
+ std::make_pair(FWPM_FILTER_FLAG_INDEXED, L"FWPM_FILTER_FLAG_INDEXED")
+ };
+
+ return common::string::FormatFlags(definitions, flags);
+}
+
+std::wstring LayerFlags(UINT32 flags)
+{
+ std::vector<std::pair<UINT32, std::wstring> > definitions =
+ {
+ std::make_pair(FWPM_LAYER_FLAG_KERNEL, L"FWPM_LAYER_FLAG_KERNEL"),
+ std::make_pair(FWPM_LAYER_FLAG_BUILTIN, L"FWPM_LAYER_FLAG_BUILTIN"),
+ std::make_pair(FWPM_LAYER_FLAG_CLASSIFY_MOSTLY, L"FWPM_LAYER_FLAG_CLASSIFY_MOSTLY"),
+ std::make_pair(FWPM_LAYER_FLAG_BUFFERED, L"FWPM_LAYER_FLAG_BUFFERED")
+ };
+
+ return common::string::FormatFlags(definitions, flags);
+}
+
+std::wstring ProviderContextType(FWPM_PROVIDER_CONTEXT_TYPE type)
+{
+ switch (type)
+ {
+ case FWPM_IPSEC_KEYING_CONTEXT: return L"FWPM_IPSEC_KEYING_CONTEXT";
+ case FWPM_IPSEC_IKE_QM_TRANSPORT_CONTEXT: return L"FWPM_IPSEC_IKE_QM_TRANSPORT_CONTEXT";
+ case FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT: return L"FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT";
+ case FWPM_IPSEC_AUTHIP_QM_TRANSPORT_CONTEXT: return L"FWPM_IPSEC_AUTHIP_QM_TRANSPORT_CONTEXT";
+ case FWPM_IPSEC_AUTHIP_QM_TUNNEL_CONTEXT: return L"FWPM_IPSEC_AUTHIP_QM_TUNNEL_CONTEXT";
+ case FWPM_IPSEC_IKE_MM_CONTEXT: return L"FWPM_IPSEC_IKE_MM_CONTEXT";
+ case FWPM_IPSEC_AUTHIP_MM_CONTEXT: return L"FWPM_IPSEC_AUTHIP_MM_CONTEXT";
+ case FWPM_CLASSIFY_OPTIONS_CONTEXT: return L"FWPM_CLASSIFY_OPTIONS_CONTEXT";
+ case FWPM_GENERAL_CONTEXT: return L"FWPM_GENERAL_CONTEXT";
+ case FWPM_IPSEC_IKEV2_QM_TUNNEL_CONTEXT: return L"FWPM_IPSEC_IKEV2_QM_TUNNEL_CONTEXT";
+ case FWPM_IPSEC_IKEV2_MM_CONTEXT: return L"FWPM_IPSEC_IKEV2_MM_CONTEXT";
+ case FWPM_IPSEC_DOSP_CONTEXT: return L"FWPM_IPSEC_DOSP_CONTEXT";
+ case FWPM_IPSEC_IKEV2_QM_TRANSPORT_CONTEXT: return L"FWPM_IPSEC_IKEV2_QM_TRANSPORT_CONTEXT";
+ default: return L"[Unknown]";
+ }
+}
+
+std::wstring Direction(UINT32 direction)
+{
+ switch (direction)
+ {
+ case FWP_DIRECTION_IN: return L"In";
+ case FWP_DIRECTION_OUT: return L"Out";
+ case FWP_DIRECTION_FORWARD: return L"Forward";
+ default: return L"[Unknown]";
+ }
+}
+
+std::wstring FilterDecoration(IPropertyDecorator *decorator, UINT64 id)
+{
+ if (nullptr == decorator)
+ {
+ return L"";
+ }
+
+ return (InlineFormatter() << L" " << decorator->FilterDecoration(id)).str();
+}
+
+std::wstring LayerDecoration(IPropertyDecorator *decorator, UINT16 id)
+{
+ if (nullptr == decorator)
+ {
+ return L"";
+ }
+
+ return (InlineFormatter() << L" " << decorator->LayerDecoration(id)).str();
+}
+
+std::wstring LayerDecoration(IPropertyDecorator *decorator, const GUID &key)
+{
+ if (nullptr == decorator)
+ {
+ return L"";
+ }
+
+ return (InlineFormatter() << L" " << decorator->LayerDecoration(key)).str();
+}
+
+std::wstring ProviderDecoration(IPropertyDecorator *decorator, const GUID &key)
+{
+ if (nullptr == decorator)
+ {
+ return L"";
+ }
+
+ return (InlineFormatter() << L" " << decorator->ProviderDecoration(key)).str();
+}
+
+std::wstring SublayerDecoration(IPropertyDecorator *decorator, const GUID &key)
+{
+ if (nullptr == decorator)
+ {
+ return L"";
+ }
+
+ return (InlineFormatter() << L" " << decorator->SublayerDecoration(key)).str();
+}
+
+void AddStringProperty(PropertyList &props, const wchar_t *name, const wchar_t *value)
+{
+ if (nullptr == value || 0 == wcslen(value))
+ {
+ return;
+ }
+
+ props.add(name, value);
+}
+
+// This won't work because sometimes 0 is a valid flag value
+//template<typename T>
+//void AddFlagProperty(PropertyList &props, const std::wstring &name, T value, std::function<std::wstring(T)> formatter>
+
+} // namespace detail
+
+PropertyList SessionProperties(const FWPM_SESSION0 &session)
+{
+ PropertyList props;
+ InlineFormatter f;
+
+ props.add(L"key", common::string::FormatGuid(session.sessionKey));
+ detail::AddStringProperty(props, L"name", session.displayData.name);
+ detail::AddStringProperty(props, L"description", session.displayData.description);
+
+ props.add(L"flags", (f << session.flags << L" = " << detail::SessionFlags(session.flags)).str());
+ props.add(L"wait timeout", (f << session.txnWaitTimeoutInMSec).str());
+ props.add(L"sid", common::string::FormatSid(*session.sid));
+ props.add(L"username", session.username);
+ props.add(L"kernel", (session.kernelMode ? L"true" : L"false"));
+
+ return props;
+}
+
+PropertyList ProviderProperties(const FWPM_PROVIDER0 &provider)
+{
+ PropertyList props;
+ InlineFormatter f;
+
+ props.add(L"key", common::string::FormatGuid(provider.providerKey));
+ detail::AddStringProperty(props, L"name", provider.displayData.name);
+ detail::AddStringProperty(props, L"description", provider.displayData.description);
+
+ props.add(L"flags", (f << provider.flags << L" = " << detail::ProviderFlags(provider.flags)).str());
+
+ if (0 != provider.providerData.size)
+ {
+ props.add(L"provider data", (f << L"Present (" << provider.providerData.size << L" bytes)").str());
+ }
+
+ detail::AddStringProperty(props, L"service name", provider.serviceName);
+
+ return props;
+}
+
+PropertyList EventProperties(const FWPM_NET_EVENT0 &event, IPropertyDecorator *decorator)
+{
+ PropertyList props;
+ InlineFormatter f;
+
+ props.add(L"timestamp", common::string::FormatTime(event.header.timeStamp));
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET))
+ {
+ props.add(L"protocol", detail::FormatIpProtocol(event.header.ipProtocol));
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_IP_VERSION_SET)
+ && 0 != (event.header.flags & FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET))
+ {
+ if (event.header.ipVersion == FWP_IP_VERSION_V4)
+ {
+ props.add(L"local addr", common::string::FormatIpv4(event.header.localAddrV4));
+ }
+ else
+ {
+ props.add(L"local addr", common::string::FormatIpv6(event.header.localAddrV6.byteArray16));
+ }
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_IP_VERSION_SET)
+ && 0 != (event.header.flags & FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET))
+ {
+ if (event.header.ipVersion == FWP_IP_VERSION_V4)
+ {
+ props.add(L"remote addr", common::string::FormatIpv4(event.header.remoteAddrV4));
+ }
+ else
+ {
+ props.add(L"remote addr", common::string::FormatIpv6(event.header.remoteAddrV6.byteArray16));
+ }
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET))
+ {
+ props.add(L"local port", (f << event.header.localPort).str());
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_REMOTE_PORT_SET))
+ {
+ props.add(L"remote port", (f << event.header.remotePort).str());
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_APP_ID_SET))
+ {
+ auto begin = reinterpret_cast<wchar_t *>(event.header.appId.data);
+ auto end = begin + (event.header.appId.size / sizeof(wchar_t));
+
+ props.add(L"app id", std::wstring(begin, end));
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_USER_ID_SET))
+ {
+ props.add(L"user id", common::string::FormatSid(*event.header.userId));
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_SCOPE_ID_SET))
+ {
+ props.add(L"IPv6 scope id", (f << event.header.scopeId).str());
+ }
+
+ switch (event.type)
+ {
+ case FWPM_NET_EVENT_TYPE_IKEEXT_MM_FAILURE:
+ {
+ props.add(L"type", L"IKEEXT_MM_FAILURE");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_IKEEXT_QM_FAILURE:
+ {
+ props.add(L"type", L"IKEEXT_QM_FAILURE");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_IKEEXT_EM_FAILURE:
+ {
+ props.add(L"type", L"IKEEXT_EM_FAILURE");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP:
+ {
+ props.add(L"type", L"CLASSIFY_DROP");
+
+ props.add(L"filter id", (f << event.classifyDrop->filterId
+ << detail::FilterDecoration(decorator, event.classifyDrop->filterId)).str());
+
+ props.add(L"layer id", (f << event.classifyDrop->layerId
+ << detail::LayerDecoration(decorator, event.classifyDrop->layerId)).str());
+
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP:
+ {
+ props.add(L"type", L"IPSEC_KERNEL_DROP");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_IPSEC_DOSP_DROP:
+ {
+ props.add(L"type", L"IPSEC_DOSP_DROP");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_CLASSIFY_ALLOW:
+ {
+ props.add(L"type", L"CLASSIFY_ALLOW");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_CAPABILITY_DROP:
+ {
+ props.add(L"type", L"CAPABILITY_DROP");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_CAPABILITY_ALLOW:
+ {
+ props.add(L"type", L"CAPABILITY_ALLOW");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP_MAC:
+ {
+ props.add(L"type", L"CLASSIFY_DROP_MAC");
+ break;
+ }
+ default:
+ {
+ props.add(L"type", L"Unknown");
+ }
+ };
+
+ return props;
+}
+
+PropertyList EventProperties(const FWPM_NET_EVENT1 &event, IPropertyDecorator *decorator)
+{
+ //
+ // TODO-MAYBE: Restructure code to operate on individual elements of the structure
+ // then use upcasting and a single implementation for extracting the basic information.
+ //
+
+ PropertyList props;
+ InlineFormatter f;
+
+ props.add(L"timestamp", common::string::FormatTime(event.header.timeStamp));
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET))
+ {
+ props.add(L"protocol", detail::FormatIpProtocol(event.header.ipProtocol));
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_IP_VERSION_SET)
+ && 0 != (event.header.flags & FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET))
+ {
+ if (event.header.ipVersion == FWP_IP_VERSION_V4)
+ {
+ props.add(L"local addr", common::string::FormatIpv4(event.header.localAddrV4));
+ }
+ else
+ {
+ props.add(L"local addr", common::string::FormatIpv6(event.header.localAddrV6.byteArray16));
+ }
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_IP_VERSION_SET)
+ && 0 != (event.header.flags & FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET))
+ {
+ if (event.header.ipVersion == FWP_IP_VERSION_V4)
+ {
+ props.add(L"remote addr", common::string::FormatIpv4(event.header.remoteAddrV4));
+ }
+ else
+ {
+ props.add(L"remote addr", common::string::FormatIpv6(event.header.remoteAddrV6.byteArray16));
+ }
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET))
+ {
+ props.add(L"local port", (f << event.header.localPort).str());
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_REMOTE_PORT_SET))
+ {
+ props.add(L"remote port", (f << event.header.remotePort).str());
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_APP_ID_SET))
+ {
+ auto begin = reinterpret_cast<wchar_t *>(event.header.appId.data);
+ auto end = begin + (event.header.appId.size / sizeof(wchar_t));
+
+ props.add(L"app id", std::wstring(begin, end));
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_USER_ID_SET))
+ {
+ props.add(L"user id", common::string::FormatSid(*event.header.userId));
+ }
+
+ if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_SCOPE_ID_SET))
+ {
+ props.add(L"IPv6 scope id", (f << event.header.scopeId).str());
+ }
+
+ switch (event.type)
+ {
+ case FWPM_NET_EVENT_TYPE_IKEEXT_MM_FAILURE:
+ {
+ props.add(L"type", L"IKEEXT_MM_FAILURE");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_IKEEXT_QM_FAILURE:
+ {
+ props.add(L"type", L"IKEEXT_QM_FAILURE");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_IKEEXT_EM_FAILURE:
+ {
+ props.add(L"type", L"IKEEXT_EM_FAILURE");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP:
+ {
+ props.add(L"type", L"CLASSIFY_DROP");
+
+ props.add(L"filter id", (f << event.classifyDrop->filterId
+ << detail::FilterDecoration(decorator, event.classifyDrop->filterId)).str());
+
+ props.add(L"layer id", (f << event.classifyDrop->layerId
+ << detail::LayerDecoration(decorator, event.classifyDrop->layerId)).str());
+
+ props.add(L"direction", detail::Direction(event.classifyDrop->msFwpDirection));
+
+ if (1 == event.classifyDrop->isLoopback)
+ {
+ props.add(L"loopback", L"True");
+ }
+
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP:
+ {
+ props.add(L"type", L"IPSEC_KERNEL_DROP");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_IPSEC_DOSP_DROP:
+ {
+ props.add(L"type", L"IPSEC_DOSP_DROP");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_CLASSIFY_ALLOW:
+ {
+ props.add(L"type", L"CLASSIFY_ALLOW");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_CAPABILITY_DROP:
+ {
+ props.add(L"type", L"CAPABILITY_DROP");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_CAPABILITY_ALLOW:
+ {
+ props.add(L"type", L"CAPABILITY_ALLOW");
+ break;
+ }
+ case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP_MAC:
+ {
+ props.add(L"type", L"CLASSIFY_DROP_MAC");
+ break;
+ }
+ default:
+ {
+ props.add(L"type", L"Unknown");
+ }
+ };
+
+ return props;
+}
+
+PropertyList FilterProperties(const FWPM_FILTER0 &filter, IPropertyDecorator *decorator)
+{
+ PropertyList props;
+ InlineFormatter f;
+
+ props.add(L"key", common::string::FormatGuid(filter.filterKey));
+ detail::AddStringProperty(props, L"name", filter.displayData.name);
+ detail::AddStringProperty(props, L"description", filter.displayData.description);
+
+ props.add(L"flags", (f << filter.flags << L" = " << detail::FilterFlags(filter.flags)).str());
+
+ if (nullptr != filter.providerKey)
+ {
+ props.add(L"provider key", (f << common::string::FormatGuid(*filter.providerKey)
+ << detail::ProviderDecoration(decorator, *filter.providerKey)).str());
+ }
+
+ if (0 != filter.providerData.size)
+ {
+ props.add(L"provider data", (f << L"Present (" << filter.providerData.size << L" bytes)").str());
+ }
+
+ props.add(L"layer key", (f << common::string::FormatGuid(filter.layerKey)
+ << detail::LayerDecoration(decorator, filter.layerKey)).str());
+
+ props.add(L"sublayer key", (f << common::string::FormatGuid(filter.subLayerKey)
+ << detail::SublayerDecoration(decorator, filter.subLayerKey)).str());
+
+ if (FWP_UINT64 == filter.weight.type)
+ {
+ props.add(L"weight", (f << *filter.weight.uint64 << L" (exact)").str());
+ }
+ else if (FWP_UINT8 == filter.weight.type)
+ {
+ props.add(L"weight", (f << filter.weight.uint8 << L" (relative 0-15)").str());
+ }
+ else
+ {
+ props.add(L"weight", L"Automatic");
+ }
+
+ props.add(L"num conditions", (f << filter.numFilterConditions).str());
+ props.add(L"conditions", L"TODO");
+
+ if (FWP_ACTION_BLOCK == filter.action.type)
+ {
+ props.add(L"action", L"Block");
+ props.add(L"filter type", common::string::FormatGuid(filter.action.filterType));
+ }
+ else if (FWP_ACTION_PERMIT == filter.action.type)
+ {
+ props.add(L"action", L"Permit");
+ props.add(L"filter type", common::string::FormatGuid(filter.action.filterType));
+ }
+ else if (FWP_ACTION_CALLOUT_TERMINATING == filter.action.type)
+ {
+ props.add(L"action", L"Callout terminating");
+ props.add(L"callout key", common::string::FormatGuid(filter.action.calloutKey));
+ }
+ else if (FWP_ACTION_CALLOUT_INSPECTION == filter.action.type)
+ {
+ props.add(L"action", L"Callout inspection");
+ props.add(L"callout key", common::string::FormatGuid(filter.action.calloutKey));
+ }
+ else if (FWP_ACTION_CALLOUT_UNKNOWN == filter.action.type)
+ {
+ props.add(L"action", L"Callout unknown");
+ props.add(L"callout key", common::string::FormatGuid(filter.action.calloutKey));
+ }
+
+ if (0 != (filter.flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT))
+ {
+ props.add(L"context", L"Provider context");
+ }
+ else
+ {
+ props.add(L"context", L"Raw context");
+ }
+
+ props.add(L"filter id", (f << filter.filterId).str());
+
+ if (FWP_UINT64 == filter.effectiveWeight.type)
+ {
+ props.add(L"effective weight", (f << *filter.effectiveWeight.uint64 << L" (exact)").str());
+ }
+ else if (FWP_UINT8 == filter.effectiveWeight.type)
+ {
+ props.add(L"effective weight", (f << filter.effectiveWeight.uint8 << L" (relative 0-15)").str());
+ }
+ else
+ {
+ props.add(L"effective weight", L"Automatic");
+ }
+
+ return props;
+}
+
+PropertyList LayerProperties(const FWPM_LAYER0 &layer, IPropertyDecorator *decorator)
+{
+ PropertyList props;
+ InlineFormatter f;
+
+ props.add(L"key", common::string::FormatGuid(layer.layerKey));
+ detail::AddStringProperty(props, L"name", layer.displayData.name);
+ detail::AddStringProperty(props, L"description", layer.displayData.description);
+
+ props.add(L"flags", (f << layer.flags << L" = " << detail::LayerFlags(layer.flags)).str());
+ props.add(L"num fields", (f << layer.numFields).str());
+ props.add(L"field array", L"TODO");
+
+ props.add(L"default sublayer", (f << common::string::FormatGuid(layer.defaultSubLayerKey)
+ << detail::SublayerDecoration(decorator, layer.defaultSubLayerKey)).str());
+
+ props.add(L"layer id", (f << layer.layerId).str());
+
+ return props;
+}
+
+PropertyList ProviderContextProperties(const FWPM_PROVIDER_CONTEXT0 &context, IPropertyDecorator *decorator)
+{
+ PropertyList props;
+ InlineFormatter f;
+
+ props.add(L"key", common::string::FormatGuid(context.providerContextKey));
+ detail::AddStringProperty(props, L"name", context.displayData.name);
+ detail::AddStringProperty(props, L"description", context.displayData.description);
+
+ if (0 != (context.flags & FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT))
+ {
+ props.add(L"flags", L"FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT");
+ }
+ else
+ {
+ props.add(L"flags", (f << context.flags).str());
+ }
+
+ if (nullptr != context.providerKey)
+ {
+ props.add(L"provider key", (f << common::string::FormatGuid(*context.providerKey)
+ << detail::ProviderDecoration(decorator, *context.providerKey)).str());
+ }
+
+ if (0 != context.providerData.size)
+ {
+ props.add(L"provider data", (f << L"Present (" << context.providerData.size << L" bytes)").str());
+ }
+
+ props.add(L"context type", detail::ProviderContextType(context.type));
+ props.add(L"id", (f << context.providerContextId).str());
+
+ return props;
+}
+
+PropertyList SublayerProperties(const FWPM_SUBLAYER0 &sublayer, IPropertyDecorator *decorator)
+{
+ PropertyList props;
+ InlineFormatter f;
+
+ props.add(L"key", common::string::FormatGuid(sublayer.subLayerKey));
+ detail::AddStringProperty(props, L"name", sublayer.displayData.name);
+ detail::AddStringProperty(props, L"description", sublayer.displayData.description);
+
+ if (0 != (sublayer.flags & FWPM_SUBLAYER_FLAG_PERSISTENT))
+ {
+ props.add(L"flags", L"FWPM_SUBLAYER_FLAG_PERSISTENT");
+ }
+ else
+ {
+ props.add(L"flags", (f << sublayer.flags).str());
+ }
+
+ if (nullptr != sublayer.providerKey)
+ {
+ props.add(L"provider key", (f << common::string::FormatGuid(*sublayer.providerKey)
+ << detail::ProviderDecoration(decorator, *sublayer.providerKey)).str());
+ }
+
+ if (0 != sublayer.providerData.size)
+ {
+ props.add(L"provider data", (f << L"Present (" << sublayer.providerData.size << L" bytes)").str());
+ }
+
+ props.add(L"weight", (f << sublayer.weight).str());
+
+ return props;
+}
diff --git a/windows/winfw/src/extras/cli/objectproperties.h b/windows/winfw/src/extras/cli/objectproperties.h
new file mode 100644
index 0000000000..a4f349f828
--- /dev/null
+++ b/windows/winfw/src/extras/cli/objectproperties.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "propertylist.h"
+#include "ipropertydecorator.h"
+#include <windows.h>
+#include <fwpmu.h>
+
+PropertyList SessionProperties(const FWPM_SESSION0 &session);
+PropertyList ProviderProperties(const FWPM_PROVIDER0 &provider);
+PropertyList EventProperties(const FWPM_NET_EVENT0 &event, IPropertyDecorator *decorator = nullptr);
+PropertyList EventProperties(const FWPM_NET_EVENT1 &event, IPropertyDecorator *decorator = nullptr);
+PropertyList FilterProperties(const FWPM_FILTER0 &filter, IPropertyDecorator *decorator = nullptr);
+PropertyList LayerProperties(const FWPM_LAYER0 &layer, IPropertyDecorator *decorator = nullptr);
+PropertyList ProviderContextProperties(const FWPM_PROVIDER_CONTEXT0 &context, IPropertyDecorator *decorator = nullptr);
+PropertyList SublayerProperties(const FWPM_SUBLAYER0 &sublayer, IPropertyDecorator *decorator = nullptr);
diff --git a/windows/winfw/src/extras/cli/propertydecorator.cpp b/windows/winfw/src/extras/cli/propertydecorator.cpp
new file mode 100644
index 0000000000..115c628c27
--- /dev/null
+++ b/windows/winfw/src/extras/cli/propertydecorator.cpp
@@ -0,0 +1,92 @@
+#include "stdafx.h"
+#include "propertydecorator.h"
+#include "libwfp/objectexplorer.h"
+#include "libcommon/string.h"
+#include "inlineformatter.h"
+#include <cwchar>
+
+namespace detail
+{
+
+std::wstring Format(const wchar_t *in_name, const wchar_t *in_desc)
+{
+ auto haveName = (nullptr != in_name && 0 != wcslen(in_name));
+ auto haveDesc = (nullptr != in_desc && 0 != wcslen(in_desc));
+
+ std::wstring name = (haveName ? in_name : L"n/a");
+ std::wstring desc = (haveDesc ? common::string::Summary(in_desc, 50) : L"n/a");
+
+ return (InlineFormatter() << L"[" << name << L", " << desc << L"]").str();
+}
+
+} // namespace detail
+
+PropertyDecorator::PropertyDecorator(std::shared_ptr<wfp::FilterEngine> engine)
+ : m_engine(engine)
+{
+}
+
+std::wstring PropertyDecorator::FilterDecoration(UINT64 id)
+{
+ std::wstring brief = L"[n/a]";
+
+ wfp::ObjectExplorer::GetFilter(*m_engine, id, [&brief](const FWPM_FILTER0 &filter)
+ {
+ brief = detail::Format(filter.displayData.name, filter.displayData.description);
+ return true;
+ });
+
+ return brief;
+}
+
+std::wstring PropertyDecorator::LayerDecoration(UINT16 id)
+{
+ std::wstring brief = L"[n/a]";
+
+ wfp::ObjectExplorer::GetLayer(*m_engine, id, [&brief](const FWPM_LAYER0 &layer)
+ {
+ brief = detail::Format(layer.displayData.name, layer.displayData.description);
+ return true;
+ });
+
+ return brief;
+}
+
+std::wstring PropertyDecorator::LayerDecoration(const GUID &key)
+{
+ std::wstring brief = L"[n/a]";
+
+ wfp::ObjectExplorer::GetLayer(*m_engine, key, [&brief](const FWPM_LAYER0 &layer)
+ {
+ brief = detail::Format(layer.displayData.name, layer.displayData.description);
+ return true;
+ });
+
+ return brief;
+}
+
+std::wstring PropertyDecorator::ProviderDecoration(const GUID &key)
+{
+ std::wstring brief = L"[n/a]";
+
+ wfp::ObjectExplorer::GetProvider(*m_engine, key, [&brief](const FWPM_PROVIDER0 &provider)
+ {
+ brief = detail::Format(provider.displayData.name, provider.displayData.description);
+ return true;
+ });
+
+ return brief;
+}
+
+std::wstring PropertyDecorator::SublayerDecoration(const GUID &key)
+{
+ std::wstring brief = L"[n/a]";
+
+ wfp::ObjectExplorer::GetSublayer(*m_engine, key, [&brief](const FWPM_SUBLAYER0 &sublayer)
+ {
+ brief = detail::Format(sublayer.displayData.name, sublayer.displayData.description);
+ return true;
+ });
+
+ return brief;
+}
diff --git a/windows/winfw/src/extras/cli/propertydecorator.h b/windows/winfw/src/extras/cli/propertydecorator.h
new file mode 100644
index 0000000000..b021d275e1
--- /dev/null
+++ b/windows/winfw/src/extras/cli/propertydecorator.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "ipropertydecorator.h"
+#include "libwfp/filterengine.h"
+#include <memory>
+
+class PropertyDecorator : public IPropertyDecorator
+{
+public:
+
+ PropertyDecorator(std::shared_ptr<wfp::FilterEngine> engine);
+
+ //
+ // The format of the returned string:
+ // [name, first 50 chars of description]
+ //
+
+ std::wstring FilterDecoration(UINT64 id) override;
+ std::wstring LayerDecoration(UINT16 id) override;
+ std::wstring LayerDecoration(const GUID &key) override;
+ std::wstring ProviderDecoration(const GUID &key) override;
+ std::wstring SublayerDecoration(const GUID &key) override;
+
+private:
+
+ std::shared_ptr<wfp::FilterEngine> m_engine;
+};
diff --git a/windows/winfw/src/extras/cli/propertylist.h b/windows/winfw/src/extras/cli/propertylist.h
new file mode 100644
index 0000000000..b5d9d71499
--- /dev/null
+++ b/windows/winfw/src/extras/cli/propertylist.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <string>
+#include <utility>
+#include <vector>
+
+class PropertyList
+{
+public:
+
+ struct Property
+ {
+ Property(const std::wstring &n, const std::wstring &v)
+ : name(n), value(v)
+ {
+ }
+
+ Property(std::wstring &&n, std::wstring &&v)
+ : name(n), value(v)
+ {
+ }
+
+ std::wstring name;
+ std::wstring value;
+ };
+
+ void add(const std::wstring &name, const std::wstring &value)
+ {
+ m_properties.emplace_back(name, value);
+ }
+
+ void add(std::wstring &&name, std::wstring &&value)
+ {
+ m_properties.emplace_back(name, value);
+ }
+
+ const std::vector<Property> &list()
+ {
+ return m_properties;
+ }
+
+ std::vector<Property>::const_iterator begin() const
+ {
+ return m_properties.begin();
+ }
+
+ std::vector<Property>::const_iterator end() const
+ {
+ return m_properties.end();
+ }
+
+private:
+
+ std::vector<Property> m_properties;
+};
diff --git a/windows/winfw/src/extras/cli/stdafx.cpp b/windows/winfw/src/extras/cli/stdafx.cpp
new file mode 100644
index 0000000000..62355160c9
--- /dev/null
+++ b/windows/winfw/src/extras/cli/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// cli.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/windows/winfw/src/extras/cli/stdafx.h b/windows/winfw/src/extras/cli/stdafx.h
new file mode 100644
index 0000000000..b005a839de
--- /dev/null
+++ b/windows/winfw/src/extras/cli/stdafx.h
@@ -0,0 +1,15 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#include <stdio.h>
+#include <tchar.h>
+
+
+
+// TODO: reference additional headers your program requires here
diff --git a/windows/winfw/src/extras/cli/subcommanddispatcher.cpp b/windows/winfw/src/extras/cli/subcommanddispatcher.cpp
new file mode 100644
index 0000000000..4bb7868cd7
--- /dev/null
+++ b/windows/winfw/src/extras/cli/subcommanddispatcher.cpp
@@ -0,0 +1,27 @@
+#include "stdafx.h"
+#include "subcommanddispatcher.h"
+#include "libcommon/string.h"
+#include <sstream>
+#include <stdexcept>
+#include <utility>
+
+void SubcommandDispatcher::addSubcommand(const std::wstring &command, Handler handler)
+{
+ m_commands.insert(std::make_pair(command, handler));
+}
+
+void SubcommandDispatcher::dispatch(const std::wstring &command, const std::vector<std::wstring> &arguments)
+{
+ auto selectedCommand = m_commands.find(command);
+
+ if (m_commands.end() == selectedCommand)
+ {
+ std::wstringstream ss;
+
+ ss << L"Unsupported subcommand '" << command << "'. Cannot complete request.";
+
+ throw std::runtime_error(common::string::ToAnsi(ss.str()).c_str());
+ }
+
+ selectedCommand->second(common::string::SplitKeyValuePairs(arguments));
+}
diff --git a/windows/winfw/src/extras/cli/subcommanddispatcher.h b/windows/winfw/src/extras/cli/subcommanddispatcher.h
new file mode 100644
index 0000000000..a480ec8104
--- /dev/null
+++ b/windows/winfw/src/extras/cli/subcommanddispatcher.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "libcommon/string.h"
+#include <functional>
+#include <string>
+#include <unordered_map>
+
+class SubcommandDispatcher
+{
+ typedef std::function<void(const common::string::KeyValuePairs &)> Handler;
+
+public:
+
+ void addSubcommand(const std::wstring &command, Handler handler);
+ void dispatch(const std::wstring &command, const std::vector<std::wstring> &arguments);
+
+private:
+
+ std::unordered_map<std::wstring, Handler> m_commands;
+};
diff --git a/windows/winfw/src/extras/cli/targetver.h b/windows/winfw/src/extras/cli/targetver.h
new file mode 100644
index 0000000000..ae4a5c032c
--- /dev/null
+++ b/windows/winfw/src/extras/cli/targetver.h
@@ -0,0 +1,12 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <WinSDKVer.h>
+
+#define _WIN32_WINNT _WIN32_WINNT_WIN7
+
+#include <SDKDDKVer.h>
diff --git a/windows/winfw/src/extras/cli/util.cpp b/windows/winfw/src/extras/cli/util.cpp
new file mode 100644
index 0000000000..6181e68639
--- /dev/null
+++ b/windows/winfw/src/extras/cli/util.cpp
@@ -0,0 +1,62 @@
+#include "stdafx.h"
+#include "util.h"
+#include "inlineformatter.h"
+#include "libcommon/string.h"
+#include <string>
+
+void PrettyPrintProperties(MessageSink messageSink, PrettyPrintOptions options, const PropertyList &properties)
+{
+ size_t longestName = 0;
+
+ for (auto &property : properties)
+ {
+ longestName = max(longestName, property.name.size());
+ }
+
+ //
+ // Format is:
+ // indent, name, [separator], tab, [tab, ...]
+ //
+
+ const auto separator = std::wstring(options.useSeparator ? L":" : L"");
+
+ auto insert = options.indent + longestName + separator.size();
+
+ if (0 == (insert % 8))
+ {
+ insert += 8;
+ }
+ else
+ {
+ insert = ((insert / 8) + 1) * 8;
+ }
+
+ std::wstring indenter(options.indent, L' ');
+ InlineFormatter f;
+
+ for (auto &property : properties)
+ {
+ auto at = options.indent + property.name.size() + separator.size();
+ auto distance = insert - at;
+ auto tabs = (0 == (distance % 8) ? distance / 8 : (distance / 8) + 1);
+
+ messageSink((f << indenter << property.name << separator
+ << std::wstring(tabs, L'\t') << property.value).str());
+ }
+}
+
+std::wstring GetArgumentValue(const common::string::KeyValuePairs &arguments, const std::wstring &key)
+{
+ auto arg = arguments.find(key);
+
+ if (arguments.end() == arg)
+ {
+ std::wstringstream ss;
+
+ ss << L"Missing argument: '" << key << L"'";
+
+ throw std::runtime_error(common::string::ToAnsi(ss.str()));
+ }
+
+ return arg->second;
+}
diff --git a/windows/winfw/src/extras/cli/util.h b/windows/winfw/src/extras/cli/util.h
new file mode 100644
index 0000000000..3919e03422
--- /dev/null
+++ b/windows/winfw/src/extras/cli/util.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <string>
+#include "propertylist.h"
+#include "libwfp/filterengine.h"
+#include "libcommon/string.h"
+
+typedef std::function<void(const std::wstring &)> MessageSink;
+
+struct PrettyPrintOptions
+{
+ size_t indent;
+ bool useSeparator;
+};
+
+void PrettyPrintProperties(MessageSink, PrettyPrintOptions options, const PropertyList &properties);
+
+std::wstring GetArgumentValue(const common::string::KeyValuePairs &arguments, const std::wstring &key);
diff --git a/windows/winfw/src/winfw/dllmain.cpp b/windows/winfw/src/winfw/dllmain.cpp
new file mode 100644
index 0000000000..21717c7d4a
--- /dev/null
+++ b/windows/winfw/src/winfw/dllmain.cpp
@@ -0,0 +1,11 @@
+#include "stdafx.h"
+#include <windows.h>
+
+BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID)
+{
+ //
+ // Avoid doing work in DllMain since the loader lock is held
+ //
+
+ return TRUE;
+}
diff --git a/windows/winfw/src/winfw/fwcontext.cpp b/windows/winfw/src/winfw/fwcontext.cpp
new file mode 100644
index 0000000000..b97f350ec7
--- /dev/null
+++ b/windows/winfw/src/winfw/fwcontext.cpp
@@ -0,0 +1,164 @@
+#include "stdafx.h"
+#include "fwcontext.h"
+#include "mullvadobjects.h"
+#include "rules/blockall.h"
+#include "rules/ifirewallrule.h"
+#include "rules/permitdhcp.h"
+#include "rules/permitlan.h"
+#include "rules/permitlanservice.h"
+#include "rules/permitloopback.h"
+#include "rules/permitvpnrelay.h"
+#include "rules/permitvpntunnel.h"
+#include "rules/restrictdns.h"
+#include "libwfp/transaction.h"
+#include "libwfp/filterengine.h"
+#include <functional>
+#include <stdexcept>
+#include <utility>
+
+namespace
+{
+
+rules::PermitVpnRelay::Protocol TranslateProtocol(WinFwProtocol protocol)
+{
+ switch (protocol)
+ {
+ case Tcp: return rules::PermitVpnRelay::Protocol::Tcp;
+ case Udp: return rules::PermitVpnRelay::Protocol::Udp;
+ default:
+ {
+ throw std::logic_error("Missing case handler in switch clause");
+ }
+ };
+}
+
+void AppendSettingsRules(FwContext::Ruleset &ruleset, const WinFwSettings &settings)
+{
+ if (settings.permitDhcp)
+ {
+ ruleset.emplace_back(std::make_unique<rules::PermitDhcp>());
+ }
+
+ if (settings.permitLan)
+ {
+ ruleset.emplace_back(std::make_unique<rules::PermitLan>());
+ ruleset.emplace_back(std::make_unique<rules::PermitLanService>());
+ }
+}
+
+void AppendNetBlockedRules(FwContext::Ruleset &ruleset)
+{
+ ruleset.emplace_back(std::make_unique<rules::BlockAll>());
+ ruleset.emplace_back(std::make_unique<rules::PermitLoopback>());
+}
+
+} // anonymous namespace
+
+FwContext::FwContext(uint32_t timeout)
+ : m_baseline(0)
+{
+ auto engine = wfp::FilterEngine::DynamicSession(timeout);
+
+ //
+ // Pass engine ownership to "session controller"
+ //
+ m_sessionController = std::make_unique<SessionController>(std::move(engine));
+
+ if (false == applyBaseConfiguration())
+ {
+ throw std::runtime_error("Failed to apply base configuration in BFE.");
+ }
+
+ m_baseline = m_sessionController->checkpoint();
+}
+
+bool FwContext::applyPolicyConnecting(const WinFwSettings &settings, const WinFwRelay &relay)
+{
+ Ruleset ruleset;
+
+ AppendNetBlockedRules(ruleset);
+ AppendSettingsRules(ruleset, settings);
+
+ ruleset.emplace_back(std::make_unique<rules::PermitVpnRelay>(
+ wfp::IpAddress(relay.ip),
+ relay.port,
+ TranslateProtocol(relay.protocol)
+ ));
+
+ return applyRuleset(ruleset);
+}
+
+bool FwContext::applyPolicyConnected(const WinFwSettings &settings, const WinFwRelay &relay, const wchar_t *tunnelInterfaceAlias, const wchar_t *primaryDns)
+{
+ Ruleset ruleset;
+
+ AppendNetBlockedRules(ruleset);
+ AppendSettingsRules(ruleset, settings);
+
+ ruleset.emplace_back(std::make_unique<rules::PermitVpnRelay>(
+ wfp::IpAddress(relay.ip),
+ relay.port,
+ TranslateProtocol(relay.protocol)
+ ));
+
+ ruleset.emplace_back(std::make_unique<rules::PermitVpnTunnel>(
+ tunnelInterfaceAlias
+ ));
+
+ ruleset.emplace_back(std::make_unique<rules::RestrictDns>(
+ tunnelInterfaceAlias,
+ wfp::IpAddress(primaryDns)
+ ));
+
+ return applyRuleset(ruleset);
+}
+
+bool FwContext::applyPolicyNetBlocked()
+{
+ Ruleset ruleset;
+
+ AppendNetBlockedRules(ruleset);
+
+ return applyRuleset(ruleset);
+}
+
+bool FwContext::reset()
+{
+ return m_sessionController->executeTransaction([this]()
+ {
+ m_sessionController->revert(m_baseline);
+ return true;
+ });
+}
+
+bool FwContext::applyRuleset(const Ruleset &ruleset)
+{
+ return m_sessionController->executeTransaction([&]()
+ {
+ m_sessionController->revert(m_baseline);
+
+ for (const auto &rule : ruleset)
+ {
+ if (false == rule->apply(*m_sessionController))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ });
+}
+
+bool FwContext::applyBaseConfiguration()
+{
+ return m_sessionController->executeTransaction([&]()
+ {
+ //
+ // Install structural objects
+ //
+
+ return m_sessionController->addProvider(*MullvadObjects::Provider())
+ && m_sessionController->addSublayer(*MullvadObjects::SublayerWhitelist())
+ && m_sessionController->addSublayer(*MullvadObjects::SublayerBlacklist());
+ });
+}
diff --git a/windows/winfw/src/winfw/fwcontext.h b/windows/winfw/src/winfw/fwcontext.h
new file mode 100644
index 0000000000..00f952441e
--- /dev/null
+++ b/windows/winfw/src/winfw/fwcontext.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "winfw.h"
+#include "sessioncontroller.h"
+#include "rules/ifirewallrule.h"
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+class FwContext
+{
+public:
+
+ FwContext(uint32_t timeout);
+
+ bool applyPolicyConnecting(const WinFwSettings &settings, const WinFwRelay &relay);
+ bool applyPolicyConnected(const WinFwSettings &settings, const WinFwRelay &relay, const wchar_t *tunnelInterfaceAlias, const wchar_t *primaryDns);
+ bool applyPolicyNetBlocked();
+
+ bool reset();
+
+ using Ruleset = std::vector<std::unique_ptr<rules::IFirewallRule> >;
+
+private:
+
+ FwContext(const FwContext &) = delete;
+ FwContext &operator=(const FwContext &) = delete;
+
+ bool applyBaseConfiguration();
+ bool applyRuleset(const Ruleset &ruleset);
+
+ std::unique_ptr<SessionController> m_sessionController;
+
+ uint32_t m_baseline;
+};
diff --git a/windows/winfw/src/winfw/iobjectinstaller.h b/windows/winfw/src/winfw/iobjectinstaller.h
new file mode 100644
index 0000000000..1d38d427c3
--- /dev/null
+++ b/windows/winfw/src/winfw/iobjectinstaller.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "libwfp/iconditionbuilder.h"
+#include "libwfp/filterbuilder.h"
+#include "libwfp/providerbuilder.h"
+#include "libwfp/sublayerbuilder.h"
+
+struct IObjectInstaller
+{
+ virtual ~IObjectInstaller() = 0
+ {
+ }
+
+ virtual bool addProvider(wfp::ProviderBuilder &providerBuilder) = 0;
+ virtual bool addSublayer(wfp::SublayerBuilder &sublayerBuilder) = 0;
+ virtual bool addFilter(wfp::FilterBuilder &filterBuilder, const wfp::IConditionBuilder &conditionBuilder) = 0;
+};
diff --git a/windows/winfw/src/winfw/mullvadguids.cpp b/windows/winfw/src/winfw/mullvadguids.cpp
new file mode 100644
index 0000000000..dbe00b6538
--- /dev/null
+++ b/windows/winfw/src/winfw/mullvadguids.cpp
@@ -0,0 +1,380 @@
+#include "stdafx.h"
+#include "mullvadguids.h"
+
+//static
+const GUID &MullvadGuids::Provider()
+{
+ static const GUID g =
+ {
+ 0x21e1dab8,
+ 0xb9db,
+ 0x43c0,
+ { 0xb3, 0x43, 0xeb, 0x93, 0x65, 0xc7, 0xbd, 0xd2 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::SublayerWhitelist()
+{
+ static const GUID g =
+ {
+ 0x11d1a31a,
+ 0xd7fa,
+ 0x469b,
+ { 0xbc, 0x21, 0xcc, 0xe9, 0x2e, 0x35, 0xfe, 0x90 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::SublayerBlacklist()
+{
+ static const GUID g =
+ {
+ 0x843b74f0,
+ 0xb499,
+ 0x499a,
+ { 0xac, 0xe3, 0xf9, 0xee, 0xa2, 0x4, 0x89, 0xc1 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterBlockAll_Outbound_Ipv4()
+{
+ static const GUID g =
+ {
+ 0xa81c5411,
+ 0xfd0,
+ 0x43a9,
+ { 0xa9, 0xbe, 0x31, 0x3f, 0x29, 0x9d, 0xe6, 0x4f }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterBlockAll_Outbound_Ipv6()
+{
+ static const GUID g =
+ {
+ 0x8ae5c389,
+ 0xd604,
+ 0x43df,
+ { 0x87, 0x4a, 0x5c, 0x86, 0x76, 0xc9, 0xc2, 0xb8 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterBlockAll_Inbound_Ipv4()
+{
+ static const GUID g =
+ {
+ 0x86d07155,
+ 0x885f,
+ 0x409a,
+ { 0x8f, 0x22, 0x1, 0x9f, 0x87, 0x7a, 0xe4, 0x9 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterBlockAll_Inbound_Ipv6()
+{
+ static const GUID g =
+ {
+ 0x18b8c1d2,
+ 0x5910,
+ 0x4b51,
+ { 0xa5, 0x48, 0x1e, 0xfc, 0xd5, 0x4b, 0x63, 0xe9 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitLan_10_8()
+{
+ static const GUID g =
+ {
+ 0x73fe6348,
+ 0x62f4,
+ 0x4686,
+ { 0x95, 0x47, 0x51, 0xa8, 0x21, 0xb, 0xa3, 0x8f }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitLan_172_16_12()
+{
+ static const GUID g =
+ {
+ 0x7a38dae,
+ 0x150f,
+ 0x47f1,
+ { 0xa6, 0xac, 0x99, 0x3, 0x48, 0x53, 0x83, 0x26 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitLan_192_168_16()
+{
+ static const GUID g =
+ {
+ 0x518bfc38,
+ 0xa7c5,
+ 0x42fe,
+ { 0xa3, 0xf2, 0xe1, 0x56, 0x24, 0xd7, 0x86, 0x1c }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitLan_Multicast()
+{
+ static const GUID g =
+ {
+ 0xea5e136b,
+ 0xd951,
+ 0x4263,
+ { 0x99, 0xd8, 0x85, 0xc3, 0xf6, 0x4b, 0xda, 0xe9 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitLanService_10_8()
+{
+ static const GUID g =
+ {
+ 0x24ed3b23,
+ 0x5d5a,
+ 0x4f1e,
+ { 0x8c, 0xfa, 0xfd, 0x68, 0x79, 0x6a, 0x83, 0x8a }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitLanService_172_16_12()
+{
+ static const GUID g =
+ {
+ 0xa925dc62,
+ 0x54ea,
+ 0x46f5,
+ { 0x9d, 0x37, 0xa9, 0x5a, 0xf2, 0x84, 0xc3, 0x6f }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitLanService_192_168_16()
+{
+ static const GUID g =
+ {
+ 0x97fd73cb,
+ 0x9bf0,
+ 0x47f2,
+ { 0x98, 0x69, 0xd1, 0x5e, 0xf3, 0x5c, 0x3a, 0x8 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitLoopback_Outbound_Ipv4()
+{
+ static const GUID g =
+ {
+ 0xd9ff592d,
+ 0xbe46,
+ 0x49fb,
+ { 0x97, 0xec, 0x71, 0x1, 0x3c, 0x12, 0xb8, 0x30 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitLoopback_Outbound_Ipv6()
+{
+ static const GUID g =
+ {
+ 0x764d4944,
+ 0x8a1e,
+ 0x4d96,
+ { 0xbf, 0xf0, 0x8d, 0xa6, 0x4f, 0x31, 0x44, 0xa2 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitLoopback_Inbound_Ipv4()
+{
+ static const GUID g =
+ {
+ 0xb8efb500,
+ 0xc51,
+ 0x4550,
+ { 0xbf, 0x5c, 0x48, 0x54, 0xa6, 0xc8, 0x48, 0xb9 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitLoopback_Inbound_Ipv6()
+{
+ static const GUID g =
+ {
+ 0xbad325b0,
+ 0x736c,
+ 0x4e67,
+ { 0x8b, 0x37, 0x62, 0xb2, 0xdb, 0xe7, 0xd6, 0xeb }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitDhcp_Outbound_Request()
+{
+ static const GUID g =
+ {
+ 0x6cf1687b,
+ 0x35e9,
+ 0x4d18,
+ { 0xa2, 0x3, 0xb2, 0x6b, 0x71, 0xa9, 0x5f, 0x8d }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitDhcp_Inbound_Response()
+{
+ static const GUID g =
+ {
+ 0x2db298d7,
+ 0x4108,
+ 0x47ff,
+ { 0x85, 0x99, 0xaf, 0xa5, 0xcb, 0x95, 0x9c, 0x25 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitVpnRelay()
+{
+ static const GUID g =
+ {
+ 0x160c205d,
+ 0xdb40,
+ 0x4f79,
+ { 0x90, 0x6d, 0xfd, 0xa1, 0xe1, 0xc1, 0x8a, 0x70 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitVpnTunnel_Outbound_Ipv4()
+{
+ static const GUID g =
+ {
+ 0xdfdcbb76,
+ 0x2284,
+ 0x4b03,
+ { 0x93, 0x4e, 0x93, 0xe5, 0xd3, 0x84, 0x8c, 0xf1 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterPermitVpnTunnel_Outbound_Ipv6()
+{
+ static const GUID g =
+ {
+ 0x9b1fa7d,
+ 0x843b,
+ 0x4946,
+ { 0xa6, 0x2, 0x90, 0x4, 0x26, 0x2a, 0xb8, 0x6b }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterRestrictDns_Outbound_Ipv4()
+{
+ static const GUID g =
+ {
+ 0xc0792b44,
+ 0xfc3c,
+ 0x42e8,
+ { 0xa6, 0x60, 0x25, 0x4b, 0xd0, 0x4, 0xb1, 0x9d }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterRestrictDns_Outbound_Ipv6()
+{
+ static const GUID g =
+ {
+ 0xcde477eb,
+ 0x2d8a,
+ 0x45b8,
+ { 0x9a, 0x3e, 0x9a, 0xa3, 0xbe, 0x4d, 0xe2, 0xb4 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterRestrictDns_Outbound_Tunnel_Ipv4()
+{
+ static const GUID g =
+ {
+ 0x790445dc,
+ 0xb23e,
+ 0x4ab4,
+ { 0x8e, 0x2f, 0xc7, 0x6, 0x55, 0x5f, 0x94, 0xff }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::FilterRestrictDns_Outbound_Tunnel_Ipv6()
+{
+ static const GUID g =
+ {
+ 0xacc90d87,
+ 0xab77,
+ 0x4cf4,
+ { 0x84, 0xee, 0x1d, 0x68, 0x95, 0xf0, 0x66, 0xc2 }
+ };
+
+ return g;
+}
diff --git a/windows/winfw/src/winfw/mullvadguids.h b/windows/winfw/src/winfw/mullvadguids.h
new file mode 100644
index 0000000000..788b8b8b30
--- /dev/null
+++ b/windows/winfw/src/winfw/mullvadguids.h
@@ -0,0 +1,45 @@
+#pragma once
+#include <guiddef.h>
+
+class MullvadGuids
+{
+public:
+
+ MullvadGuids() = delete;
+
+ static const GUID &Provider();
+ static const GUID &SublayerWhitelist();
+ static const GUID &SublayerBlacklist();
+
+ static const GUID &FilterBlockAll_Outbound_Ipv4();
+ static const GUID &FilterBlockAll_Outbound_Ipv6();
+ static const GUID &FilterBlockAll_Inbound_Ipv4();
+ static const GUID &FilterBlockAll_Inbound_Ipv6();
+
+ static const GUID &FilterPermitLan_10_8();
+ static const GUID &FilterPermitLan_172_16_12();
+ static const GUID &FilterPermitLan_192_168_16();
+ static const GUID &FilterPermitLan_Multicast();
+
+ static const GUID &FilterPermitLanService_10_8();
+ static const GUID &FilterPermitLanService_172_16_12();
+ static const GUID &FilterPermitLanService_192_168_16();
+
+ static const GUID &FilterPermitLoopback_Outbound_Ipv4();
+ static const GUID &FilterPermitLoopback_Outbound_Ipv6();
+ static const GUID &FilterPermitLoopback_Inbound_Ipv4();
+ static const GUID &FilterPermitLoopback_Inbound_Ipv6();
+
+ static const GUID &FilterPermitDhcp_Outbound_Request();
+ static const GUID &FilterPermitDhcp_Inbound_Response();
+
+ static const GUID &FilterPermitVpnRelay();
+
+ static const GUID &FilterPermitVpnTunnel_Outbound_Ipv4();
+ static const GUID &FilterPermitVpnTunnel_Outbound_Ipv6();
+
+ static const GUID &FilterRestrictDns_Outbound_Ipv4();
+ static const GUID &FilterRestrictDns_Outbound_Ipv6();
+ static const GUID &FilterRestrictDns_Outbound_Tunnel_Ipv4();
+ static const GUID &FilterRestrictDns_Outbound_Tunnel_Ipv6();
+};
diff --git a/windows/winfw/src/winfw/mullvadobjects.cpp b/windows/winfw/src/winfw/mullvadobjects.cpp
new file mode 100644
index 0000000000..43618b51b3
--- /dev/null
+++ b/windows/winfw/src/winfw/mullvadobjects.cpp
@@ -0,0 +1,46 @@
+#include "stdafx.h"
+#include "mullvadobjects.h"
+#include "mullvadguids.h"
+
+//static
+std::unique_ptr<wfp::ProviderBuilder> MullvadObjects::Provider()
+{
+ auto builder = std::make_unique<wfp::ProviderBuilder>();
+
+ (*builder)
+ .name(L"Mullvad VPN")
+ .description(L"Mullvad VPN firewall integration")
+ .key(MullvadGuids::Provider());
+
+ return builder;
+}
+
+//static
+std::unique_ptr<wfp::SublayerBuilder> MullvadObjects::SublayerWhitelist()
+{
+ auto builder = std::make_unique<wfp::SublayerBuilder>();
+
+ (*builder)
+ .name(L"Mullvad VPN whitelist")
+ .description(L"Filters that permit traffic")
+ .key(MullvadGuids::SublayerWhitelist())
+ .provider(MullvadGuids::Provider())
+ .weight(MAXUINT16);
+
+ return builder;
+}
+
+//static
+std::unique_ptr<wfp::SublayerBuilder> MullvadObjects::SublayerBlacklist()
+{
+ auto builder = std::make_unique<wfp::SublayerBuilder>();
+
+ (*builder)
+ .name(L"Mullvad VPN blacklist")
+ .description(L"Filters that block traffic")
+ .key(MullvadGuids::SublayerBlacklist())
+ .provider(MullvadGuids::Provider())
+ .weight(MAXUINT16 - 1);
+
+ return builder;
+}
diff --git a/windows/winfw/src/winfw/mullvadobjects.h b/windows/winfw/src/winfw/mullvadobjects.h
new file mode 100644
index 0000000000..9a056c9186
--- /dev/null
+++ b/windows/winfw/src/winfw/mullvadobjects.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "libwfp/providerbuilder.h"
+#include "libwfp/sublayerbuilder.h"
+#include "libwfp/conditionbuilder.h"
+#include "libwfp/filterbuilder.h"
+#include <memory>
+
+class MullvadObjects
+{
+public:
+
+ MullvadObjects() = delete;
+
+ static std::unique_ptr<wfp::ProviderBuilder> Provider();
+ static std::unique_ptr<wfp::SublayerBuilder> SublayerWhitelist();
+ static std::unique_ptr<wfp::SublayerBuilder> SublayerBlacklist();
+};
diff --git a/windows/winfw/src/winfw/rules/blockall.cpp b/windows/winfw/src/winfw/rules/blockall.cpp
new file mode 100644
index 0000000000..ff8ba5a065
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/blockall.cpp
@@ -0,0 +1,73 @@
+#include "stdafx.h"
+#include "blockall.h"
+#include "winfw/mullvadguids.h"
+#include "libwfp/filterbuilder.h"
+#include "libwfp/nullconditionbuilder.h"
+
+namespace rules
+{
+
+bool BlockAll::apply(IObjectInstaller &objectInstaller)
+{
+ wfp::FilterBuilder filterBuilder;
+
+ //
+ // #1 block outbound connections, ipv4
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterBlockAll_Outbound_Ipv4())
+ .name(L"Block all outbound connections")
+ .description(L"This filter is part of a rule that restricts inbound and outbound traffic")
+ .provider(MullvadGuids::Provider())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4)
+ .sublayer(MullvadGuids::SublayerWhitelist())
+ .weight(wfp::FilterBuilder::WeightClass::Min)
+ .block();
+
+ wfp::NullConditionBuilder nullConditionBuilder;
+
+ if (false == objectInstaller.addFilter(filterBuilder, nullConditionBuilder))
+ {
+ return false;
+ }
+
+ //
+ // #2 block outbound connections, ipv6
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterBlockAll_Outbound_Ipv6())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
+
+ if (false == objectInstaller.addFilter(filterBuilder, nullConditionBuilder))
+ {
+ return false;
+ }
+
+ //
+ // #3 block inbound connections, ipv4
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterBlockAll_Inbound_Ipv4())
+ .name(L"Block all inbound connections")
+ .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4);
+
+ if (false == objectInstaller.addFilter(filterBuilder, nullConditionBuilder))
+ {
+ return false;
+ }
+
+ //
+ // #4 block inbound connections, ipv6
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterBlockAll_Inbound_Ipv6())
+ .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6);
+
+ return objectInstaller.addFilter(filterBuilder, nullConditionBuilder);
+}
+
+}
diff --git a/windows/winfw/src/winfw/rules/blockall.h b/windows/winfw/src/winfw/rules/blockall.h
new file mode 100644
index 0000000000..c60c43fa1d
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/blockall.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "ifirewallrule.h"
+
+namespace rules
+{
+
+class BlockAll : public IFirewallRule
+{
+public:
+
+ BlockAll() = default;
+ ~BlockAll() = default;
+
+ bool apply(IObjectInstaller &objectInstaller) override;
+};
+
+}
diff --git a/windows/winfw/src/winfw/rules/ifirewallrule.h b/windows/winfw/src/winfw/rules/ifirewallrule.h
new file mode 100644
index 0000000000..e0c8755b20
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/ifirewallrule.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "winfw/iobjectinstaller.h"
+
+//
+// A firewall rule uses one of more filters to implement a concept
+// E.g. "allow lan traffic"
+//
+
+namespace rules
+{
+
+struct IFirewallRule
+{
+ virtual ~IFirewallRule() = 0
+ {
+ }
+
+ //virtual std::wstring name() = 0;
+ //virtual std::vector<std::wstring> details() = 0; // doesn't work? because there can be multiple filters each with multiple conditions
+
+ virtual bool apply(IObjectInstaller &objectInstaller) = 0;
+};
+
+}
diff --git a/windows/winfw/src/winfw/rules/permitdhcp.cpp b/windows/winfw/src/winfw/rules/permitdhcp.cpp
new file mode 100644
index 0000000000..e92b88056e
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/permitdhcp.cpp
@@ -0,0 +1,74 @@
+#include "stdafx.h"
+#include "permitdhcp.h"
+#include "winfw/mullvadguids.h"
+#include "libwfp/filterbuilder.h"
+#include "libwfp/conditionbuilder.h"
+#include "libwfp/ipaddress.h"
+#include "libwfp/conditions/conditionprotocol.h"
+#include "libwfp/conditions/conditionport.h"
+#include "libwfp/conditions/conditionip.h"
+#include "libwfp/conditions/conditionport.h"
+
+using namespace wfp::conditions;
+
+namespace rules
+{
+
+bool PermitDhcp::apply(IObjectInstaller &objectInstaller)
+{
+ //
+ // First UDP packet for a unique [remote address, port] tuple is mapped into:
+ //
+ // outbound: FWPM_LAYER_ALE_AUTH_CONNECT_V{4|6}
+ // inbound: FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4|6}
+ //
+
+ wfp::FilterBuilder filterBuilder;
+
+ //
+ // #1 permit outbound DHCP request
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitDhcp_Outbound_Request())
+ .name(L"Permit outbound DHCP request")
+ .description(L"This filter is part of a rule that permits DHCP client traffic")
+ .provider(MullvadGuids::Provider())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4)
+ .sublayer(MullvadGuids::SublayerWhitelist())
+ .weight(wfp::FilterBuilder::WeightClass::Max)
+ .permit();
+
+ {
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4);
+
+ conditionBuilder.add_condition(ConditionProtocol::Udp());
+ conditionBuilder.add_condition(ConditionPort::Local(68));
+ conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 255, 255, 255, 255 })));
+ conditionBuilder.add_condition(ConditionPort::Remote(67));
+
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+ }
+
+ //
+ // #2 permit inbound DHCP response
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitDhcp_Inbound_Response())
+ .name(L"Permit inbound DHCP response")
+ .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4);
+
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4);
+
+ conditionBuilder.add_condition(ConditionProtocol::Udp());
+ conditionBuilder.add_condition(ConditionPort::Remote(67));
+ conditionBuilder.add_condition(ConditionPort::Local(68));
+
+ return objectInstaller.addFilter(filterBuilder, conditionBuilder);
+}
+
+}
diff --git a/windows/winfw/src/winfw/rules/permitdhcp.h b/windows/winfw/src/winfw/rules/permitdhcp.h
new file mode 100644
index 0000000000..58bd90bfa5
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/permitdhcp.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "ifirewallrule.h"
+
+namespace rules
+{
+
+class PermitDhcp : public IFirewallRule
+{
+public:
+
+ PermitDhcp() = default;
+ ~PermitDhcp() = default;
+
+ bool apply(IObjectInstaller &objectInstaller) override;
+};
+
+}
diff --git a/windows/winfw/src/winfw/rules/permitlan.cpp b/windows/winfw/src/winfw/rules/permitlan.cpp
new file mode 100644
index 0000000000..cf2e753d05
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/permitlan.cpp
@@ -0,0 +1,96 @@
+#include "stdafx.h"
+#include "permitlan.h"
+#include "winfw/mullvadguids.h"
+#include "libwfp/filterbuilder.h"
+#include "libwfp/conditionbuilder.h"
+#include "libwfp/ipaddress.h"
+#include "libwfp/conditions/conditionip.h"
+
+using namespace wfp::conditions;
+
+namespace rules
+{
+
+bool PermitLan::apply(IObjectInstaller &objectInstaller)
+{
+ wfp::FilterBuilder filterBuilder;
+
+ //
+ // #1 locally-initiated on 10/8
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitLan_10_8())
+ .name(L"Permit locally-initiated traffic on 10/8")
+ .description(L"This filter is part of a rule that permits LAN traffic")
+ .provider(MullvadGuids::Provider())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4)
+ .sublayer(MullvadGuids::SublayerWhitelist())
+ .weight(wfp::FilterBuilder::WeightClass::Max)
+ .permit();
+
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4);
+
+ conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 10, 0, 0, 0 }), uint8_t(8)));
+ conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 10, 0, 0, 0 }), uint8_t(8)));
+
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+
+ //
+ // #2 locally-initiated on 172.16/12
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitLan_172_16_12())
+ .name(L"Permit locally-initiated traffic on 172.16/12");
+
+ conditionBuilder.reset();
+
+ conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 172, 16, 0, 0 }), uint8_t(12)));
+ conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 172, 16, 0, 0 }), uint8_t(12)));
+
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+
+ //
+ // #3 locally-initiated on 192.168/16
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitLan_192_168_16())
+ .name(L"Permit locally-initiated traffic on 192.168/16");
+
+ conditionBuilder.reset();
+
+ conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 192, 168, 0, 0 }), uint8_t(16)));
+ conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 192, 168, 0, 0 }), uint8_t(16)));
+
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+
+ //
+ // #4 LAN to multicast
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitLan_Multicast())
+ .name(L"Permit locally-initiated multicast traffic");
+
+ conditionBuilder.reset();
+
+ conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 10, 0, 0, 0 }), uint8_t(8)));
+ conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 172, 16, 0, 0 }), uint8_t(12)));
+ conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 192, 168, 0, 0 }), uint8_t(16)));
+ conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 224, 0, 0, 0 }), uint8_t(24)));
+
+ return objectInstaller.addFilter(filterBuilder, conditionBuilder);
+}
+
+}
diff --git a/windows/winfw/src/winfw/rules/permitlan.h b/windows/winfw/src/winfw/rules/permitlan.h
new file mode 100644
index 0000000000..9194b0bcd7
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/permitlan.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "ifirewallrule.h"
+
+namespace rules
+{
+
+class PermitLan : public IFirewallRule
+{
+public:
+
+ PermitLan() = default;
+ ~PermitLan() = default;
+
+ bool apply(IObjectInstaller &objectInstaller) override;
+};
+
+}
diff --git a/windows/winfw/src/winfw/rules/permitlanservice.cpp b/windows/winfw/src/winfw/rules/permitlanservice.cpp
new file mode 100644
index 0000000000..4e52f07047
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/permitlanservice.cpp
@@ -0,0 +1,76 @@
+#include "stdafx.h"
+#include "permitlanservice.h"
+#include "winfw/mullvadguids.h"
+#include "libwfp/filterbuilder.h"
+#include "libwfp/conditionbuilder.h"
+#include "libwfp/ipaddress.h"
+#include "libwfp/conditions/conditionip.h"
+
+using namespace wfp::conditions;
+
+namespace rules
+{
+
+bool PermitLanService::apply(IObjectInstaller &objectInstaller)
+{
+ wfp::FilterBuilder filterBuilder;
+
+ //
+ // #1 incoming request on 10/8
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitLanService_10_8())
+ .name(L"Permit incoming requests on 10/8")
+ .description(L"This filter is part of a rule that permits hosting services in a LAN environment")
+ .provider(MullvadGuids::Provider())
+ .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4)
+ .sublayer(MullvadGuids::SublayerWhitelist())
+ .weight(wfp::FilterBuilder::WeightClass::Max)
+ .permit();
+
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4);
+
+ conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 10, 0, 0, 0 }), uint8_t(8)));
+ conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 10, 0, 0, 0 }), uint8_t(8)));
+
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+
+ //
+ // #2 incoming request on 172.16/12
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitLanService_172_16_12())
+ .name(L"Permit incoming requests on 172.16/12");
+
+ conditionBuilder.reset();
+
+ conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 172, 16, 0, 0 }), uint8_t(12)));
+ conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 172, 16, 0, 0 }), uint8_t(12)));
+
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+
+ //
+ // #3 incoming request on 192.168/16
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitLanService_192_168_16())
+ .name(L"Permit incoming requests on 192.168/16");
+
+ conditionBuilder.reset();
+
+ conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 192, 168, 0, 0 }), uint8_t(16)));
+ conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 192, 168, 0, 0 }), uint8_t(16)));
+
+ return objectInstaller.addFilter(filterBuilder, conditionBuilder);
+}
+
+}
diff --git a/windows/winfw/src/winfw/rules/permitlanservice.h b/windows/winfw/src/winfw/rules/permitlanservice.h
new file mode 100644
index 0000000000..be482386a3
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/permitlanservice.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "ifirewallrule.h"
+
+namespace rules
+{
+
+class PermitLanService : public IFirewallRule
+{
+public:
+
+ PermitLanService() = default;
+ ~PermitLanService() = default;
+
+ bool apply(IObjectInstaller &objectInstaller) override;
+};
+
+}
diff --git a/windows/winfw/src/winfw/rules/permitloopback.cpp b/windows/winfw/src/winfw/rules/permitloopback.cpp
new file mode 100644
index 0000000000..f98fe4f756
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/permitloopback.cpp
@@ -0,0 +1,96 @@
+#include "stdafx.h"
+#include "permitloopback.h"
+#include "winfw/mullvadguids.h"
+#include "libwfp/filterbuilder.h"
+#include "libwfp/conditionbuilder.h"
+#include "libwfp/conditions/conditionloopback.h"
+
+using namespace wfp::conditions;
+
+namespace rules
+{
+
+bool PermitLoopback::apply(IObjectInstaller &objectInstaller)
+{
+ wfp::FilterBuilder filterBuilder;
+
+ //
+ // #1 permit outbound connections, ipv4
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitLoopback_Outbound_Ipv4())
+ .name(L"Permit outbound connections on loopback")
+ .description(L"This filter is part of a rule that permits all loopback traffic")
+ .provider(MullvadGuids::Provider())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4)
+ .sublayer(MullvadGuids::SublayerWhitelist())
+ .weight(wfp::FilterBuilder::WeightClass::Max)
+ .permit();
+
+ {
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4);
+
+ conditionBuilder.add_condition(std::make_unique<ConditionLoopback>());
+
+ if (false == objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+ }
+
+ //
+ // #2 permit outbound connections, ipv6
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitLoopback_Outbound_Ipv6())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
+
+ {
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
+
+ conditionBuilder.add_condition(std::make_unique<ConditionLoopback>());
+
+ if (false == objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+ }
+
+ //
+ // #3 permit inbound connections, ipv4
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitLoopback_Inbound_Ipv4())
+ .name(L"Permit inbound connections on loopback")
+ .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4);
+
+ {
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4);
+
+ conditionBuilder.add_condition(std::make_unique<ConditionLoopback>());
+
+ if (false == objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+ }
+
+ //
+ // #4 permit inbound connections, ipv6
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitLoopback_Inbound_Ipv6())
+ .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6);
+
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6);
+
+ conditionBuilder.add_condition(std::make_unique<ConditionLoopback>());
+
+ return objectInstaller.addFilter(filterBuilder, conditionBuilder);
+}
+
+}
diff --git a/windows/winfw/src/winfw/rules/permitloopback.h b/windows/winfw/src/winfw/rules/permitloopback.h
new file mode 100644
index 0000000000..71694c353d
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/permitloopback.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "ifirewallrule.h"
+
+namespace rules
+{
+
+class PermitLoopback : public IFirewallRule
+{
+public:
+
+ PermitLoopback() = default;
+ ~PermitLoopback() = default;
+
+ bool apply(IObjectInstaller &objectInstaller) override;
+};
+
+}
diff --git a/windows/winfw/src/winfw/rules/permitvpnrelay.cpp b/windows/winfw/src/winfw/rules/permitvpnrelay.cpp
new file mode 100644
index 0000000000..e6a2f9b918
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/permitvpnrelay.cpp
@@ -0,0 +1,80 @@
+#include "stdafx.h"
+#include "permitvpnrelay.h"
+#include "winfw/mullvadguids.h"
+#include "libwfp/filterbuilder.h"
+#include "libwfp/conditionbuilder.h"
+#include "libwfp/conditions/conditionprotocol.h"
+#include "libwfp/conditions/conditionip.h"
+#include "libwfp/conditions/conditionport.h"
+
+using namespace wfp::conditions;
+
+namespace rules
+{
+
+namespace
+{
+
+const GUID &LayerFromIp(const wfp::IpAddress &ip)
+{
+ switch (ip.type())
+ {
+ case wfp::IpAddress::Type::Ipv4: return FWPM_LAYER_ALE_AUTH_CONNECT_V4;
+ case wfp::IpAddress::Type::Ipv6: return FWPM_LAYER_ALE_AUTH_CONNECT_V6;
+ default:
+ {
+ throw std::logic_error("Missing case handler in switch clause");
+ }
+ };
+}
+
+std::unique_ptr<ConditionProtocol> CreateProtocolCondition(PermitVpnRelay::Protocol protocol)
+{
+ switch (protocol)
+ {
+ case PermitVpnRelay::Protocol::Tcp: return ConditionProtocol::Tcp();
+ case PermitVpnRelay::Protocol::Udp: return ConditionProtocol::Udp();
+ default:
+ {
+ throw std::logic_error("Missing case handler in switch clause");
+ }
+ };
+}
+
+} // anonymous namespace
+
+PermitVpnRelay::PermitVpnRelay(const wfp::IpAddress &relay, uint16_t relayPort, Protocol protocol)
+ : m_relay(relay)
+ , m_relayPort(relayPort)
+ , m_protocol(protocol)
+{
+}
+
+bool PermitVpnRelay::apply(IObjectInstaller &objectInstaller)
+{
+ wfp::FilterBuilder filterBuilder;
+
+ //
+ // #1 permit connecting to relay
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitVpnRelay())
+ .name(L"Permit outbound connections to VPN relay")
+ .description(L"This filter is part of a rule that permits communication with a VPN relay")
+ .provider(MullvadGuids::Provider())
+ .layer(LayerFromIp(m_relay))
+ .sublayer(MullvadGuids::SublayerWhitelist())
+ .weight(wfp::FilterBuilder::WeightClass::Max)
+ .permit();
+
+ wfp::ConditionBuilder conditionBuilder(LayerFromIp(m_relay));
+
+ conditionBuilder.add_condition(ConditionIp::Remote(m_relay));
+ conditionBuilder.add_condition(ConditionPort::Remote(m_relayPort));
+ conditionBuilder.add_condition(CreateProtocolCondition(m_protocol));
+
+ return objectInstaller.addFilter(filterBuilder, conditionBuilder);
+}
+
+}
diff --git a/windows/winfw/src/winfw/rules/permitvpnrelay.h b/windows/winfw/src/winfw/rules/permitvpnrelay.h
new file mode 100644
index 0000000000..c5ae8b024a
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/permitvpnrelay.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "ifirewallrule.h"
+#include "libwfp/ipaddress.h"
+
+namespace rules
+{
+
+class PermitVpnRelay : public IFirewallRule
+{
+public:
+
+ enum class Protocol
+ {
+ Tcp,
+ Udp
+ };
+
+ PermitVpnRelay(const wfp::IpAddress &relay, uint16_t relayPort, Protocol protocol);
+
+ bool apply(IObjectInstaller &objectInstaller) override;
+
+private:
+
+ const wfp::IpAddress m_relay;
+ const uint16_t m_relayPort;
+ const Protocol m_protocol;
+};
+
+}
diff --git a/windows/winfw/src/winfw/rules/permitvpntunnel.cpp b/windows/winfw/src/winfw/rules/permitvpntunnel.cpp
new file mode 100644
index 0000000000..39830e70ec
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/permitvpntunnel.cpp
@@ -0,0 +1,62 @@
+#include "stdafx.h"
+#include "permitvpntunnel.h"
+#include "winfw/mullvadguids.h"
+#include "libwfp/filterbuilder.h"
+#include "libwfp/conditionbuilder.h"
+#include "libwfp/conditions/conditioninterface.h"
+
+using namespace wfp::conditions;
+
+namespace rules
+{
+
+PermitVpnTunnel::PermitVpnTunnel(const std::wstring &tunnelInterfaceAlias)
+ : m_tunnelInterfaceAlias(tunnelInterfaceAlias)
+{
+}
+
+bool PermitVpnTunnel::apply(IObjectInstaller &objectInstaller)
+{
+ wfp::FilterBuilder filterBuilder;
+
+ //
+ // #1 permit locally-initiated traffic on tunnel interface, ipv4
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitVpnTunnel_Outbound_Ipv4())
+ .name(L"Permit locally-initiated traffic on tunnel interface")
+ .description(L"This filter is part of a rule that permits communications inside the VPN tunnel")
+ .provider(MullvadGuids::Provider())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4)
+ .sublayer(MullvadGuids::SublayerWhitelist())
+ .weight(wfp::FilterBuilder::WeightClass::Max)
+ .permit();
+
+ {
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4);
+
+ conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias));
+
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+ }
+
+ //
+ // #2 permit locally-initiated traffic on tunnel interface, ipv6
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterPermitVpnTunnel_Outbound_Ipv6())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
+
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
+
+ conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias));
+
+ return objectInstaller.addFilter(filterBuilder, conditionBuilder);
+}
+
+}
diff --git a/windows/winfw/src/winfw/rules/permitvpntunnel.h b/windows/winfw/src/winfw/rules/permitvpntunnel.h
new file mode 100644
index 0000000000..49c1b028ab
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/permitvpntunnel.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "ifirewallrule.h"
+#include <string>
+
+namespace rules
+{
+
+class PermitVpnTunnel : public IFirewallRule
+{
+public:
+
+ PermitVpnTunnel(const std::wstring &tunnelInterfaceAlias);
+
+ bool apply(IObjectInstaller &objectInstaller) override;
+
+private:
+
+ const std::wstring m_tunnelInterfaceAlias;
+};
+
+}
diff --git a/windows/winfw/src/winfw/rules/restrictdns.cpp b/windows/winfw/src/winfw/rules/restrictdns.cpp
new file mode 100644
index 0000000000..d8a953fb3a
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/restrictdns.cpp
@@ -0,0 +1,148 @@
+#include "stdafx.h"
+#include "restrictdns.h"
+#include "winfw/mullvadguids.h"
+#include "libwfp/filterbuilder.h"
+#include "libwfp/conditionbuilder.h"
+#include "libwfp/conditions/conditioninterface.h"
+#include "libwfp/conditions/conditionip.h"
+#include "libwfp/conditions/conditionport.h"
+
+using namespace wfp::conditions;
+
+namespace rules
+{
+
+RestrictDns::RestrictDns(const std::wstring &tunnelInterfaceAlias, const wfp::IpAddress &dns)
+ : m_tunnelInterfaceAlias(tunnelInterfaceAlias)
+ , m_dns(dns)
+{
+}
+
+bool RestrictDns::apply(IObjectInstaller &objectInstaller)
+{
+ wfp::FilterBuilder filterBuilder;
+
+ //
+ // Requires that the following rules are in effect:
+ //
+ // BlockAll
+ // PermitVpnTunnel
+ //
+ // TODO: Have each rule specify requirements?
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterRestrictDns_Outbound_Ipv4())
+ .name(L"Block DNS requests outside the VPN tunnel")
+ .description(L"This filter is part of a rule that restricts DNS traffic")
+ .provider(MullvadGuids::Provider())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4)
+ .sublayer(MullvadGuids::SublayerBlacklist())
+ .weight(wfp::FilterBuilder::WeightClass::Max)
+ .block();
+
+ {
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4);
+
+ conditionBuilder.add_condition(ConditionPort::Remote(53));
+ conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias, CompareNeq()));
+
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+ }
+
+ //
+ // IPv6 also
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterRestrictDns_Outbound_Ipv6())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
+
+ {
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
+
+ conditionBuilder.add_condition(ConditionPort::Remote(53));
+ conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias, CompareNeq()));
+
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+ }
+
+ //
+ // This next part is a little redundant since the entire rule could be defined
+ // using three filters. Let's use four filters to maintain some kind of readability.
+ //
+ // The reason it would be possible to use three filters is because the single DNS
+ // is going to be either v4 or v6, so all requests that cannot be sent to the DNS
+ // will have to be blocked (thereby shadowing one of the filters above).
+ //
+
+ filterBuilder
+ .name(L"Restrict DNS requests inside the VPN tunnel");
+
+ if (m_dns.type() == wfp::IpAddress::Type::Ipv4)
+ {
+ filterBuilder
+ .key(MullvadGuids::FilterRestrictDns_Outbound_Tunnel_Ipv4())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4);
+
+ {
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4);
+
+ conditionBuilder.add_condition(ConditionPort::Remote(53));
+ conditionBuilder.add_condition(ConditionIp::Remote(m_dns, CompareNeq()));
+
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+ }
+
+ filterBuilder
+ .key(MullvadGuids::FilterRestrictDns_Outbound_Tunnel_Ipv6())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
+
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
+
+ conditionBuilder.add_condition(ConditionPort::Remote(53));
+
+ return objectInstaller.addFilter(filterBuilder, conditionBuilder);
+ }
+
+ //
+ // Specified DNS is IPv6
+ //
+
+ filterBuilder
+ .key(MullvadGuids::FilterRestrictDns_Outbound_Tunnel_Ipv6())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
+
+ {
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
+
+ conditionBuilder.add_condition(ConditionPort::Remote(53));
+ conditionBuilder.add_condition(ConditionIp::Remote(m_dns, CompareNeq()));
+
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
+ }
+
+ filterBuilder
+ .key(MullvadGuids::FilterRestrictDns_Outbound_Tunnel_Ipv4())
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4);
+
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4);
+
+ conditionBuilder.add_condition(ConditionPort::Remote(53));
+
+ return objectInstaller.addFilter(filterBuilder, conditionBuilder);
+}
+
+}
diff --git a/windows/winfw/src/winfw/rules/restrictdns.h b/windows/winfw/src/winfw/rules/restrictdns.h
new file mode 100644
index 0000000000..da09e970d3
--- /dev/null
+++ b/windows/winfw/src/winfw/rules/restrictdns.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "ifirewallrule.h"
+#include "libwfp/ipaddress.h"
+#include <string>
+
+namespace rules
+{
+
+class RestrictDns : public IFirewallRule
+{
+public:
+
+ RestrictDns(const std::wstring &tunnelInterfaceAlias, const wfp::IpAddress &dns);
+
+ bool apply(IObjectInstaller &objectInstaller) override;
+
+private:
+
+ const std::wstring m_tunnelInterfaceAlias;
+ const wfp::IpAddress m_dns;
+};
+
+}
diff --git a/windows/winfw/src/winfw/sessioncontroller.cpp b/windows/winfw/src/winfw/sessioncontroller.cpp
new file mode 100644
index 0000000000..2965233600
--- /dev/null
+++ b/windows/winfw/src/winfw/sessioncontroller.cpp
@@ -0,0 +1,248 @@
+#include "stdafx.h"
+#include "sessioncontroller.h"
+#include "libwfp/objectinstaller.h"
+#include "libwfp/objectdeleter.h"
+#include "libwfp/transaction.h"
+#include "libcommon/memory.h"
+#include <utility>
+
+namespace
+{
+
+template<typename T>
+void EraseBack(T &container, size_t elements)
+{
+ if (elements >= container.size())
+ {
+ container.clear();
+ }
+ else
+ {
+ container.erase
+ (
+ std::next(container.begin(), container.size() - elements),
+ container.end()
+ );
+ }
+}
+
+template<typename T>
+void ProcessReverse(T &container, size_t elements, std::function<void(typename T::value_type &)> f)
+{
+ auto it = container.rbegin();
+ auto end = std::next(it, elements);
+
+ while (it != end)
+ {
+ f(*it++);
+ }
+}
+
+bool CheckpointKeyToIndex(const std::vector<SessionRecord> &container, uint32_t key, size_t &elementIndex)
+{
+ auto index = 0;
+
+ for (auto it = container.begin(); it != container.end(); ++it, ++index)
+ {
+ if (it->key() == key)
+ {
+ elementIndex = index;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // anonymous namespace
+
+SessionController::SessionController(std::unique_ptr<wfp::FilterEngine> &&engine)
+ : m_engine(std::move(engine))
+ , m_activeTransaction(false)
+{
+}
+
+SessionController::~SessionController()
+{
+ //
+ // TODO: Review destruction of this instance and its owner.
+ //
+
+ try
+ {
+ executeTransaction([this]()
+ {
+ reset();
+ return true;
+ });
+ }
+ catch (...)
+ {
+ return;
+ }
+}
+
+bool SessionController::addProvider(wfp::ProviderBuilder &providerBuilder)
+{
+ if (false == m_activeTransaction)
+ {
+ throw std::runtime_error("Cannot add provider outside transaction");
+ }
+
+ GUID key;
+
+ auto status = wfp::ObjectInstaller::AddProvider(*m_engine, providerBuilder, &key);
+
+ if (status)
+ {
+ m_transactionRecords.emplace_back(SessionRecord(key, SessionRecord::ObjectType::Provider));
+ }
+
+ return status;
+}
+
+bool SessionController::addSublayer(wfp::SublayerBuilder &sublayerBuilder)
+{
+ if (false == m_activeTransaction)
+ {
+ throw std::runtime_error("Cannot add sublayer outside transaction");
+ }
+
+ GUID key;
+
+ auto status = wfp::ObjectInstaller::AddSublayer(*m_engine, sublayerBuilder, &key);
+
+ if (status)
+ {
+ m_transactionRecords.emplace_back(SessionRecord(key, SessionRecord::ObjectType::Sublayer));
+ }
+
+ return status;
+}
+
+bool SessionController::addFilter(wfp::FilterBuilder &filterBuilder, const wfp::IConditionBuilder &conditionBuilder)
+{
+ if (false == m_activeTransaction)
+ {
+ throw std::runtime_error("Cannot add filter outside transaction");
+ }
+
+ UINT64 id;
+
+ auto status = wfp::ObjectInstaller::AddFilter(*m_engine, filterBuilder, conditionBuilder, &id);
+
+ if (status)
+ {
+ m_transactionRecords.emplace_back(SessionRecord(id));
+ }
+
+ return status;
+}
+
+bool SessionController::executeTransaction(std::function<bool()> operation)
+{
+ if (m_activeTransaction.exchange(true))
+ {
+ throw std::runtime_error("Recursive/concurrent transactions are not supported");
+ }
+
+ common::memory::ScopeDestructor scopeDestructor;
+
+ scopeDestructor += [this]()
+ {
+ m_activeTransaction.store(false);
+ };
+
+ m_transactionRecords = m_records;
+
+ auto status = wfp::Transaction::Execute(*m_engine, operation);
+
+ if (status)
+ {
+ m_records.swap(m_transactionRecords);
+ }
+
+ return status;
+}
+
+bool SessionController::executeReadOnlyTransaction(std::function<bool()> operation)
+{
+ if (m_activeTransaction.exchange(true))
+ {
+ throw std::runtime_error("Recursive/concurrent transactions are not supported");
+ }
+
+ common::memory::ScopeDestructor scopeDestructor;
+
+ scopeDestructor += [this]()
+ {
+ m_activeTransaction.store(false);
+ };
+
+ return wfp::Transaction::ExecuteReadOnly(*m_engine, operation);
+}
+
+uint32_t SessionController::checkpoint()
+{
+ if (m_activeTransaction)
+ {
+ throw std::runtime_error("Cannot read checkpoint key while in transaction");
+ }
+
+ if (m_records.empty())
+ {
+ return 0;
+ }
+
+ return m_records.back().key();
+}
+
+void SessionController::revert(uint32_t key)
+{
+ if (false == m_activeTransaction)
+ {
+ throw std::runtime_error("Cannot revert session state outside transaction");
+ }
+
+ size_t elementIndex = 0;
+
+ if (false == CheckpointKeyToIndex(m_transactionRecords, key, elementIndex))
+ {
+ throw std::runtime_error("Invalid checkpoint key (checkpoint may have been overwritten?)");
+ }
+
+ const size_t numRemove = m_transactionRecords.size() - (elementIndex + 1);
+
+ rewindState(numRemove);
+}
+
+void SessionController::reset()
+{
+ if (false == m_activeTransaction)
+ {
+ throw std::runtime_error("Cannot reset session state outside transaction");
+ }
+
+ rewindState(m_transactionRecords.size());
+}
+
+void SessionController::rewindState(size_t steps)
+{
+ auto purged = 0;
+
+ try
+ {
+ ProcessReverse(m_transactionRecords, steps, [this, &purged](SessionRecord &record)
+ {
+ record.purge(*m_engine);
+ ++purged;
+ });
+ }
+ catch (...)
+ {
+ EraseBack(m_transactionRecords, purged);
+ throw;
+ }
+
+ EraseBack(m_transactionRecords, steps);
+}
diff --git a/windows/winfw/src/winfw/sessioncontroller.h b/windows/winfw/src/winfw/sessioncontroller.h
new file mode 100644
index 0000000000..61163533c4
--- /dev/null
+++ b/windows/winfw/src/winfw/sessioncontroller.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "iobjectinstaller.h"
+#include "sessionrecord.h"
+#include "libwfp/filterengine.h"
+#include <atomic>
+#include <memory>
+#include <vector>
+
+class SessionController : public IObjectInstaller
+{
+public:
+
+ SessionController(std::unique_ptr<wfp::FilterEngine> &&engine);
+ ~SessionController();
+
+ bool addProvider(wfp::ProviderBuilder &providerBuilder) override;
+ bool addSublayer(wfp::SublayerBuilder &sublayerBuilder) override;
+ bool addFilter(wfp::FilterBuilder &filterBuilder, const wfp::IConditionBuilder &conditionBuilder) override;
+
+ bool executeTransaction(std::function<bool()> operation);
+ bool executeReadOnlyTransaction(std::function<bool()> operation);
+
+ //
+ // Retrieve checkpoint key that can be used to restore the current session state
+ // This should be done outside of an active transaction
+ //
+ uint32_t checkpoint();
+
+ //
+ // Purge objects in the stack and return to an earlier state
+ // Use only inside active transaction
+ //
+ void revert(uint32_t key);
+
+ //
+ // Purge all objects in the stack
+ // Use only inside active transaction
+ //
+ void reset();
+
+private:
+
+ SessionController(const SessionController &) = delete;
+ SessionController &operator=(const SessionController &) = delete;
+
+ void rewindState(size_t steps);
+
+ std::unique_ptr<wfp::FilterEngine> m_engine;
+
+ std::vector<SessionRecord> m_records;
+ std::vector<SessionRecord> m_transactionRecords;
+
+ std::atomic_bool m_activeTransaction;
+};
diff --git a/windows/winfw/src/winfw/sessionrecord.cpp b/windows/winfw/src/winfw/sessionrecord.cpp
new file mode 100644
index 0000000000..ad11f4c013
--- /dev/null
+++ b/windows/winfw/src/winfw/sessionrecord.cpp
@@ -0,0 +1,58 @@
+#include "stdafx.h"
+#include "sessionrecord.h"
+#include "libwfp/objectdeleter.h"
+#include <atomic>
+#include <cstdint>
+#include <stdexcept>
+
+namespace
+{
+
+std::atomic<uint32_t> g_keybase = 0;
+
+} // anonymous namespace
+
+SessionRecord::SessionRecord(const GUID &id, ObjectType type)
+ : m_type(type)
+ , m_id(id)
+ , m_key(g_keybase++)
+{
+}
+
+SessionRecord::SessionRecord(UINT64 id)
+ : m_type(ObjectType::Filter)
+ , m_filterId(id)
+ , m_key(g_keybase++)
+{
+}
+
+void SessionRecord::purge(wfp::FilterEngine &engine)
+{
+ switch (m_type)
+ {
+ case ObjectType::Provider:
+ {
+ wfp::ObjectDeleter::DeleteProvider(engine, m_id);
+ break;
+ }
+ case ObjectType::Sublayer:
+ {
+ wfp::ObjectDeleter::DeleteSublayer(engine, m_id);
+ break;
+ }
+ case ObjectType::Filter:
+ {
+ wfp::ObjectDeleter::DeleteFilter(engine, m_filterId);
+ break;
+ }
+ default:
+ {
+ throw std::logic_error("Missing case handler in switch clause");
+ }
+ };
+}
+
+uint32_t SessionRecord::key() const
+{
+ return m_key;
+}
diff --git a/windows/winfw/src/winfw/sessionrecord.h b/windows/winfw/src/winfw/sessionrecord.h
new file mode 100644
index 0000000000..8ad3be8c86
--- /dev/null
+++ b/windows/winfw/src/winfw/sessionrecord.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "libwfp/filterengine.h"
+#include <guiddef.h>
+#include <windows.h>
+
+class SessionRecord
+{
+public:
+
+ enum class ObjectType
+ {
+ Provider,
+ Sublayer,
+ Filter
+ };
+
+ SessionRecord(const GUID &id, ObjectType type);
+ SessionRecord(UINT64 id);
+
+ SessionRecord(const SessionRecord &) = default;
+ SessionRecord(SessionRecord &&) = default;
+ SessionRecord &operator=(const SessionRecord &) = default;
+
+ void purge(wfp::FilterEngine &engine);
+
+ uint32_t key() const;
+
+private:
+
+ ObjectType m_type;
+
+ GUID m_id;
+ UINT64 m_filterId;
+
+ uint32_t m_key;
+};
diff --git a/windows/winfw/src/winfw/stdafx.cpp b/windows/winfw/src/winfw/stdafx.cpp
new file mode 100644
index 0000000000..af96757b53
--- /dev/null
+++ b/windows/winfw/src/winfw/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// winfw.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/windows/winfw/src/winfw/stdafx.h b/windows/winfw/src/winfw/stdafx.h
new file mode 100644
index 0000000000..f3a07375c7
--- /dev/null
+++ b/windows/winfw/src/winfw/stdafx.h
@@ -0,0 +1,16 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include <windows.h>
+
+
+
+// TODO: reference additional headers your program requires here
diff --git a/windows/winfw/src/winfw/targetver.h b/windows/winfw/src/winfw/targetver.h
new file mode 100644
index 0000000000..ae4a5c032c
--- /dev/null
+++ b/windows/winfw/src/winfw/targetver.h
@@ -0,0 +1,12 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <WinSDKVer.h>
+
+#define _WIN32_WINNT _WIN32_WINNT_WIN7
+
+#include <SDKDDKVer.h>
diff --git a/windows/winfw/src/winfw/winfw.cpp b/windows/winfw/src/winfw/winfw.cpp
new file mode 100644
index 0000000000..c7e171a664
--- /dev/null
+++ b/windows/winfw/src/winfw/winfw.cpp
@@ -0,0 +1,208 @@
+#include "stdafx.h"
+#include "winfw.h"
+#include "fwcontext.h"
+#include "libwfp/ipaddress.h"
+#include <windows.h>
+#include <stdexcept>
+
+namespace
+{
+
+uint32_t g_timeout = 0;
+
+WinFwErrorSink g_ErrorSink = nullptr;
+void * g_ErrorContext = nullptr;
+
+FwContext *g_fwContext = nullptr;
+
+} // anonymous namespace
+
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_Initialize(
+ uint32_t timeout,
+ WinFwErrorSink errorSink,
+ void *errorContext
+)
+{
+ if (nullptr != g_fwContext)
+ {
+ //
+ // This is an error.
+ // The existing instance may have a different timeout etc.
+ //
+ return false;
+ }
+
+ // Convert seconds to milliseconds.
+ g_timeout = timeout * 1000;
+
+ g_ErrorSink = errorSink;
+ g_ErrorContext = errorContext;
+
+ try
+ {
+ g_fwContext = new FwContext(g_timeout);
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != g_ErrorSink)
+ {
+ g_ErrorSink(err.what(), g_ErrorContext);
+ }
+
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_Deinitialize()
+{
+ if (nullptr == g_fwContext)
+ {
+ return true;
+ }
+
+ delete g_fwContext;
+ g_fwContext = nullptr;
+
+ return true;
+}
+
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_ApplyPolicyConnecting(
+ const WinFwSettings &settings,
+ const WinFwRelay &relay
+)
+{
+ if (nullptr == g_fwContext)
+ {
+ return false;
+ }
+
+ try
+ {
+ return g_fwContext->applyPolicyConnecting(settings, relay);
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != g_ErrorSink)
+ {
+ g_ErrorSink(err.what(), g_ErrorContext);
+ }
+
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+}
+
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_ApplyPolicyConnected(
+ const WinFwSettings &settings,
+ const WinFwRelay &relay,
+ const wchar_t *tunnelInterfaceAlias,
+ const wchar_t *primaryDns
+)
+{
+ if (nullptr == g_fwContext)
+ {
+ return false;
+ }
+
+ try
+ {
+ return g_fwContext->applyPolicyConnected(settings, relay, tunnelInterfaceAlias, primaryDns);
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != g_ErrorSink)
+ {
+ g_ErrorSink(err.what(), g_ErrorContext);
+ }
+
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+}
+
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_ApplyPolicyNetBlocked(
+)
+{
+ if (nullptr == g_fwContext)
+ {
+ return false;
+ }
+
+ try
+ {
+ return g_fwContext->applyPolicyNetBlocked();
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != g_ErrorSink)
+ {
+ g_ErrorSink(err.what(), g_ErrorContext);
+ }
+
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+}
+
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_Reset()
+{
+ if (nullptr == g_fwContext)
+ {
+ //
+ // This is an error.
+ // The reset instance enforces the block-all rule.
+ //
+ return false;
+ }
+
+ try
+ {
+ return g_fwContext->reset();
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != g_ErrorSink)
+ {
+ g_ErrorSink(err.what(), g_ErrorContext);
+ }
+
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+}
diff --git a/windows/winfw/src/winfw/winfw.def b/windows/winfw/src/winfw/winfw.def
new file mode 100644
index 0000000000..ff028b76d0
--- /dev/null
+++ b/windows/winfw/src/winfw/winfw.def
@@ -0,0 +1,9 @@
+LIBRARY winfw
+EXPORTS
+
+WinFw_Initialize
+WinFw_Deinitialize
+WinFw_ApplyPolicyConnecting
+WinFw_ApplyPolicyConnected
+WinFw_ApplyPolicyNetBlocked
+WinFw_Reset
diff --git a/windows/winfw/src/winfw/winfw.h b/windows/winfw/src/winfw/winfw.h
new file mode 100644
index 0000000000..a575b4d436
--- /dev/null
+++ b/windows/winfw/src/winfw/winfw.h
@@ -0,0 +1,150 @@
+#pragma once
+#include <cstdint>
+
+//
+// WINFW public API
+//
+
+#ifdef WINFW_EXPORTS
+#define WINFW_LINKAGE __declspec(dllexport)
+#else
+#define WINFW_LINKAGE __declspec(dllimport)
+#endif
+
+#define WINFW_API __stdcall
+
+///////////////////////////////////////////////////////////////////////////////
+// Structures
+///////////////////////////////////////////////////////////////////////////////
+
+#pragma pack(push, 1)
+
+typedef struct tag_WinFwSettings
+{
+ // Permit outbound DHCP requests and inbound DHCP responses on all interfaces.
+ bool permitDhcp;
+
+ // Permit all traffic to and from private address ranges.
+ bool permitLan;
+}
+WinFwSettings;
+
+enum WinFwProtocol : uint8_t
+{
+ Tcp = 0,
+ Udp = 1
+};
+
+typedef struct tag_WinFwRelay
+{
+ const wchar_t *ip;
+ uint16_t port;
+ WinFwProtocol protocol;
+}
+WinFwRelay;
+
+#pragma pack(pop)
+
+///////////////////////////////////////////////////////////////////////////////
+// Functions
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Initialize:
+//
+// Call this function once at startup, to register the provider etc.
+//
+// The timeout, in seconds, specifies how long to wait for the
+// transaction lock to become available. Specify 0 to use a default timeout
+// determined by Windows.
+//
+// Optionally provide a callback if you are interested in logging exceptions.
+//
+typedef void (WINFW_API *WinFwErrorSink)(const char *errorMessage, void *context);
+
+extern "C"
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_Initialize(
+ uint32_t timeout,
+ WinFwErrorSink errorSink,
+ void *errorContext
+);
+
+//
+// Deinitialize:
+//
+// Call this function once before unloading WFPCTL or exiting the process.
+//
+extern "C"
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_Deinitialize();
+
+//
+// ApplyPolicyConnecting:
+//
+// Apply restrictions in the firewall that block all traffic, except:
+// - What is specified by settings
+// - Communication with the relay server
+//
+extern "C"
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_ApplyPolicyConnecting(
+ const WinFwSettings &settings,
+ const WinFwRelay &relay
+);
+
+//
+// ApplyPolicyConnected:
+//
+// Apply restrictions in the firewall that block all traffic, except:
+// - What is specified by settings
+// - Communication with the relay server
+// - Non-DNS traffic inside the VPN tunnel
+// - DNS requests inside the VPN tunnel, to the specified DNS server
+//
+// Parameters:
+//
+// tunnelInterfaceAlias:
+// Friendly name of VPN tunnel interface
+// primaryDns:
+// String encoded IP address of DNS to use inside tunnel
+//
+extern "C"
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_ApplyPolicyConnected(
+ const WinFwSettings &settings,
+ const WinFwRelay &relay,
+ const wchar_t *tunnelInterfaceAlias,
+ const wchar_t *primaryDns
+);
+
+//
+// ApplyPolicyNetBlocked:
+//
+// Apply restrictions in the firewall that block all traffic.
+//
+extern "C"
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_ApplyPolicyNetBlocked(
+);
+
+//
+// Reset:
+//
+// Clear the policy in effect, if any.
+//
+extern "C"
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_Reset();
diff --git a/windows/winfw/src/winfw/winfw.vcxproj b/windows/winfw/src/winfw/winfw.vcxproj
new file mode 100644
index 0000000000..f6c59f9486
--- /dev/null
+++ b/windows/winfw/src/winfw/winfw.vcxproj
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp" />
+ <ClCompile Include="mullvadguids.cpp" />
+ <ClCompile Include="mullvadobjects.cpp" />
+ <ClCompile Include="rules\blockall.cpp" />
+ <ClCompile Include="rules\permitdhcp.cpp" />
+ <ClCompile Include="rules\permitlan.cpp" />
+ <ClCompile Include="rules\permitlanservice.cpp" />
+ <ClCompile Include="rules\permitloopback.cpp" />
+ <ClCompile Include="rules\permitvpnrelay.cpp" />
+ <ClCompile Include="rules\permitvpntunnel.cpp" />
+ <ClCompile Include="rules\restrictdns.cpp" />
+ <ClCompile Include="sessioncontroller.cpp" />
+ <ClCompile Include="sessionrecord.cpp" />
+ <ClCompile Include="stdafx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="fwcontext.cpp" />
+ <ClCompile Include="winfw.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="iobjectinstaller.h" />
+ <ClInclude Include="mullvadguids.h" />
+ <ClInclude Include="mullvadobjects.h" />
+ <ClInclude Include="rules\blockall.h" />
+ <ClInclude Include="rules\ifirewallrule.h" />
+ <ClInclude Include="rules\permitdhcp.h" />
+ <ClInclude Include="rules\permitlan.h" />
+ <ClInclude Include="rules\permitlanservice.h" />
+ <ClInclude Include="rules\permitloopback.h" />
+ <ClInclude Include="rules\permitvpnrelay.h" />
+ <ClInclude Include="rules\permitvpntunnel.h" />
+ <ClInclude Include="rules\restrictdns.h" />
+ <ClInclude Include="sessioncontroller.h" />
+ <ClInclude Include="sessionrecord.h" />
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="targetver.h" />
+ <ClInclude Include="fwcontext.h" />
+ <ClInclude Include="winfw.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="winfw.def" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>winfw</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;WINFW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir)..\..\..\libwfp\src\</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>libwfp.lib;libcommon.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
+ <ModuleDefinitionFile>winfw.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;WINFW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir)..\..\..\libwfp\src\</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>libwfp.lib;libcommon.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
+ <ModuleDefinitionFile>winfw.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;NDEBUG;WINFW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir)..\..\..\libwfp\src\</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>libwfp.lib;libcommon.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
+ <ModuleDefinitionFile>winfw.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;WINFW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir)..\..\..\libwfp\src\</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>libwfp.lib;libcommon.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
+ <ModuleDefinitionFile>winfw.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/windows/winfw/src/winfw/winfw.vcxproj.filters b/windows/winfw/src/winfw/winfw.vcxproj.filters
new file mode 100644
index 0000000000..1b7c0711f6
--- /dev/null
+++ b/windows/winfw/src/winfw/winfw.vcxproj.filters
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp" />
+ <ClCompile Include="stdafx.cpp" />
+ <ClCompile Include="winfw.cpp" />
+ <ClCompile Include="fwcontext.cpp" />
+ <ClCompile Include="sessioncontroller.cpp" />
+ <ClCompile Include="mullvadguids.cpp" />
+ <ClCompile Include="mullvadobjects.cpp" />
+ <ClCompile Include="rules\permitlan.cpp">
+ <Filter>rules</Filter>
+ </ClCompile>
+ <ClCompile Include="rules\blockall.cpp">
+ <Filter>rules</Filter>
+ </ClCompile>
+ <ClCompile Include="rules\permitlanservice.cpp">
+ <Filter>rules</Filter>
+ </ClCompile>
+ <ClCompile Include="rules\permitloopback.cpp">
+ <Filter>rules</Filter>
+ </ClCompile>
+ <ClCompile Include="rules\permitdhcp.cpp">
+ <Filter>rules</Filter>
+ </ClCompile>
+ <ClCompile Include="rules\permitvpnrelay.cpp">
+ <Filter>rules</Filter>
+ </ClCompile>
+ <ClCompile Include="rules\permitvpntunnel.cpp">
+ <Filter>rules</Filter>
+ </ClCompile>
+ <ClCompile Include="sessionrecord.cpp" />
+ <ClCompile Include="rules\restrictdns.cpp">
+ <Filter>rules</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="targetver.h" />
+ <ClInclude Include="winfw.h" />
+ <ClInclude Include="fwcontext.h" />
+ <ClInclude Include="sessioncontroller.h" />
+ <ClInclude Include="mullvadguids.h" />
+ <ClInclude Include="mullvadobjects.h" />
+ <ClInclude Include="rules\ifirewallrule.h">
+ <Filter>rules</Filter>
+ </ClInclude>
+ <ClInclude Include="rules\permitlan.h">
+ <Filter>rules</Filter>
+ </ClInclude>
+ <ClInclude Include="iobjectinstaller.h" />
+ <ClInclude Include="rules\blockall.h">
+ <Filter>rules</Filter>
+ </ClInclude>
+ <ClInclude Include="rules\permitlanservice.h">
+ <Filter>rules</Filter>
+ </ClInclude>
+ <ClInclude Include="rules\permitloopback.h">
+ <Filter>rules</Filter>
+ </ClInclude>
+ <ClInclude Include="rules\permitdhcp.h">
+ <Filter>rules</Filter>
+ </ClInclude>
+ <ClInclude Include="rules\permitvpnrelay.h">
+ <Filter>rules</Filter>
+ </ClInclude>
+ <ClInclude Include="rules\permitvpntunnel.h">
+ <Filter>rules</Filter>
+ </ClInclude>
+ <ClInclude Include="sessionrecord.h" />
+ <ClInclude Include="rules\restrictdns.h">
+ <Filter>rules</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="rules">
+ <UniqueIdentifier>{3639a3ba-599e-400c-918b-8869654cdc1f}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="winfw.def" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/windows/winfw/winfw.sln b/windows/winfw/winfw.sln
new file mode 100644
index 0000000000..32b40ccecd
--- /dev/null
+++ b/windows/winfw/winfw.sln
@@ -0,0 +1,58 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27130.2027
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winfw", "src\winfw\winfw.vcxproj", "{801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}"
+ ProjectSection(ProjectDependencies) = postProject
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF}
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B} = {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcommon", "..\windows-libraries\src\libcommon\libcommon.vcxproj", "{B52E2D10-A94A-4605-914A-2DCEF6A757EF}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwfp", "..\libwfp\src\libwfp\libwfp.vcxproj", "{2164E6D9-6023-4932-A08F-7A5C15E2CA0B}"
+ ProjectSection(ProjectDependencies) = postProject
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Debug|x64.ActiveCfg = Debug|x64
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Debug|x64.Build.0 = Debug|x64
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Debug|x86.ActiveCfg = Debug|Win32
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Debug|x86.Build.0 = Debug|Win32
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Release|x64.ActiveCfg = Release|x64
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Release|x64.Build.0 = Release|x64
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Release|x86.ActiveCfg = Release|Win32
+ {801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}.Release|x86.Build.0 = Release|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x64.ActiveCfg = Debug|x64
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x64.Build.0 = Debug|x64
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x86.ActiveCfg = Debug|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x86.Build.0 = Debug|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x64.ActiveCfg = Release|x64
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x64.Build.0 = Release|x64
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x86.ActiveCfg = Release|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x86.Build.0 = Release|Win32
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Debug|x64.ActiveCfg = Debug|x64
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Debug|x64.Build.0 = Debug|x64
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Debug|x86.ActiveCfg = Debug|Win32
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Debug|x86.Build.0 = Debug|Win32
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Release|x64.ActiveCfg = Release|x64
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Release|x64.Build.0 = Release|x64
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Release|x86.ActiveCfg = Release|Win32
+ {2164E6D9-6023-4932-A08F-7A5C15E2CA0B}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {56448AED-BD78-45C7-8608-17952F396A96}
+ EndGlobalSection
+EndGlobal