summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--windows/windns/extras.sln56
-rw-r--r--windows/windns/src/extras/loader/loader.cpp120
-rw-r--r--windows/windns/src/extras/loader/loader.vcxproj193
-rw-r--r--windows/windns/src/extras/loader/loader.vcxproj.filters11
-rw-r--r--windows/windns/src/extras/loader/stdafx.cpp8
-rw-r--r--windows/windns/src/extras/loader/stdafx.h15
-rw-r--r--windows/windns/src/extras/loader/targetver.h8
-rw-r--r--windows/windns/src/windns/clientsinkinfo.h21
-rw-r--r--windows/windns/src/windns/configmanager.cpp139
-rw-r--r--windows/windns/src/windns/configmanager.h115
-rw-r--r--windows/windns/src/windns/dllmain.cpp11
-rw-r--r--windows/windns/src/windns/interfaceconfig.cpp66
-rw-r--r--windows/windns/src/windns/interfaceconfig.h62
-rw-r--r--windows/windns/src/windns/netconfigeventsink.cpp34
-rw-r--r--windows/windns/src/windns/netconfigeventsink.h20
-rw-r--r--windows/windns/src/windns/netconfighelpers.cpp65
-rw-r--r--windows/windns/src/windns/netconfighelpers.h24
-rw-r--r--windows/windns/src/windns/netsh.cpp159
-rw-r--r--windows/windns/src/windns/netsh.h26
-rw-r--r--windows/windns/src/windns/stdafx.cpp8
-rw-r--r--windows/windns/src/windns/stdafx.h17
-rw-r--r--windows/windns/src/windns/targetver.h12
-rw-r--r--windows/windns/src/windns/types.h7
-rw-r--r--windows/windns/src/windns/windns.cpp237
-rw-r--r--windows/windns/src/windns/windns.h107
-rw-r--r--windows/windns/src/windns/windns.vcxproj209
-rw-r--r--windows/windns/src/windns/windns.vcxproj.filters27
-rw-r--r--windows/windns/src/windns/windnscontext.cpp98
-rw-r--r--windows/windns/src/windns/windnscontext.h28
-rw-r--r--windows/windns/windns.sln48
m---------windows/windows-libraries0
32 files changed, 1952 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 400d25a55d..94c9d5c1e3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,5 +6,5 @@ flow-typed
.DS_Store
*.log
dist-assets/relays.json
-windows/winfw/bin/
+windows/**/bin/
**/.vs/
diff --git a/windows/windns/extras.sln b/windows/windns/extras.sln
new file mode 100644
index 0000000000..ed1e71d4c2
--- /dev/null
+++ b/windows/windns/extras.sln
@@ -0,0 +1,56 @@
+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}") = "loader", "src\extras\loader\loader.vcxproj", "{1476A8B9-4A9E-4358-8744-A350CB97E152}"
+ ProjectSection(ProjectDependencies) = postProject
+ {A5344205-FC37-4572-9C63-8564ECC410AC} = {A5344205-FC37-4572-9C63-8564ECC410AC}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "windns", "src\windns\windns.vcxproj", "{A5344205-FC37-4572-9C63-8564ECC410AC}"
+ ProjectSection(ProjectDependencies) = postProject
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcommon", "..\windows-libraries\src\libcommon\libcommon.vcxproj", "{B52E2D10-A94A-4605-914A-2DCEF6A757EF}"
+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
+ {1476A8B9-4A9E-4358-8744-A350CB97E152}.Debug|x64.ActiveCfg = Debug|x64
+ {1476A8B9-4A9E-4358-8744-A350CB97E152}.Debug|x64.Build.0 = Debug|x64
+ {1476A8B9-4A9E-4358-8744-A350CB97E152}.Debug|x86.ActiveCfg = Debug|Win32
+ {1476A8B9-4A9E-4358-8744-A350CB97E152}.Debug|x86.Build.0 = Debug|Win32
+ {1476A8B9-4A9E-4358-8744-A350CB97E152}.Release|x64.ActiveCfg = Release|x64
+ {1476A8B9-4A9E-4358-8744-A350CB97E152}.Release|x64.Build.0 = Release|x64
+ {1476A8B9-4A9E-4358-8744-A350CB97E152}.Release|x86.ActiveCfg = Release|Win32
+ {1476A8B9-4A9E-4358-8744-A350CB97E152}.Release|x86.Build.0 = Release|Win32
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Debug|x64.ActiveCfg = Debug|x64
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Debug|x64.Build.0 = Debug|x64
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Debug|x86.ActiveCfg = Debug|Win32
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Debug|x86.Build.0 = Debug|Win32
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Release|x64.ActiveCfg = Release|x64
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Release|x64.Build.0 = Release|x64
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Release|x86.ActiveCfg = Release|Win32
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.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
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {16D5A833-BF2F-4049-A7E1-570B792F8148}
+ EndGlobalSection
+EndGlobal
diff --git a/windows/windns/src/extras/loader/loader.cpp b/windows/windns/src/extras/loader/loader.cpp
new file mode 100644
index 0000000000..1f7b5a6f97
--- /dev/null
+++ b/windows/windns/src/extras/loader/loader.cpp
@@ -0,0 +1,120 @@
+#include "stdafx.h"
+#include "windns/windns.h"
+#include "libcommon/trace/trace.h"
+#include "libcommon/trace/consoletracesink.h"
+#include <iostream>
+#include <conio.h>
+#include <vector>
+#include <windows.h>
+
+void WINDNS_API ErrorSink(const char *errorMessage, void *context)
+{
+ std::cout << "WINDNS Error: " << errorMessage << std::endl;
+}
+
+void WINDNS_API ConfigSink(const void *configData, uint32_t dataLength, void *context)
+{
+ std::wcout << L"Updated config was delivered to WINDNS client code" << std::endl;
+
+ auto f = CreateFileW(L"windns_recovery", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr);
+
+ if (INVALID_HANDLE_VALUE == f)
+ {
+ std::wcout << L"Failed to create recovery file" << std::endl;
+ return;
+ }
+
+ if (FALSE == WriteFile(f, configData, dataLength, nullptr, nullptr))
+ {
+ std::wcout << L"Failed to update recovery file" << std::endl;
+ }
+
+ CloseHandle(f);
+}
+
+void Recover()
+{
+ auto f = CreateFileW(L"windns_recovery", GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
+
+ if (INVALID_HANDLE_VALUE == f)
+ {
+ std::wcout << L"Failed to open recovery file" << std::endl;
+ return;
+ }
+
+ std::vector<uint8_t> data;
+
+ data.resize(GetFileSize(f, nullptr));
+
+ if (FALSE == ReadFile(f, &data[0], static_cast<DWORD>(data.size()), nullptr, nullptr))
+ {
+ std::wcout << L"Failed to read in recovery data" << std::endl;
+ CloseHandle(f);
+
+ return;
+ }
+
+ std::wcout << L"WinDns_Recover: " << WinDns_Recover(&data[0], static_cast<uint32_t>(data.size())) << std::endl;
+}
+
+bool Ask(const std::wstring &question)
+{
+ std::wcout << question.c_str() << L" Y/N: ";
+
+ auto answer = _getwch();
+
+ std::wcout << std::endl;
+
+ if ('y' == answer || 'Y' == answer)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void WaitInput(const std::wstring &message)
+{
+ std::wcout << message.c_str() << std::endl;
+ _getwch();
+}
+
+int main()
+{
+ common::trace::Trace::RegisterSink(new common::trace::ConsoleTraceSink);
+
+ if (Ask(L"Perform recovery?"))
+ {
+ Recover();
+ return 0;
+ }
+
+ std::wcout << L"WinDns_Initialize: " << WinDns_Initialize(ErrorSink, nullptr) << std::endl;
+
+ const wchar_t *servers[] =
+ {
+ L"8.8.8.8"
+ };
+
+ std::wcout << L"WinDns_Set: " << WinDns_Set(servers, _countof(servers), ConfigSink, nullptr) << std::endl;
+
+ WaitInput(L"Press a key to abort DNS monitoring + enforcing...");
+
+ if (Ask(L"Perform WinDns_Reset() before next WinDns_Set()?"))
+ {
+ std::wcout << L"WinDns_Reset: " << WinDns_Reset() << std::endl;
+ }
+
+ std::wcout << L"WinDns_Set: " << WinDns_Set(servers, _countof(servers), ConfigSink, nullptr) << std::endl;
+
+ WaitInput(L"Press a key to abort DNS monitoring + enforcing...");
+
+ if (Ask(L"Perform WinDns_Reset() before WinDns_Deinitialize()?"))
+ {
+ std::wcout << L"WinDns_Reset: " << WinDns_Reset() << std::endl;
+ }
+
+ std::wcout << L"WinDns_Deinitialize: " << WinDns_Deinitialize() << std::endl;
+
+ return 0;
+} \ No newline at end of file
diff --git a/windows/windns/src/extras/loader/loader.vcxproj b/windows/windns/src/extras/loader/loader.vcxproj
new file mode 100644
index 0000000000..7fdfe073c3
--- /dev/null
+++ b/windows/windns/src/extras/loader/loader.vcxproj
@@ -0,0 +1,193 @@
+<?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>{1476A8B9-4A9E-4358-8744-A350CB97E152}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>loader</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|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <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)'=='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|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir)../../;$(ProjectDir)../../../../windows-libraries/src/</AdditionalIncludeDirectories>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories>
+ <AdditionalDependencies>windns.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>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir)../../;$(ProjectDir)../../../../windows-libraries/src/</AdditionalIncludeDirectories>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories>
+ <AdditionalDependencies>windns.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>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir)../../;$(ProjectDir)../../../../windows-libraries/src/</AdditionalIncludeDirectories>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories>
+ <AdditionalDependencies>windns.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>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir)../../;$(ProjectDir)../../../../windows-libraries/src/</AdditionalIncludeDirectories>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories>
+ <AdditionalDependencies>windns.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>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="targetver.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="loader.cpp" />
+ <ClCompile Include="stdafx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/windows/windns/src/extras/loader/loader.vcxproj.filters b/windows/windns/src/extras/loader/loader.vcxproj.filters
new file mode 100644
index 0000000000..e29869c50f
--- /dev/null
+++ b/windows/windns/src/extras/loader/loader.vcxproj.filters
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="targetver.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="loader.cpp" />
+ <ClCompile Include="stdafx.cpp" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/windows/windns/src/extras/loader/stdafx.cpp b/windows/windns/src/extras/loader/stdafx.cpp
new file mode 100644
index 0000000000..e8a73894f3
--- /dev/null
+++ b/windows/windns/src/extras/loader/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// dnstest.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/windns/src/extras/loader/stdafx.h b/windows/windns/src/extras/loader/stdafx.h
new file mode 100644
index 0000000000..b005a839de
--- /dev/null
+++ b/windows/windns/src/extras/loader/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/windns/src/extras/loader/targetver.h b/windows/windns/src/extras/loader/targetver.h
new file mode 100644
index 0000000000..87c0086de7
--- /dev/null
+++ b/windows/windns/src/extras/loader/targetver.h
@@ -0,0 +1,8 @@
+#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 <SDKDDKVer.h>
diff --git a/windows/windns/src/windns/clientsinkinfo.h b/windows/windns/src/windns/clientsinkinfo.h
new file mode 100644
index 0000000000..db42485d8e
--- /dev/null
+++ b/windows/windns/src/windns/clientsinkinfo.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "windns.h"
+
+struct ErrorSinkInfo
+{
+ WinDnsErrorSink sink;
+ void *context;
+};
+
+struct ConfigSinkInfo
+{
+ WinDnsConfigSink sink;
+ void *context;
+};
+
+struct ClientSinkInfo
+{
+ ErrorSinkInfo errorSinkInfo;
+ ConfigSinkInfo configSinkInfo;
+};
diff --git a/windows/windns/src/windns/configmanager.cpp b/windows/windns/src/windns/configmanager.cpp
new file mode 100644
index 0000000000..c9fdde1142
--- /dev/null
+++ b/windows/windns/src/windns/configmanager.cpp
@@ -0,0 +1,139 @@
+#include "stdafx.h"
+#include "configmanager.h"
+#include "libcommon/serialization/serializer.h"
+#include "libcommon/trace/xtrace.h"
+#include <utility>
+#include <algorithm>
+
+ConfigManager::ConfigManager
+(
+ const std::vector<std::wstring> &servers,
+ const ConfigSinkInfo &configSinkInfo
+)
+ : m_servers(servers)
+ , m_configSinkInfo(configSinkInfo)
+{
+}
+
+void ConfigManager::lock()
+{
+ m_mutex.lock();
+}
+
+void ConfigManager::unlock()
+{
+ m_mutex.unlock();
+}
+
+void ConfigManager::updateServers(const std::vector<std::wstring> &servers)
+{
+ XTRACE(L"Updating DNS server list");
+ m_servers = servers;
+}
+
+void ConfigManager::updateConfigSink(const ConfigSinkInfo &configSinkInfo)
+{
+ XTRACE(L"Updating config sink");
+ m_configSinkInfo = configSinkInfo;
+}
+
+const std::vector<std::wstring> &ConfigManager::getServers() const
+{
+ return m_servers;
+}
+
+ConfigManager::UpdateStatus ConfigManager::updateConfig(const InterfaceConfig &previous, const InterfaceConfig &target)
+{
+ XTRACE(L"Interface configuration update for interface=", target.interfaceIndex());
+
+ //
+ // There are a few cases we need to deal with:
+ //
+ // 1/ An interface being offline and coming online.
+ // 2/ An external application changing the interface settings.
+ // 3/ Us changing the interface settings.
+ // a. On an interface the ConfigManager hasn't seen before.
+ // b. On an interface the ConfigManager already knows about.
+ //
+
+ const auto configIndex = target.configIndex();
+ auto iter = m_configs.find(configIndex);
+
+ if (verifyServers(target))
+ {
+ XTRACE(L"Update event was initiated by WINDNS or did not include DNS changes");
+
+ //
+ // If we haven't seen this config id before, it means the 'previous' instance
+ // is the original configuration on the system, and as such must be recorded.
+ //
+ if (m_configs.end() == iter)
+ {
+ XTRACE(L"Creating new interface configuration entry");
+ m_configs.insert(std::make_pair(configIndex, previous));
+
+ exportConfigs();
+ }
+
+ return UpdateStatus::DnsApproved;
+ }
+
+ //
+ // The update was not initiated by us so store the updated configuration.
+ //
+ if (m_configs.end() == iter)
+ {
+ XTRACE(L"Creating new interface configuration entry");
+ m_configs.insert(std::make_pair(configIndex, target));
+ }
+ else
+ {
+ XTRACE(L"Updating interface configuration entry");
+ iter->second.updateServers(target);
+ }
+
+ exportConfigs();
+
+ return UpdateStatus::DnsDeviates;
+}
+
+bool ConfigManager::processConfigs(std::function<bool(const InterfaceConfig &)> configSink)
+{
+ for (auto it = m_configs.begin(); it != m_configs.end(); ++it)
+ {
+ if (false == configSink(it->second))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ConfigManager::verifyServers(const InterfaceConfig &config)
+{
+ auto updatedServers = config.servers();
+
+ if (nullptr == updatedServers)
+ {
+ return false;
+ }
+
+ return std::equal(m_servers.begin(), m_servers.end(), updatedServers->begin(), updatedServers->end());
+}
+
+void ConfigManager::exportConfigs()
+{
+ common::serialization::Serializer s;
+
+ s << static_cast<uint32_t>(m_configs.size());
+
+ for (auto it = m_configs.begin(); it != m_configs.end(); ++it)
+ {
+ it->second.serialize(s);
+ }
+
+ auto data = s.blob();
+
+ m_configSinkInfo.sink(&data[0], static_cast<uint32_t>(data.size()), m_configSinkInfo.context);
+}
diff --git a/windows/windns/src/windns/configmanager.h b/windows/windns/src/windns/configmanager.h
new file mode 100644
index 0000000000..157f148901
--- /dev/null
+++ b/windows/windns/src/windns/configmanager.h
@@ -0,0 +1,115 @@
+#pragma once
+
+#include "interfaceconfig.h"
+#include "clientsinkinfo.h"
+#include <map>
+#include <string>
+#include <mutex>
+#include <memory>
+#include <functional>
+
+//
+// The ConfigManager is engineered to track the "real" DNS configuration for an adapter.
+//
+// The situation is somewhat complicated, because a given system may have several adapters, which
+// in turn may have several configurations?
+//
+// Every update for every configuration is recorded, bar the ones that correspond to us
+// overriding the DNS settings.
+//
+
+class ConfigManager
+{
+public:
+
+ struct Mutex
+ {
+ Mutex(const Mutex &) = delete;
+ Mutex &operator=(const Mutex &) = delete;
+ Mutex(Mutex &&) = delete;
+ Mutex &operator=(Mutex &&) = delete;
+
+ Mutex(ConfigManager &manager)
+ : m_manager(manager)
+ {
+ m_manager.lock();
+ }
+
+ ~Mutex()
+ {
+ m_manager.unlock();
+ }
+
+ ConfigManager &m_manager;
+ };
+
+ //
+ // "servers" specifies the set of servers used when overriding settings.
+ // This enables filtering out the corresponding event.
+ //
+ ConfigManager
+ (
+ const std::vector<std::wstring> &servers,
+ const ConfigSinkInfo &configSinkInfo
+ );
+
+ //
+ // The ConfigManager is shared between threads.
+ // Locking is managed externally for reasons of efficiency.
+ //
+ void lock();
+ void unlock();
+
+ //
+ // Notify the ConfigManager that servers used when overriding DNS settings have changed.
+ //
+ void updateServers(const std::vector<std::wstring> &servers);
+
+ //
+ // Update the callback used for persisting settings.
+ //
+ void updateConfigSink(const ConfigSinkInfo &configSinkInfo);
+
+ //
+ // Get the current set of servers used for overriding DNS settings.
+ //
+ const std::vector<std::wstring> &getServers() const;
+
+ //
+ // Notify the ConfigManager that a live configuration has been updated.
+ //
+ enum class UpdateStatus
+ {
+ DnsApproved,
+ DnsDeviates
+ };
+
+ UpdateStatus updateConfig(const InterfaceConfig &previous, const InterfaceConfig &target);
+
+ //
+ // Enumerate recorded configs.
+ //
+ bool processConfigs(std::function<bool(const InterfaceConfig &)> configSink);
+
+private:
+
+ std::mutex m_mutex;
+
+ std::vector<std::wstring> m_servers;
+ ConfigSinkInfo m_configSinkInfo;
+
+ //
+ // Organize configs based on their system assigned index.
+ //
+ std::map<uint32_t, InterfaceConfig> m_configs;
+
+ //
+ // Check DNS server list to see if it matches what we're trying to enforce.
+ //
+ bool verifyServers(const InterfaceConfig &config);
+
+ //
+ // Bundle the current config details and send them into the config sink.
+ //
+ void exportConfigs();
+};
diff --git a/windows/windns/src/windns/dllmain.cpp b/windows/windns/src/windns/dllmain.cpp
new file mode 100644
index 0000000000..f11c65c519
--- /dev/null
+++ b/windows/windns/src/windns/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/windns/src/windns/interfaceconfig.cpp b/windows/windns/src/windns/interfaceconfig.cpp
new file mode 100644
index 0000000000..74e912de89
--- /dev/null
+++ b/windows/windns/src/windns/interfaceconfig.cpp
@@ -0,0 +1,66 @@
+#include "stdafx.h"
+#include "interfaceconfig.h"
+#include "netconfighelpers.h"
+#include "libcommon/com.h"
+#include "libcommon/wmi/wmi.h"
+
+using namespace common;
+
+InterfaceConfig::InterfaceConfig(CComPtr<IWbemClassObject> instance)
+{
+ //
+ // V_xxx macros seem to require an l-value so access the correct field directly instead.
+ //
+
+ m_configIndex = wmi::WmiGetPropertyAlways(instance, L"Index").ulVal;
+
+ m_dhcp = wmi::WmiGetPropertyAlways(instance, L"DHCPEnabled").boolVal;
+
+ m_interfaceIndex = wmi::WmiGetPropertyAlways(instance, L"InterfaceIndex").ulVal;
+ m_interfaceGuid = ComConvertString(wmi::WmiGetPropertyAlways(instance, L"SettingID").bstrVal);
+
+ m_servers = nchelpers::GetDnsServers(instance);
+}
+
+InterfaceConfig::InterfaceConfig(common::serialization::Deserializer &deserializer)
+{
+ common::serialization::Deserializer &d = deserializer;
+
+ d >> m_configIndex;
+ d >> (uint8_t &)m_dhcp;
+ d >> m_interfaceIndex;
+ d >> m_interfaceGuid;
+
+ bool serversAvailable;
+
+ d >> (uint8_t &)serversAvailable;
+
+ if (serversAvailable)
+ {
+ m_servers = std::make_shared<std::vector<std::wstring> >();
+ d >> *m_servers;
+ }
+}
+
+void InterfaceConfig::serialize(common::serialization::Serializer &serializer) const
+{
+ common::serialization::Serializer &s = serializer;
+
+ s << m_configIndex;
+ s << (uint8_t)m_dhcp;
+ s << m_interfaceIndex;
+ s << m_interfaceGuid;
+
+ //
+ // TODO: Encapsulate this inside a new type.
+ //
+ if (nullptr == m_servers.get())
+ {
+ s << (uint8_t)0;
+ }
+ else
+ {
+ s << (uint8_t)1;
+ s << *m_servers;
+ }
+}
diff --git a/windows/windns/src/windns/interfaceconfig.h b/windows/windns/src/windns/interfaceconfig.h
new file mode 100644
index 0000000000..e95296b7fb
--- /dev/null
+++ b/windows/windns/src/windns/interfaceconfig.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#include "types.h"
+#include "libcommon/serialization/deserializer.h"
+#include "libcommon/serialization/serializer.h"
+#include <cstdint>
+#include <string>
+#include <vector>
+#include <atlbase.h>
+#include <wbemidl.h>
+
+class InterfaceConfig
+{
+public:
+
+ // instance = Win32_NetworkAdapterConfiguration.
+ explicit InterfaceConfig(CComPtr<IWbemClassObject> instance);
+
+ explicit InterfaceConfig(common::serialization::Deserializer &deserializer);
+ void serialize(common::serialization::Serializer &serializer) const;
+
+ void updateServers(const InterfaceConfig &rhs)
+ {
+ m_servers = rhs.m_servers;
+ }
+
+ uint32_t configIndex() const
+ {
+ return m_configIndex;
+ }
+
+ bool dhcp() const
+ {
+ return m_dhcp;
+ }
+
+ uint32_t interfaceIndex() const
+ {
+ return m_interfaceIndex;
+ }
+
+ const std::wstring &interfaceGuid() const
+ {
+ return m_interfaceGuid;
+ }
+
+ const std::vector<std::wstring> *servers() const
+ {
+ return m_servers.get();
+ }
+
+private:
+
+ uint32_t m_configIndex;
+
+ bool m_dhcp;
+
+ uint32_t m_interfaceIndex;
+ std::wstring m_interfaceGuid;
+
+ OptionalStringList m_servers;
+};
diff --git a/windows/windns/src/windns/netconfigeventsink.cpp b/windows/windns/src/windns/netconfigeventsink.cpp
new file mode 100644
index 0000000000..e4ef7ae1ce
--- /dev/null
+++ b/windows/windns/src/windns/netconfigeventsink.cpp
@@ -0,0 +1,34 @@
+#include "stdafx.h"
+#include "netconfigeventsink.h"
+#include "windns/netconfighelpers.h"
+
+using namespace common;
+
+NetConfigEventSink::NetConfigEventSink(std::shared_ptr<wmi::IConnection> connection, std::shared_ptr<ConfigManager> configManager)
+ : m_connection(connection)
+ , m_configManager(configManager)
+{
+}
+
+void NetConfigEventSink::update(CComPtr<IWbemClassObject> previous, CComPtr<IWbemClassObject> target)
+{
+ InterfaceConfig previousConfig(previous);
+ InterfaceConfig targetConfig(target);
+
+ ConfigManager::Mutex mutex(*m_configManager);
+
+ //
+ // This is OK because the config manager will reject updates
+ // that set our DNS servers.
+ //
+ if (ConfigManager::UpdateStatus::DnsApproved == m_configManager->updateConfig(previousConfig, targetConfig))
+ {
+ return;
+ }
+
+ //
+ // The update was initiated from an external source.
+ // Override current settings to enforce our selected DNS servers.
+ //
+ nchelpers::SetDnsServers(targetConfig.interfaceIndex(), m_configManager->getServers());
+}
diff --git a/windows/windns/src/windns/netconfigeventsink.h b/windows/windns/src/windns/netconfigeventsink.h
new file mode 100644
index 0000000000..342278c08c
--- /dev/null
+++ b/windows/windns/src/windns/netconfigeventsink.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "libcommon/wmi/ieventsink.h"
+#include "libcommon/wmi/iconnection.h"
+#include "windns/configmanager.h"
+#include <memory>
+
+class NetConfigEventSink : public common::wmi::IModificationEventSink
+{
+public:
+
+ NetConfigEventSink(std::shared_ptr<common::wmi::IConnection> connection, std::shared_ptr<ConfigManager> configManager);
+
+ void update(CComPtr<IWbemClassObject> previous, CComPtr<IWbemClassObject> target) override;
+
+private:
+
+ std::shared_ptr<common::wmi::IConnection> m_connection;
+ std::shared_ptr<ConfigManager> m_configManager;
+};
diff --git a/windows/windns/src/windns/netconfighelpers.cpp b/windows/windns/src/windns/netconfighelpers.cpp
new file mode 100644
index 0000000000..65e38b06c3
--- /dev/null
+++ b/windows/windns/src/windns/netconfighelpers.cpp
@@ -0,0 +1,65 @@
+#include "stdafx.h"
+#include "netconfighelpers.h"
+#include "libcommon/com.h"
+#include "libcommon/wmi/wmi.h"
+#include "libcommon/trace/xtrace.h"
+#include "netsh.h"
+
+using namespace common;
+
+namespace nchelpers
+{
+
+OptionalStringList GetDnsServers(CComPtr<IWbemClassObject> instance)
+{
+ OptionalStringList result;
+
+ auto servers = wmi::WmiGetProperty(instance, L"DNSServerSearchOrder");
+
+ if (VT_EMPTY == V_VT(&servers) || VT_NULL == V_VT(&servers))
+ {
+ return result;
+ }
+
+ result = std::make_shared<std::vector<std::wstring> >(
+ ComConvertStringArray(V_ARRAY(&servers)));
+
+ return result;
+}
+
+uint32_t GetInterfaceIndex(CComPtr<IWbemClassObject> instance)
+{
+ return wmi::WmiGetPropertyAlways(instance, L"InterfaceIndex").ulVal;
+}
+
+void SetDnsServers(uint32_t interfaceIndex, const std::vector<std::wstring> &servers)
+{
+ NetSh::SetIpv4PrimaryDns(interfaceIndex, servers[0]);
+
+ if (servers.size() > 1)
+ {
+ NetSh::SetIpv4SecondaryDns(interfaceIndex, servers[1]);
+ }
+}
+
+void RevertDnsServers(const InterfaceConfig &config)
+{
+ XTRACE("Reverting DNS configuration for interface with index=", config.interfaceIndex());
+
+ auto servers = config.servers();
+
+ if (config.dhcp() || nullptr == servers || 0 == servers->size())
+ {
+ NetSh::SetIpv4Dhcp(config.interfaceIndex());
+ return;
+ }
+
+ NetSh::SetIpv4PrimaryDns(config.interfaceIndex(), (*servers)[0]);
+
+ if (servers->size() > 1)
+ {
+ NetSh::SetIpv4SecondaryDns(config.interfaceIndex(), (*servers)[1]);
+ }
+}
+
+}
diff --git a/windows/windns/src/windns/netconfighelpers.h b/windows/windns/src/windns/netconfighelpers.h
new file mode 100644
index 0000000000..fbfef34035
--- /dev/null
+++ b/windows/windns/src/windns/netconfighelpers.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "types.h"
+#include "interfaceconfig.h"
+#include <string>
+#include <vector>
+#include <cstdint>
+#include <atlbase.h>
+#include <wbemidl.h>
+
+namespace nchelpers
+{
+
+// instance = Win32_NetworkAdapterConfiguration
+OptionalStringList GetDnsServers(CComPtr<IWbemClassObject> instance);
+
+// instance = Win32_NetworkAdapterConfiguration
+uint32_t GetInterfaceIndex(CComPtr<IWbemClassObject> instance);
+
+void SetDnsServers(uint32_t interfaceIndex, const std::vector<std::wstring> &servers);
+
+void RevertDnsServers(const InterfaceConfig &config);
+
+}
diff --git a/windows/windns/src/windns/netsh.cpp b/windows/windns/src/windns/netsh.cpp
new file mode 100644
index 0000000000..af65ace2a9
--- /dev/null
+++ b/windows/windns/src/windns/netsh.cpp
@@ -0,0 +1,159 @@
+#include "stdafx.h"
+#include "netsh.h"
+#include "libcommon/applicationrunner.h"
+#include <sstream>
+#include <stdexcept>
+
+namespace
+{
+
+void ValidateShellOut(common::ApplicationRunner &netsh)
+{
+ static const uint32_t TIMEOUT_TWO_SECONDS = 2000;
+
+ DWORD returnCode;
+
+ if (false == netsh.join(returnCode, TIMEOUT_TWO_SECONDS))
+ {
+ throw std::runtime_error("'netsh' did not complete in a timely manner");
+ }
+
+ if (returnCode != 0)
+ {
+ std::stringstream ss;
+
+ ss << "'netsh' failed the requested operation. Error: " << returnCode;
+
+ throw std::runtime_error(ss.str());
+ }
+}
+
+} // anonymous namespace
+
+//static
+void NetSh::SetIpv4PrimaryDns(uint32_t interfaceIndex, std::wstring server)
+{
+ //
+ // netsh interface ipv4 set dnsservers name="Ethernet 2" source=static address=8.8.8.8 validate=no
+ //
+ // Note: we're specifying the interface by index instead.
+ //
+
+ std::wstringstream ss;
+
+ ss << L"interface ipv4 set dnsservers name="
+ << interfaceIndex
+ << L" source=static address="
+ << server
+ << L" validate=no";
+
+ auto netsh = common::ApplicationRunner::StartWithoutConsole(L"netsh.exe", ss.str());
+
+ ValidateShellOut(*netsh);
+}
+
+//static
+void NetSh::SetIpv4SecondaryDns(uint32_t interfaceIndex, std::wstring server)
+{
+ //
+ // netsh interface ipv4 add dnsservers name="Ethernet 2" address=8.8.4.4 index=2 validate=no
+ //
+ // Note: we're specifying the interface by index instead.
+ //
+
+ std::wstringstream ss;
+
+ ss << L"interface ipv4 add dnsservers name="
+ << interfaceIndex
+ << L" address="
+ << server
+ << L" index=2 validate=no";
+
+ auto netsh = common::ApplicationRunner::StartWithoutConsole(L"netsh.exe", ss.str());
+
+ ValidateShellOut(*netsh);
+}
+
+//static
+void NetSh::SetIpv4Dhcp(uint32_t interfaceIndex)
+{
+ //
+ // netsh interface ipv4 set dnsservers name="Ethernet 2" source=dhcp
+ //
+ // Note: we're specifying the interface by index instead.
+ //
+
+ std::wstringstream ss;
+
+ ss << L"interface ipv4 set dnsservers name="
+ << interfaceIndex
+ << L" source=dhcp";
+
+ auto netsh = common::ApplicationRunner::StartWithoutConsole(L"netsh.exe", ss.str());
+
+ ValidateShellOut(*netsh);
+}
+
+//static
+void NetSh::SetIpv6PrimaryDns(uint32_t interfaceIndex, std::wstring server)
+{
+ //
+ // netsh interface ipv6 set dnsservers name="Ethernet 2" source=static address=2001:4860:4860::8888 validate=no
+ //
+ // Note: we're specifying the interface by index instead.
+ //
+
+ std::wstringstream ss;
+
+ ss << L"interface ipv6 set dnsservers name="
+ << interfaceIndex
+ << L" source=static address="
+ << server
+ << L" validate=no";
+
+ auto netsh = common::ApplicationRunner::StartWithoutConsole(L"netsh.exe", ss.str());
+
+ ValidateShellOut(*netsh);
+}
+
+//static
+void NetSh::SetIpv6SecondaryDns(uint32_t interfaceIndex, std::wstring server)
+{
+ //
+ // netsh interface ipv6 add dnsservers name="Ethernet 2" address=2001:4860:4860::8844 index=2 validate=no
+ //
+ // Note: we're specifying the interface by index instead.
+ //
+
+ std::wstringstream ss;
+
+ ss << L"interface ipv6 add dnsservers name="
+ << interfaceIndex
+ << L"address ="
+ << server
+ << L" index=2 validate=no";
+
+ auto netsh = common::ApplicationRunner::StartWithoutConsole(L"netsh.exe", ss.str());
+
+ ValidateShellOut(*netsh);
+}
+
+//static
+void NetSh::SetIpv6Dhcp(uint32_t interfaceIndex)
+{
+ //
+ // netsh interface ipv6 set dnsservers name="Ethernet 2" source=dhcp
+ //
+ // Note: we're specifying the interface by index instead.
+ //
+
+ std::wstringstream ss;
+
+ ss << L"interface ipv6 set dnsservers name="
+ << interfaceIndex
+ << L" source=dhcp";
+
+ auto netsh = common::ApplicationRunner::StartWithoutConsole(L"netsh.exe", ss.str());
+
+ ValidateShellOut(*netsh);
+}
diff --git a/windows/windns/src/windns/netsh.h b/windows/windns/src/windns/netsh.h
new file mode 100644
index 0000000000..d299cf6f87
--- /dev/null
+++ b/windows/windns/src/windns/netsh.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <string>
+#include <cstdint>
+
+class NetSh
+{
+public:
+
+ static void SetIpv4PrimaryDns(uint32_t interfaceIndex, std::wstring server);
+
+ //
+ // Caveat: This sets the primary DNS server if there isn't already one.
+ //
+ static void SetIpv4SecondaryDns(uint32_t interfaceIndex, std::wstring server);
+
+ static void SetIpv4Dhcp(uint32_t interfaceIndex);
+
+ static void SetIpv6PrimaryDns(uint32_t interfaceIndex, std::wstring server);
+ static void SetIpv6SecondaryDns(uint32_t interfaceIndex, std::wstring server);
+ static void SetIpv6Dhcp(uint32_t interfaceIndex);
+
+private:
+
+ NetSh();
+};
diff --git a/windows/windns/src/windns/stdafx.cpp b/windows/windns/src/windns/stdafx.cpp
new file mode 100644
index 0000000000..689eb89a7e
--- /dev/null
+++ b/windows/windns/src/windns/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// windns.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/windns/src/windns/stdafx.h b/windows/windns/src/windns/stdafx.h
new file mode 100644
index 0000000000..6878f4de13
--- /dev/null
+++ b/windows/windns/src/windns/stdafx.h
@@ -0,0 +1,17 @@
+// 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
+
+#include <windows.h>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <cstdint>
diff --git a/windows/windns/src/windns/targetver.h b/windows/windns/src/windns/targetver.h
new file mode 100644
index 0000000000..ae4a5c032c
--- /dev/null
+++ b/windows/windns/src/windns/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/windns/src/windns/types.h b/windows/windns/src/windns/types.h
new file mode 100644
index 0000000000..d30d556c5d
--- /dev/null
+++ b/windows/windns/src/windns/types.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <string>
+#include <vector>
+#include <memory>
+
+using OptionalStringList = std::shared_ptr<std::vector<std::wstring> >;
diff --git a/windows/windns/src/windns/windns.cpp b/windows/windns/src/windns/windns.cpp
new file mode 100644
index 0000000000..48ad863dfa
--- /dev/null
+++ b/windows/windns/src/windns/windns.cpp
@@ -0,0 +1,237 @@
+#include "stdafx.h"
+#include "windns.h"
+#include "windnscontext.h"
+#include "clientsinkinfo.h"
+#include "libcommon/serialization/deserializer.h"
+#include "interfaceconfig.h"
+#include "netconfighelpers.h"
+#include <vector>
+#include <string>
+
+namespace
+{
+
+WinDnsErrorSink g_ErrorSink = nullptr;
+void *g_ErrorContext = nullptr;
+
+WinDnsContext *g_Context = nullptr;
+
+std::vector<std::wstring> MakeStringArray(const wchar_t **strings, uint32_t numStrings)
+{
+ std::vector<std::wstring> v;
+
+ while (numStrings--)
+ {
+ v.emplace_back(*strings++);
+ }
+
+ return v;
+}
+
+} // anonymous namespace
+
+WINDNS_LINKAGE
+bool
+WINDNS_API
+WinDns_Initialize(
+ WinDnsErrorSink errorSink,
+ void *errorContext
+)
+{
+ if (nullptr != g_Context)
+ {
+ return false;
+ }
+
+ g_ErrorSink = errorSink;
+ g_ErrorContext = errorContext;
+
+ try
+ {
+ g_Context = new WinDnsContext;
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != g_ErrorSink)
+ {
+ g_ErrorSink(err.what(), g_ErrorContext);
+ }
+
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+WINDNS_LINKAGE
+bool
+WINDNS_API
+WinDns_Deinitialize(
+)
+{
+ if (nullptr == g_Context)
+ {
+ return true;
+ }
+
+ delete g_Context;
+ g_Context = nullptr;
+
+ return true;
+}
+
+WINDNS_LINKAGE
+bool
+WINDNS_API
+WinDns_Set(
+ const wchar_t **servers,
+ uint32_t numServers,
+ WinDnsConfigSink configSink,
+ void *configContext
+)
+{
+ if (nullptr == g_Context
+ || 0 == numServers
+ || nullptr == configSink)
+ {
+ return false;
+ }
+
+ try
+ {
+ ClientSinkInfo sinkInfo;
+
+ sinkInfo.errorSinkInfo = ErrorSinkInfo{ g_ErrorSink, g_ErrorContext };
+ sinkInfo.configSinkInfo = ConfigSinkInfo{ configSink, configContext };
+
+ g_Context->set(MakeStringArray(servers, numServers), sinkInfo);
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != g_ErrorSink)
+ {
+ g_ErrorSink(err.what(), g_ErrorContext);
+ }
+
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+WINDNS_LINKAGE
+bool
+WINDNS_API
+WinDns_Reset(
+)
+{
+ if (nullptr == g_Context)
+ {
+ return true;
+ }
+
+ try
+ {
+ g_Context->reset();
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != g_ErrorSink)
+ {
+ g_ErrorSink(err.what(), g_ErrorContext);
+ }
+
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+WINDNS_LINKAGE
+bool
+WINDNS_API
+WinDns_Recover(
+ const void *configData,
+ uint32_t dataLength
+)
+{
+ std::vector<InterfaceConfig> configs;
+
+ try
+ {
+ common::serialization::Deserializer d(reinterpret_cast<const uint8_t *>(configData), dataLength);
+
+ auto numConfigs = d.decode<uint32_t>();
+
+ if (numConfigs > 50)
+ {
+ return false;
+ }
+
+ configs.reserve(numConfigs);
+
+ for (; numConfigs != 0; --numConfigs)
+ {
+ configs.emplace_back(InterfaceConfig(d));
+ }
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != g_ErrorSink)
+ {
+ auto msg = std::string("Failed to deserialize recovery data: ").append(err.what());
+
+ g_ErrorSink(msg.c_str(), g_ErrorContext);
+ }
+
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+
+ if (configs.empty())
+ {
+ return true;
+ }
+
+ bool success = true;
+
+ for (const auto &config : configs)
+ {
+ try
+ {
+ nchelpers::RevertDnsServers(config);
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != g_ErrorSink)
+ {
+ auto msg = std::string("Failed to restore interface settings: ").append(err.what());
+
+ g_ErrorSink(msg.c_str(), g_ErrorContext);
+ }
+
+ success = false;
+ }
+ catch (...)
+ {
+ success = false;
+ }
+ }
+
+ return success;
+}
diff --git a/windows/windns/src/windns/windns.h b/windows/windns/src/windns/windns.h
new file mode 100644
index 0000000000..734719ca80
--- /dev/null
+++ b/windows/windns/src/windns/windns.h
@@ -0,0 +1,107 @@
+#pragma once
+#include <cstdint>
+
+//
+// WINDNS public API
+//
+
+#ifdef WINDNS_EXPORTS
+#define WINDNS_LINKAGE __declspec(dllexport)
+#else
+#define WINDNS_LINKAGE __declspec(dllimport)
+#endif
+
+#define WINDNS_API __stdcall
+
+///////////////////////////////////////////////////////////////////////////////
+// Functions
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (WINDNS_API *WinDnsErrorSink)(const char *errorMessage, void *context);
+typedef void (WINDNS_API *WinDnsConfigSink)(const void *configData, uint32_t dataLength, void *context);
+
+//
+// WinDns_Initialize:
+//
+// Call this function once at startup, to acquire resources etc.
+//
+// The OPTIONAL error callback is remembered and used to report exceptions that
+// occur as a direct or indirect result of calling into WINDNS.
+//
+// (Recall that the monitoring provided by WINDNS is threaded.)
+//
+extern "C"
+WINDNS_LINKAGE
+bool
+WINDNS_API
+WinDns_Initialize(
+ WinDnsErrorSink errorSink,
+ void *errorContext
+);
+
+//
+// WinDns_Deinitialize:
+//
+// Call this function once before unloading WINDNS or exiting the process.
+//
+extern "C"
+WINDNS_LINKAGE
+bool
+WINDNS_API
+WinDns_Deinitialize(
+);
+
+//
+// WinDns_Set:
+//
+// Configure which DNS servers should be used and start enforcing these settings.
+//
+// The 'configSink' will receive periodic callbacks with updated config data
+// until you call WinDns_Reset.
+//
+// You should persist the config data in preparation for an eventual recovery.
+//
+extern "C"
+WINDNS_LINKAGE
+bool
+WINDNS_API
+WinDns_Set(
+ const wchar_t **servers,
+ uint32_t numServers,
+ WinDnsConfigSink configSink,
+ void *configContext
+);
+
+//
+// Windns_Reset:
+//
+// Revert server settings to what they were before calling WinDns_Set.
+//
+// (Also taking into account external changes to DNS settings that have occurred
+// during the period of enforcing specific settings.)
+//
+// It's safe to discard persisted config data once WinDns_Reset returns 'true'.
+//
+extern "C"
+WINDNS_LINKAGE
+bool
+WINDNS_API
+WinDns_Reset(
+);
+
+//
+// WinDns_Recover:
+//
+// Recover adapter configurations from a previously persisted state.
+//
+// This is useful if the machine has been abruptly powered off and
+// WINDNS did not get a chance to restore settings.
+//
+extern "C"
+WINDNS_LINKAGE
+bool
+WINDNS_API
+WinDns_Recover(
+ const void *configData,
+ uint32_t dataLength
+);
diff --git a/windows/windns/src/windns/windns.vcxproj b/windows/windns/src/windns/windns.vcxproj
new file mode 100644
index 0000000000..b495e10f0b
--- /dev/null
+++ b/windows/windns/src/windns/windns.vcxproj
@@ -0,0 +1,209 @@
+<?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>{A5344205-FC37-4572-9C63-8564ECC410AC}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>windns</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|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <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)'=='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|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;WINDNS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir)../;$(ProjectDir)../../../windows-libraries/src/</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libcommon.lib;wbemuuid.lib;comsuppw.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)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;WINDNS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir)../;$(ProjectDir)../../../windows-libraries/src/</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libcommon.lib;wbemuuid.lib;comsuppw.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;WINDNS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir)../;$(ProjectDir)../../../windows-libraries/src/</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libcommon.lib;wbemuuid.lib;comsuppw.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|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;WINDNS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir)../;$(ProjectDir)../../../windows-libraries/src/</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libcommon.lib;wbemuuid.lib;comsuppw.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="clientsinkinfo.h" />
+ <ClInclude Include="configmanager.h" />
+ <ClInclude Include="interfaceconfig.h" />
+ <ClInclude Include="netconfigeventsink.h" />
+ <ClInclude Include="netconfighelpers.h" />
+ <ClInclude Include="netsh.h" />
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="targetver.h" />
+ <ClInclude Include="types.h" />
+ <ClInclude Include="windns.h" />
+ <ClInclude Include="windnscontext.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="configmanager.cpp" />
+ <ClCompile Include="dllmain.cpp" />
+ <ClCompile Include="interfaceconfig.cpp" />
+ <ClCompile Include="netconfigeventsink.cpp" />
+ <ClCompile Include="netconfighelpers.cpp" />
+ <ClCompile Include="netsh.cpp" />
+ <ClCompile Include="stdafx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="windns.cpp" />
+ <ClCompile Include="windnscontext.cpp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/windows/windns/src/windns/windns.vcxproj.filters b/windows/windns/src/windns/windns.vcxproj.filters
new file mode 100644
index 0000000000..07619150e9
--- /dev/null
+++ b/windows/windns/src/windns/windns.vcxproj.filters
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="targetver.h" />
+ <ClInclude Include="windns.h" />
+ <ClInclude Include="windnscontext.h" />
+ <ClInclude Include="configmanager.h" />
+ <ClInclude Include="netconfigeventsink.h" />
+ <ClInclude Include="netconfighelpers.h" />
+ <ClInclude Include="clientsinkinfo.h" />
+ <ClInclude Include="netsh.h" />
+ <ClInclude Include="interfaceconfig.h" />
+ <ClInclude Include="types.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp" />
+ <ClCompile Include="stdafx.cpp" />
+ <ClCompile Include="windns.cpp" />
+ <ClCompile Include="windnscontext.cpp" />
+ <ClCompile Include="configmanager.cpp" />
+ <ClCompile Include="netconfigeventsink.cpp" />
+ <ClCompile Include="netconfighelpers.cpp" />
+ <ClCompile Include="netsh.cpp" />
+ <ClCompile Include="interfaceconfig.cpp" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/windows/windns/src/windns/windnscontext.cpp b/windows/windns/src/windns/windnscontext.cpp
new file mode 100644
index 0000000000..17265f0b65
--- /dev/null
+++ b/windows/windns/src/windns/windnscontext.cpp
@@ -0,0 +1,98 @@
+#include "stdafx.h"
+#include "windnscontext.h"
+#include "libcommon/wmi/connection.h"
+#include "netconfigeventsink.h"
+#include "netconfighelpers.h"
+
+using namespace common;
+
+WinDnsContext::WinDnsContext()
+{
+ m_connection = std::make_shared<wmi::Connection>(wmi::Connection::Namespace::Cimv2);
+}
+
+WinDnsContext::~WinDnsContext()
+{
+ try
+ {
+ reset();
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != m_sinkInfo.errorSinkInfo.sink)
+ {
+ m_sinkInfo.errorSinkInfo.sink(err.what(), m_sinkInfo.errorSinkInfo.context);
+ }
+ }
+ catch (...)
+ {
+ }
+}
+
+void WinDnsContext::set(const std::vector<std::wstring> &servers, const ClientSinkInfo &sinkInfo)
+{
+ m_sinkInfo = sinkInfo;
+
+ if (nullptr == m_notification)
+ {
+ m_configManager = std::make_shared<ConfigManager>(servers, m_sinkInfo.configSinkInfo);
+
+ //
+ // Register interface configuration monitoring.
+ //
+
+ auto eventSink = std::make_shared<NetConfigEventSink>(m_connection, m_configManager);
+ auto eventDispatcher = CComPtr<wmi::IEventDispatcher>(new wmi::ModificationEventDispatcher(eventSink));
+
+ m_notification = std::make_unique<wmi::Notification>(m_connection, eventDispatcher);
+
+ m_notification->activate
+ (
+ L"SELECT * "
+ L"FROM __InstanceModificationEvent "
+ L"WITHIN 1 "
+ L"WHERE TargetInstance ISA 'Win32_NetworkAdapterConfiguration'"
+ L"AND TargetInstance.IPEnabled = True"
+ );
+ }
+ else
+ {
+ ConfigManager::Mutex mutex(*m_configManager);
+
+ m_configManager->updateServers(servers);
+ m_configManager->updateConfigSink(m_sinkInfo.configSinkInfo);
+ }
+
+ //
+ // Discover all active interfaces and apply our DNS settings.
+ //
+
+ auto resultSet = m_connection->query(L"SELECT * from Win32_NetworkAdapterConfiguration WHERE IPEnabled = True");
+
+ while (resultSet.advance())
+ {
+ nchelpers::SetDnsServers(nchelpers::GetInterfaceIndex(resultSet.result()), servers);
+ }
+}
+
+void WinDnsContext::reset()
+{
+ if (nullptr == m_notification)
+ {
+ return;
+ }
+
+ m_notification->deactivate();
+ m_notification = nullptr;
+
+ //
+ // Revert configs
+ // Safe to do without a mutex guarding the config manager
+ //
+
+ m_configManager->processConfigs([&](const InterfaceConfig &config)
+ {
+ nchelpers::RevertDnsServers(config);
+ return true;
+ });
+}
diff --git a/windows/windns/src/windns/windnscontext.h b/windows/windns/src/windns/windnscontext.h
new file mode 100644
index 0000000000..c514153edd
--- /dev/null
+++ b/windows/windns/src/windns/windnscontext.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "windns.h"
+#include "libcommon/wmi/connection.h"
+#include "libcommon/wmi/notification.h"
+#include "configmanager.h"
+#include "clientsinkinfo.h"
+#include <vector>
+#include <string>
+#include <memory>
+
+class WinDnsContext
+{
+public:
+
+ WinDnsContext();
+ ~WinDnsContext();
+
+ void set(const std::vector<std::wstring> &servers, const ClientSinkInfo &sinkInfo);
+ void reset();
+
+private:
+
+ std::shared_ptr<common::wmi::Connection> m_connection;
+ std::shared_ptr<ConfigManager> m_configManager;
+ std::unique_ptr<common::wmi::Notification> m_notification;
+ ClientSinkInfo m_sinkInfo;
+};
diff --git a/windows/windns/windns.sln b/windows/windns/windns.sln
new file mode 100644
index 0000000000..5db77f1c6b
--- /dev/null
+++ b/windows/windns/windns.sln
@@ -0,0 +1,48 @@
+
+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}") = "libcommon", "..\windows-libraries\src\libcommon\libcommon.vcxproj", "{B52E2D10-A94A-4605-914A-2DCEF6A757EF}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "windns", "src\windns\windns.vcxproj", "{A5344205-FC37-4572-9C63-8564ECC410AC}"
+ 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
+ {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
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Debug|x64.ActiveCfg = Debug|x64
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Debug|x64.Build.0 = Debug|x64
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Debug|x64.Deploy.0 = Debug|x64
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Debug|x86.ActiveCfg = Debug|Win32
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Debug|x86.Build.0 = Debug|Win32
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Debug|x86.Deploy.0 = Debug|Win32
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Release|x64.ActiveCfg = Release|x64
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Release|x64.Build.0 = Release|x64
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Release|x64.Deploy.0 = Release|x64
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Release|x86.ActiveCfg = Release|Win32
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Release|x86.Build.0 = Release|Win32
+ {A5344205-FC37-4572-9C63-8564ECC410AC}.Release|x86.Deploy.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {A28F3793-15A7-4EE5-A899-373DF084D91F}
+ EndGlobalSection
+EndGlobal
diff --git a/windows/windows-libraries b/windows/windows-libraries
-Subproject 75c07635f1430ccd9403da058d76e01435ec65a
+Subproject af32131869f182d0c9a4af7a27cd7bc27d619e0