diff options
| author | Odd Stranne <odd@mullvad.net> | 2018-05-25 16:31:35 +0200 |
|---|---|---|
| committer | Odd Stranne <odd@mullvad.net> | 2018-06-18 08:45:13 +0200 |
| commit | 96d551f1126fa74dfc9bdb92573c9029efbbc411 (patch) | |
| tree | ff52f453370b8c95f892b2ad21d074e8546fddbe /windows/windns/src | |
| parent | 0107dc9c1c5b4ff630991a031ce528697967686e (diff) | |
| download | mullvadvpn-96d551f1126fa74dfc9bdb92573c9029efbbc411.tar.xz mullvadvpn-96d551f1126fa74dfc9bdb92573c9029efbbc411.zip | |
Move windns into windows/windns
Diffstat (limited to 'windows/windns/src')
44 files changed, 2206 insertions, 0 deletions
diff --git a/windows/windns/src/dnstest/dnstest.cpp b/windows/windns/src/dnstest/dnstest.cpp Binary files differnew file mode 100644 index 0000000000..76ae8615a4 --- /dev/null +++ b/windows/windns/src/dnstest/dnstest.cpp diff --git a/windows/windns/src/dnstest/dnstest.vcxproj b/windows/windns/src/dnstest/dnstest.vcxproj new file mode 100644 index 0000000000..4e8b9776a6 --- /dev/null +++ b/windows/windns/src/dnstest/dnstest.vcxproj @@ -0,0 +1,218 @@ +<?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>dnstest</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)../../../wfpctl/libwfp/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)../../../wfpctl/libwfp/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)../../../wfpctl/libwfp/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)../../../wfpctl/libwfp/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="..\windns\comhelpers.h" /> + <ClInclude Include="..\windns\configmanager.h" /> + <ClInclude Include="..\windns\consoletracesink.h" /> + <ClInclude Include="..\windns\dnsconfig.h" /> + <ClInclude Include="..\windns\dnsreverter.h" /> + <ClInclude Include="..\windns\netconfigeventsink.h" /> + <ClInclude Include="..\windns\netconfighelpers.h" /> + <ClInclude Include="..\windns\wmi\connection.h" /> + <ClInclude Include="..\windns\wmi\eventsink.h" /> + <ClInclude Include="..\windns\wmi\iconnection.h" /> + <ClInclude Include="..\windns\wmi\methodcall.h" /> + <ClInclude Include="..\windns\wmi\notification.h" /> + <ClInclude Include="..\windns\wmi\resultset.h" /> + <ClInclude Include="stdafx.h" /> + <ClInclude Include="targetver.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\windns\comhelpers.cpp" /> + <ClCompile Include="..\windns\configmanager.cpp" /> + <ClCompile Include="..\windns\consoletracesink.cpp" /> + <ClCompile Include="..\windns\dnsconfig.cpp" /> + <ClCompile Include="..\windns\dnsreverter.cpp" /> + <ClCompile Include="..\windns\netconfigeventsink.cpp" /> + <ClCompile Include="..\windns\netconfighelpers.cpp" /> + <ClCompile Include="..\windns\wmi\connection.cpp" /> + <ClCompile Include="..\windns\wmi\eventsink.cpp" /> + <ClCompile Include="..\windns\wmi\methodcall.cpp" /> + <ClCompile Include="..\windns\wmi\notification.cpp" /> + <ClCompile Include="..\windns\wmi\resultset.cpp" /> + <ClCompile Include="dnstest.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/dnstest/dnstest.vcxproj.filters b/windows/windns/src/dnstest/dnstest.vcxproj.filters new file mode 100644 index 0000000000..07cecae9a7 --- /dev/null +++ b/windows/windns/src/dnstest/dnstest.vcxproj.filters @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="other files"> + <UniqueIdentifier>{f6f1a55f-c90a-4972-9986-37b42d9ae7a0}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="stdafx.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="targetver.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\windns\wmi\connection.h"> + <Filter>other files</Filter> + </ClInclude> + <ClInclude Include="..\windns\wmi\iconnection.h"> + <Filter>other files</Filter> + </ClInclude> + <ClInclude Include="..\windns\wmi\resultset.h"> + <Filter>other files</Filter> + </ClInclude> + <ClInclude Include="..\windns\wmi\methodcall.h"> + <Filter>other files</Filter> + </ClInclude> + <ClInclude Include="..\windns\comhelpers.h"> + <Filter>other files</Filter> + </ClInclude> + <ClInclude Include="..\windns\configmanager.h"> + <Filter>other files</Filter> + </ClInclude> + <ClInclude Include="..\windns\dnsconfig.h"> + <Filter>other files</Filter> + </ClInclude> + <ClInclude Include="..\windns\consoletracesink.h"> + <Filter>other files</Filter> + </ClInclude> + <ClInclude Include="..\windns\wmi\eventsink.h"> + <Filter>other files</Filter> + </ClInclude> + <ClInclude Include="..\windns\netconfigeventsink.h"> + <Filter>other files</Filter> + </ClInclude> + <ClInclude Include="..\windns\netconfighelpers.h"> + <Filter>other files</Filter> + </ClInclude> + <ClInclude Include="..\windns\wmi\notification.h"> + <Filter>other files</Filter> + </ClInclude> + <ClInclude Include="..\windns\dnsreverter.h"> + <Filter>other files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="stdafx.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="dnstest.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\windns\wmi\connection.cpp"> + <Filter>other files</Filter> + </ClCompile> + <ClCompile Include="..\windns\wmi\resultset.cpp"> + <Filter>other files</Filter> + </ClCompile> + <ClCompile Include="..\windns\wmi\methodcall.cpp"> + <Filter>other files</Filter> + </ClCompile> + <ClCompile Include="..\windns\comhelpers.cpp"> + <Filter>other files</Filter> + </ClCompile> + <ClCompile Include="..\windns\configmanager.cpp"> + <Filter>other files</Filter> + </ClCompile> + <ClCompile Include="..\windns\dnsconfig.cpp"> + <Filter>other files</Filter> + </ClCompile> + <ClCompile Include="..\windns\consoletracesink.cpp"> + <Filter>other files</Filter> + </ClCompile> + <ClCompile Include="..\windns\wmi\eventsink.cpp"> + <Filter>other files</Filter> + </ClCompile> + <ClCompile Include="..\windns\netconfigeventsink.cpp"> + <Filter>other files</Filter> + </ClCompile> + <ClCompile Include="..\windns\netconfighelpers.cpp"> + <Filter>other files</Filter> + </ClCompile> + <ClCompile Include="..\windns\wmi\notification.cpp"> + <Filter>other files</Filter> + </ClCompile> + <ClCompile Include="..\windns\dnsreverter.cpp"> + <Filter>other files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/windows/windns/src/dnstest/dnstest.vcxproj.user b/windows/windns/src/dnstest/dnstest.vcxproj.user new file mode 100644 index 0000000000..be25078707 --- /dev/null +++ b/windows/windns/src/dnstest/dnstest.vcxproj.user @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup /> +</Project>
\ No newline at end of file diff --git a/windows/windns/src/dnstest/stdafx.cpp b/windows/windns/src/dnstest/stdafx.cpp Binary files differnew file mode 100644 index 0000000000..cb6d7f782d --- /dev/null +++ b/windows/windns/src/dnstest/stdafx.cpp diff --git a/windows/windns/src/dnstest/stdafx.h b/windows/windns/src/dnstest/stdafx.h Binary files differnew file mode 100644 index 0000000000..94d4ed877d --- /dev/null +++ b/windows/windns/src/dnstest/stdafx.h diff --git a/windows/windns/src/dnstest/targetver.h b/windows/windns/src/dnstest/targetver.h Binary files differnew file mode 100644 index 0000000000..567cd346ef --- /dev/null +++ b/windows/windns/src/dnstest/targetver.h diff --git a/windows/windns/src/windns/comhelpers.cpp b/windows/windns/src/windns/comhelpers.cpp new file mode 100644 index 0000000000..dea61b40d9 --- /dev/null +++ b/windows/windns/src/windns/comhelpers.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" +#include "comhelpers.h" +#include <algorithm> + +_variant_t ComGetProperty(CComPtr<IWbemClassObject> obj, const std::wstring &name) +{ + _variant_t val; + + const auto status = obj->Get(name.c_str(), 0, &val, nullptr, nullptr); + + VALIDATE_COM(status, "Retrieve COM property value"); + + return val; +} + +_variant_t ComGetPropertyAlways(CComPtr<IWbemClassObject> obj, const std::wstring &name) +{ + auto val = ComGetProperty(obj, name); + + if (VT_EMPTY == V_VT(&val) || VT_NULL == V_VT(&val)) + { + throw std::runtime_error("A required COM property value is empty."); + } + + return val; +} + +std::wstring ComConvertString(BSTR src) +{ + return std::wstring(src, SysStringLen(src)); +} + +std::vector<std::wstring> ComConvertStringArray(SAFEARRAY *src) +{ + CComSafeArray<BSTR> safeArray(src); + + std::vector<std::wstring> result; + result.reserve(safeArray.GetCount()); + + for (ULONG i = 0; i < safeArray.GetCount(); ++i) + { + result.emplace_back(ComConvertString(safeArray.GetAt(i))); + } + + return result; +} + +CComSafeArray<BSTR> ComConvertIntoStringArray(const std::vector<std::wstring> &src) +{ + CComSafeArray<BSTR> result; + + std::for_each(src.begin(), src.end(), [&](const std::wstring &str) + { + result.Add(_bstr_t(str.c_str())); + }); + + return result; +} + +_variant_t ComPackageStringArray(CComSafeArray<BSTR> &src) +{ + VARIANT v; + + V_VT(&v) = VT_ARRAY | VT_BSTR; + V_ARRAY(&v) = src.Detach(); + + _variant_t vv; + + vv.Attach(v); + + return vv; +}
\ No newline at end of file diff --git a/windows/windns/src/windns/comhelpers.h b/windows/windns/src/windns/comhelpers.h new file mode 100644 index 0000000000..67e1a46aea --- /dev/null +++ b/windows/windns/src/windns/comhelpers.h @@ -0,0 +1,26 @@ +#pragma once + +#include "libcommon/error.h" +#include <string> +#include <vector> +#include <stdexcept> +#include <winerror.h> +#include <atlbase.h> +#include <comutil.h> +#include <atlsafe.h> +#include <wbemidl.h> + +#define VALIDATE_COM(status, operation)\ +if(FAILED(status))\ +{\ + ::common::error::Throw(operation, status);\ +} + +_variant_t ComGetProperty(CComPtr<IWbemClassObject> obj, const std::wstring &name); +_variant_t ComGetPropertyAlways(CComPtr<IWbemClassObject> obj, const std::wstring &name); +std::wstring ComConvertString(BSTR src); +std::vector<std::wstring> ComConvertStringArray(SAFEARRAY *src); +CComSafeArray<BSTR> ComConvertIntoStringArray(const std::vector<std::wstring> &src); + +// NOTE: This consumes the source variable +_variant_t ComPackageStringArray(CComSafeArray<BSTR> &src); diff --git a/windows/windns/src/windns/configmanager.cpp b/windows/windns/src/windns/configmanager.cpp new file mode 100644 index 0000000000..5efb48d2d0 --- /dev/null +++ b/windows/windns/src/windns/configmanager.cpp @@ -0,0 +1,86 @@ +#include "stdafx.h" +#include "configmanager.h" +#include <utility> +#include <algorithm> + +ConfigManager::ConfigManager +( + const std::vector<std::wstring> &servers, + std::shared_ptr<ITraceSink> traceSink +) + : m_servers(servers) + , m_traceSink(traceSink) +{ +} + +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; +} + +const std::vector<std::wstring> &ConfigManager::getServers() const +{ + return m_servers; +} + +bool ConfigManager::updateConfig(DnsConfig &&config) +{ + XTRACE(L"Interface configuration update for interface =", config.interfaceIndex()); + + if (false == validUpdate(config)) + { + XTRACE(L"Ignoring interface configuration update"); + return false; + } + + auto iter = m_configs.find(config.id()); + + if (m_configs.end() == iter) + { + XTRACE(L"Creating new interface configuration entry"); + m_configs.insert(std::make_pair(config.id(), std::move(config))); + } + else + { + XTRACE(L"Updating interface configuration entry"); + iter->second = std::move(config); + } + + return true; +} + +bool ConfigManager::processConfigs(std::function<bool(const DnsConfig &)> configSink) +{ + for (auto it = m_configs.begin(); it != m_configs.end(); ++it) + { + if (false == configSink(it->second)) + { + return false; + } + } + + return true; +} + +bool ConfigManager::validUpdate(const DnsConfig &config) +{ + auto updatedServers = config.servers(); + + if (nullptr == updatedServers) + { + return true; + } + + return false == std::equal(m_servers.begin(), m_servers.end(), updatedServers->begin(), updatedServers->end()); +} diff --git a/windows/windns/src/windns/configmanager.h b/windows/windns/src/windns/configmanager.h new file mode 100644 index 0000000000..3760319bbb --- /dev/null +++ b/windows/windns/src/windns/configmanager.h @@ -0,0 +1,65 @@ +#pragma once + +#include "dnsconfig.h" +#include "itracesink.h" +#include <map> +#include <string> +#include <mutex> +#include <memory> +#include <functional> + +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, + std::shared_ptr<ITraceSink> traceSink = std::make_shared<NullTraceSink>() + ); + + void lock(); + void unlock(); + + void updateServers(const std::vector<std::wstring> &servers); + const std::vector<std::wstring> &getServers() const; + + bool updateConfig(DnsConfig &&config); + + bool processConfigs(std::function<bool(const DnsConfig &)> configSink); + +private: + + std::mutex m_mutex; + std::vector<std::wstring> m_servers; + std::map<std::wstring, DnsConfig> m_configs; + + std::shared_ptr<ITraceSink> m_traceSink; + + bool validUpdate(const DnsConfig &config); +}; diff --git a/windows/windns/src/windns/consoletracesink.cpp b/windows/windns/src/windns/consoletracesink.cpp new file mode 100644 index 0000000000..6e3d415303 --- /dev/null +++ b/windows/windns/src/windns/consoletracesink.cpp @@ -0,0 +1,8 @@ +#include "stdafx.h" +#include "consoletracesink.h" +#include <iostream> + +void ConsoleTraceSink::trace(const wchar_t *msg) +{ + std::wcout << msg << std::endl; +} diff --git a/windows/windns/src/windns/consoletracesink.h b/windows/windns/src/windns/consoletracesink.h new file mode 100644 index 0000000000..765fb4a2a0 --- /dev/null +++ b/windows/windns/src/windns/consoletracesink.h @@ -0,0 +1,8 @@ +#pragma once + +#include "itracesink.h" + +struct ConsoleTraceSink : public ITraceSink +{ + void trace(const wchar_t *msg) override; +}; diff --git a/windows/windns/src/windns/dllmain.cpp b/windows/windns/src/windns/dllmain.cpp Binary files differnew file mode 100644 index 0000000000..dfde574841 --- /dev/null +++ b/windows/windns/src/windns/dllmain.cpp diff --git a/windows/windns/src/windns/dnsconfig.cpp b/windows/windns/src/windns/dnsconfig.cpp new file mode 100644 index 0000000000..5a7ca42096 --- /dev/null +++ b/windows/windns/src/windns/dnsconfig.cpp @@ -0,0 +1,20 @@ +#include "stdafx.h" +#include "dnsconfig.h" +#include "windns/comhelpers.h" + +DnsConfig::DnsConfig(CComPtr<IWbemClassObject> instance) +{ + m_configId = nchelpers::GetConfigId(instance); + m_interfaceIndex = ComGetPropertyAlways(instance, L"InterfaceIndex").uintVal; + m_servers = nchelpers::GetDnsServers(instance); +} + +const std::vector<std::wstring> *DnsConfig::servers() const +{ + return m_servers.get(); +} + +//void DnsConfig::update(CComPtr<IWbemClassObject> instance) +//{ +// m_servers = nchelpers::GetDnsServers(instance); +//} diff --git a/windows/windns/src/windns/dnsconfig.h b/windows/windns/src/windns/dnsconfig.h new file mode 100644 index 0000000000..bb9a841c8e --- /dev/null +++ b/windows/windns/src/windns/dnsconfig.h @@ -0,0 +1,42 @@ +#pragma once + +#include "windns/netconfighelpers.h" +#include <cstdint> +#include <string> +#include <vector> +#include <memory> +#include <atlbase.h> +#include <wbemidl.h> + +class DnsConfig +{ +public: + + // instance = Win32_NetworkAdapterConfiguration. + DnsConfig(CComPtr<IWbemClassObject> instance); + + DnsConfig(const DnsConfig &) = delete; + DnsConfig &operator=(const DnsConfig &) = delete; + DnsConfig(DnsConfig &&) = default; + DnsConfig &operator=(DnsConfig &&) = default; + + const std::wstring &id() const + { + return m_configId; + } + + uint32_t interfaceIndex() const + { + return m_interfaceIndex; + } + + const std::vector<std::wstring> *servers() const; + +// void update(CComPtr<IWbemClassObject> instance); + +private: + + std::wstring m_configId; + uint32_t m_interfaceIndex; + nchelpers::OptionalStringList m_servers; +}; diff --git a/windows/windns/src/windns/dnsreverter.cpp b/windows/windns/src/windns/dnsreverter.cpp new file mode 100644 index 0000000000..12d15e7bbc --- /dev/null +++ b/windows/windns/src/windns/dnsreverter.cpp @@ -0,0 +1,32 @@ +#include "stdafx.h" +#include "dnsreverter.h" +#include "wmi/methodcall.h" +#include <sstream> + +DnsReverter::DnsReverter(std::shared_ptr<ITraceSink> traceSink) + : m_traceSink(traceSink) +{ +} + +void DnsReverter::revert(wmi::IConnection &connection, const DnsConfig &config) +{ + XTRACE("Reverting DNS configuration for interface with index=", config.interfaceIndex()); + + std::wstringstream ss; + + ss << L"SELECT * FROM Win32_NetworkAdapterConfiguration " + << L"WHERE SettingID = '" << config.id() << L"'"; + + auto resultSet = connection.query(ss.str().c_str()); + + if (false == resultSet.advance()) + { + XTRACE("Unable to retrieve active configuration"); + return; + } + + auto activeConfig = resultSet.result(); + auto targetDns = config.servers(); + + nchelpers::SetDnsServers(connection, activeConfig, targetDns); +} diff --git a/windows/windns/src/windns/dnsreverter.h b/windows/windns/src/windns/dnsreverter.h new file mode 100644 index 0000000000..98de9caa8c --- /dev/null +++ b/windows/windns/src/windns/dnsreverter.h @@ -0,0 +1,19 @@ +#pragma once + +#include "wmi/iconnection.h" +#include "dnsconfig.h" +#include "itracesink.h" +#include <memory> + +class DnsReverter +{ +public: + + DnsReverter(std::shared_ptr<ITraceSink> traceSink = std::make_shared<NullTraceSink>()); + + void revert(wmi::IConnection &connection, const DnsConfig &config); + +private: + + std::shared_ptr<ITraceSink> m_traceSink; +}; diff --git a/windows/windns/src/windns/itracesink.h b/windows/windns/src/windns/itracesink.h new file mode 100644 index 0000000000..496a7df1b9 --- /dev/null +++ b/windows/windns/src/windns/itracesink.h @@ -0,0 +1,48 @@ +#pragma once + +#include <sstream> + +struct ITraceSink +{ + virtual ~ITraceSink() = 0 + { + } + + virtual void trace(const wchar_t *msg) = 0; +}; + +struct NullTraceSink : public ITraceSink +{ + void trace(const wchar_t *) override + { + } +}; + +#ifdef _DEBUG +#define TRACING_ENABLED 1 +#else +#define TRACING_ENABLED 0 +#endif + +#if TRACING_ENABLED == 1 + +#include "macroargument.h" +#define XTRACE(...) VFUNC(XTRACE, __VA_ARGS__) + +#define XTRACE1(x)\ +{\ +std::wstringstream xtrace_ss;\ +xtrace_ss << __FUNCTIONW__ << L": " << x;\ +m_traceSink->trace(xtrace_ss.str().c_str());\ +} + +#define XTRACE2(x, y)\ +{\ +std::wstringstream xtrace_ss;\ +xtrace_ss << __FUNCTIONW__ << L": " << x << L" " << y;\ +m_traceSink->trace(xtrace_ss.str().c_str());\ +} + +#else +#define XTRACE(...) +#endif diff --git a/windows/windns/src/windns/macroargument.h b/windows/windns/src/windns/macroargument.h new file mode 100644 index 0000000000..bb27c124c8 --- /dev/null +++ b/windows/windns/src/windns/macroargument.h @@ -0,0 +1,23 @@ +#pragma once + +//http://www.neff.co.at/2017/04/04/Overloading-Macros-on-the-Number-of-Arguments.html + +// Some auxiliary macros +#define EMPTY() +#define EXPAND(X) X +#define CONCAT(X,Y) X##Y + +// Get number of arguments passed by __VA_ARGS__ +// http://stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments +// NUMARGS: all arguments must be castable to int +// NARGS: pure preprocessor macro, maximum 9 arguments +#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int)) +#define NARGS(...) _NARGS_I(_AUGMENT(__VA_ARGS__)) +#define _AUGMENT(...) _UNUSED_, __VA_ARGS__ +#define _NARGS_I(...) EXPAND(_ARG_N(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +#define _ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, count, ...) count + +// Overloading Macro on Number of Arguments +// http://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments +#define _VFUNC(NAME, N) CONCAT(NAME, N) +#define VFUNC(FUNC, ...) CONCAT(_VFUNC(FUNC, NARGS(__VA_ARGS__))(__VA_ARGS__),EMPTY()) diff --git a/windows/windns/src/windns/netconfigeventsink.cpp b/windows/windns/src/windns/netconfigeventsink.cpp new file mode 100644 index 0000000000..6a506e3f3f --- /dev/null +++ b/windows/windns/src/windns/netconfigeventsink.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "netconfigeventsink.h" +#include "windns/netconfighelpers.h" + +NetConfigEventSink::NetConfigEventSink(std::shared_ptr<wmi::IConnection> connection, std::shared_ptr<ConfigManager> configManager) + : m_connection(connection) + , m_configManager(configManager) +{ +} + +void NetConfigEventSink::update(CComPtr<IWbemClassObject> instance) +{ + DnsConfig config(instance); + + ConfigManager::Mutex mutex(*m_configManager); + + // + // This is OK because the config manager will reject updates + // that set our DNS servers. + // + auto updated = m_configManager->updateConfig(std::move(config)); + + if (updated) + { + // + // Override current settings to use our DNS servers. + // + auto servers = m_configManager->getServers(); + nchelpers::SetDnsServers(*m_connection, instance, &servers); + } +} diff --git a/windows/windns/src/windns/netconfigeventsink.h b/windows/windns/src/windns/netconfigeventsink.h new file mode 100644 index 0000000000..35ab829a08 --- /dev/null +++ b/windows/windns/src/windns/netconfigeventsink.h @@ -0,0 +1,20 @@ +#pragma once + +#include "windns/wmi/eventsink.h" +#include "windns/wmi/iconnection.h" +#include "windns/configmanager.h" +#include <memory> + +class NetConfigEventSink : public wmi::IEventSink +{ +public: + + explicit NetConfigEventSink(std::shared_ptr<wmi::IConnection> connection, std::shared_ptr<ConfigManager> configManager); + + void update(CComPtr<IWbemClassObject> instance) override; + +private: + + std::shared_ptr<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..f95dd39ee4 --- /dev/null +++ b/windows/windns/src/windns/netconfighelpers.cpp @@ -0,0 +1,55 @@ +#include "stdafx.h" +#include "netconfighelpers.h" +#include "comhelpers.h" +#include "wmi/methodcall.h" + +namespace nchelpers +{ + +std::wstring GetConfigId(CComPtr<IWbemClassObject> instance) +{ + return ComConvertString(V_BSTR(&ComGetPropertyAlways(instance, L"SettingID"))); +} + +OptionalStringList GetDnsServers(CComPtr<IWbemClassObject> instance) +{ + OptionalStringList result; + + auto servers = ComGetProperty(instance, L"DNSServerSearchOrder"); + + if (VT_EMPTY == V_VT(&servers) || VT_NULL == V_VT(&servers)) + { + return result; + } + + result = std::make_unique<std::vector<std::wstring> >( + ComConvertStringArray(V_ARRAY(&servers))); + + return result; +} + +void SetDnsServers(wmi::IConnection &connection, CComPtr<IWbemClassObject> instance, + const std::vector<std::wstring> *servers) +{ + wmi::MethodCall methodCall; + + if (nullptr == servers) + { + methodCall.addNullArgument(L"DNSServerSearchOrder", VT_ARRAY | VT_BSTR); + } + else + { + auto comServers = ComConvertIntoStringArray(*servers); + methodCall.addArgument(L"DNSServerSearchOrder", ComPackageStringArray(comServers)); + } + + auto status = methodCall.invoke(connection, instance, L"SetDNSServerSearchOrder"); + + // + // TODO check status, (type? expected value?) + // + + return; +} + +} diff --git a/windows/windns/src/windns/netconfighelpers.h b/windows/windns/src/windns/netconfighelpers.h new file mode 100644 index 0000000000..6949eced7e --- /dev/null +++ b/windows/windns/src/windns/netconfighelpers.h @@ -0,0 +1,25 @@ +#pragma once + +#include "wmi/iconnection.h" +#include <string> +#include <memory> +#include <vector> +#include <atlbase.h> +#include <wbemidl.h> + +namespace nchelpers +{ + +// instance = Win32_NetworkAdapterConfiguration +std::wstring GetConfigId(CComPtr<IWbemClassObject> instance); + +using OptionalStringList = std::unique_ptr<std::vector<std::wstring> >; + +// instance = Win32_NetworkAdapterConfiguration +OptionalStringList GetDnsServers(CComPtr<IWbemClassObject> instance); + +// instance = Win32_NetworkAdapterConfiguration +void SetDnsServers(wmi::IConnection &connection, CComPtr<IWbemClassObject> instance, + const std::vector<std::wstring> *servers); + +} diff --git a/windows/windns/src/windns/stdafx.cpp b/windows/windns/src/windns/stdafx.cpp Binary files differnew file mode 100644 index 0000000000..4fdd5990e4 --- /dev/null +++ b/windows/windns/src/windns/stdafx.cpp diff --git a/windows/windns/src/windns/stdafx.h b/windows/windns/src/windns/stdafx.h Binary files differnew file mode 100644 index 0000000000..b937b12ccd --- /dev/null +++ b/windows/windns/src/windns/stdafx.h diff --git a/windows/windns/src/windns/targetver.h b/windows/windns/src/windns/targetver.h Binary files differnew file mode 100644 index 0000000000..932a2e87b6 --- /dev/null +++ b/windows/windns/src/windns/targetver.h diff --git a/windows/windns/src/windns/windns.cpp b/windows/windns/src/windns/windns.cpp new file mode 100644 index 0000000000..7a9977951f --- /dev/null +++ b/windows/windns/src/windns/windns.cpp @@ -0,0 +1,156 @@ +#include "stdafx.h" +#include "windns.h" +#include "windnscontext.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 +) +{ + if (nullptr == g_Context) + { + return false; + } + + try + { + // + // TODO: This is a temporary hack to enable alpha version. Review. + // + + delete g_Context; + g_Context = nullptr; + + g_Context = new WinDnsContext; + + // + // Onwards. + // + + return g_Context->set(MakeStringArray(servers, numServers), g_ErrorSink, g_ErrorContext); + } + catch (std::exception &err) + { + if (nullptr != g_ErrorSink) + { + g_ErrorSink(err.what(), g_ErrorContext); + } + + return false; + } + catch (...) + { + return false; + } +} + +WINDNS_LINKAGE +bool +WINDNS_API +WinDns_Reset( +) +{ + if (nullptr == g_Context) + { + return false; + } + + try + { + return g_Context->reset(); + } + catch (std::exception &err) + { + if (nullptr != g_ErrorSink) + { + g_ErrorSink(err.what(), g_ErrorContext); + } + + return false; + } + catch (...) + { + return false; + } +} diff --git a/windows/windns/src/windns/windns.h b/windows/windns/src/windns/windns.h new file mode 100644 index 0000000000..000913ad53 --- /dev/null +++ b/windows/windns/src/windns/windns.h @@ -0,0 +1,80 @@ +#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); + +// +// 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. +// +extern "C" +WINDNS_LINKAGE +bool +WINDNS_API +WinDns_Set( + const wchar_t **servers, + uint32_t numServers +); + +// +// 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.) +// +extern "C" +WINDNS_LINKAGE +bool +WINDNS_API +WinDns_Reset( +); diff --git a/windows/windns/src/windns/windns.vcxproj b/windows/windns/src/windns/windns.vcxproj new file mode 100644 index 0000000000..6554ca06ce --- /dev/null +++ b/windows/windns/src/windns/windns.vcxproj @@ -0,0 +1,224 @@ +<?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)../../../wfpctl/libwfp/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)../../../wfpctl/libwfp/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)../../../wfpctl/libwfp/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)../../../wfpctl/libwfp/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="comhelpers.h" /> + <ClInclude Include="configmanager.h" /> + <ClInclude Include="consoletracesink.h" /> + <ClInclude Include="dnsconfig.h" /> + <ClInclude Include="dnsreverter.h" /> + <ClInclude Include="itracesink.h" /> + <ClInclude Include="macroargument.h" /> + <ClInclude Include="netconfigeventsink.h" /> + <ClInclude Include="netconfighelpers.h" /> + <ClInclude Include="stdafx.h" /> + <ClInclude Include="targetver.h" /> + <ClInclude Include="windns.h" /> + <ClInclude Include="windnscontext.h" /> + <ClInclude Include="wmi\connection.h" /> + <ClInclude Include="wmi\eventsink.h" /> + <ClInclude Include="wmi\iconnection.h" /> + <ClInclude Include="wmi\methodcall.h" /> + <ClInclude Include="wmi\notification.h" /> + <ClInclude Include="wmi\resultset.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="comhelpers.cpp" /> + <ClCompile Include="configmanager.cpp" /> + <ClCompile Include="consoletracesink.cpp" /> + <ClCompile Include="dllmain.cpp" /> + <ClCompile Include="dnsconfig.cpp" /> + <ClCompile Include="dnsreverter.cpp" /> + <ClCompile Include="netconfigeventsink.cpp" /> + <ClCompile Include="netconfighelpers.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" /> + <ClCompile Include="wmi\connection.cpp" /> + <ClCompile Include="wmi\eventsink.cpp" /> + <ClCompile Include="wmi\methodcall.cpp" /> + <ClCompile Include="wmi\notification.cpp" /> + <ClCompile Include="wmi\resultset.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..2e7658c7a7 --- /dev/null +++ b/windows/windns/src/windns/windns.vcxproj.filters @@ -0,0 +1,69 @@ +<?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="wmi\connection.h"> + <Filter>wmi</Filter> + </ClInclude> + <ClInclude Include="wmi\resultset.h"> + <Filter>wmi</Filter> + </ClInclude> + <ClInclude Include="wmi\iconnection.h"> + <Filter>wmi</Filter> + </ClInclude> + <ClInclude Include="windns.h" /> + <ClInclude Include="windnscontext.h" /> + <ClInclude Include="wmi\methodcall.h"> + <Filter>wmi</Filter> + </ClInclude> + <ClInclude Include="comhelpers.h" /> + <ClInclude Include="configmanager.h" /> + <ClInclude Include="dnsconfig.h" /> + <ClInclude Include="itracesink.h" /> + <ClInclude Include="consoletracesink.h" /> + <ClInclude Include="wmi\eventsink.h"> + <Filter>wmi</Filter> + </ClInclude> + <ClInclude Include="netconfigeventsink.h" /> + <ClInclude Include="netconfighelpers.h" /> + <ClInclude Include="macroargument.h" /> + <ClInclude Include="wmi\notification.h"> + <Filter>wmi</Filter> + </ClInclude> + <ClInclude Include="dnsreverter.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="dllmain.cpp" /> + <ClCompile Include="stdafx.cpp" /> + <ClCompile Include="wmi\connection.cpp"> + <Filter>wmi</Filter> + </ClCompile> + <ClCompile Include="wmi\resultset.cpp"> + <Filter>wmi</Filter> + </ClCompile> + <ClCompile Include="windns.cpp" /> + <ClCompile Include="windnscontext.cpp" /> + <ClCompile Include="wmi\methodcall.cpp"> + <Filter>wmi</Filter> + </ClCompile> + <ClCompile Include="comhelpers.cpp" /> + <ClCompile Include="configmanager.cpp" /> + <ClCompile Include="dnsconfig.cpp" /> + <ClCompile Include="consoletracesink.cpp" /> + <ClCompile Include="wmi\eventsink.cpp"> + <Filter>wmi</Filter> + </ClCompile> + <ClCompile Include="netconfigeventsink.cpp" /> + <ClCompile Include="netconfighelpers.cpp" /> + <ClCompile Include="wmi\notification.cpp"> + <Filter>wmi</Filter> + </ClCompile> + <ClCompile Include="dnsreverter.cpp" /> + </ItemGroup> + <ItemGroup> + <Filter Include="wmi"> + <UniqueIdentifier>{5deb73ee-53cc-49ac-bcdd-0a4b38914f0e}</UniqueIdentifier> + </Filter> + </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..c7cc10c4b9 --- /dev/null +++ b/windows/windns/src/windns/windnscontext.cpp @@ -0,0 +1,102 @@ +#include "stdafx.h" +#include "windnscontext.h" +#include "wmi/connection.h" +#include "netconfigeventsink.h" +#include "dnsreverter.h" + +WinDnsContext::WinDnsContext() +{ + m_connection = std::make_shared<wmi::Connection>(wmi::Connection::Namespace::Cimv2); +} + +bool WinDnsContext::set(const std::vector<std::wstring> &servers, WinDnsErrorSink /*errorSink*/, void * /*errorContext*/) +{ + m_configManager = std::make_shared<ConfigManager>(servers); + + // + // See test app for details. + // + // Discover all active interface configurations. + // + + auto resultSet = m_connection->query(L"SELECT * from Win32_NetworkAdapterConfiguration WHERE IPEnabled = True"); + + while (resultSet.advance()) + { + auto config = DnsConfig(resultSet.result()); + m_configManager->updateConfig(std::move(config)); + } + + // + // Register interface configuration monitoring. + // + + auto eventSink = std::make_shared<NetConfigEventSink>(m_connection, m_configManager); + auto eventSinkWrapper = CComPtr<wmi::EventSink>(new wmi::EventSink(eventSink)); + + m_notification = std::make_unique<wmi::Notification>(m_connection, eventSinkWrapper); + + m_notification->activate + ( + L"SELECT * " + L"FROM __InstanceModificationEvent " + L"WITHIN 1 " + L"WHERE TargetInstance ISA 'Win32_NetworkAdapterConfiguration'" + L"AND TargetInstance.IPEnabled = True" + ); + + // + // Apply our DNS settings + // + + { + ConfigManager::Mutex mutex(*m_configManager); + + m_configManager->processConfigs([&](const DnsConfig &config) + { + std::wstringstream ss; + + ss << L"SELECT * FROM Win32_NetworkAdapterConfiguration " + << L"WHERE SettingID = '" << config.id() << L"'"; + + auto resultSet = m_connection->query(ss.str().c_str()); + + if (resultSet.advance()) + { + auto activeConfig = resultSet.result(); + nchelpers::SetDnsServers(*m_connection, activeConfig, &servers); + } + + // Continue with the next interface configuration. + return true; + }); + } + + return true; +} + +bool WinDnsContext::reset() +{ + if (nullptr == m_notification) + { + return true; + } + + m_notification->deactivate(); + + // + // Revert configs + // Safe to do without a mutex guarding the config manager + // + + DnsReverter dnsReverter; + + m_configManager->processConfigs([&](const DnsConfig &config) + { + dnsReverter.revert(*m_connection, config); + + return true; + }); + + return true; +} diff --git a/windows/windns/src/windns/windnscontext.h b/windows/windns/src/windns/windnscontext.h new file mode 100644 index 0000000000..086969cc76 --- /dev/null +++ b/windows/windns/src/windns/windnscontext.h @@ -0,0 +1,31 @@ +#pragma once + +#include "windns.h" +#include "wmi/connection.h" +#include "wmi/notification.h" +#include "configmanager.h" +#include <vector> +#include <string> +#include <memory> + +class WinDnsContext +{ +public: + + WinDnsContext(); + + // TODO: Review. + ~WinDnsContext() + { + reset(); + } + + bool set(const std::vector<std::wstring> &servers, WinDnsErrorSink errorSink, void *errorContext); + bool reset(); + +private: + + std::shared_ptr<wmi::Connection> m_connection; + std::shared_ptr<ConfigManager> m_configManager; + std::unique_ptr<wmi::Notification> m_notification; +}; diff --git a/windows/windns/src/windns/wmi/connection.cpp b/windows/windns/src/windns/wmi/connection.cpp new file mode 100644 index 0000000000..225586a11d --- /dev/null +++ b/windows/windns/src/windns/wmi/connection.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "connection.h" +#include "windns/comhelpers.h" +#include <stdexcept> +#define _WIN32_DCOM +#include <windows.h> +#include <wbemidl.h> + +namespace +{ + +const wchar_t *LiteralNamespace(wmi::Connection::Namespace ns) +{ + switch (ns) + { + case wmi::Connection::Namespace::Default: return L"root\\Default"; + case wmi::Connection::Namespace::Cimv2: return L"root\\CIMV2"; + case wmi::Connection::Namespace::StandardCimv2: return L"root\\StandardCIMV2"; + default: + { + throw std::logic_error("Missing case handler in switch clause"); + } + } +} + +} // anonymous namespace + +namespace wmi +{ + +Connection::Connection(Namespace ns) : m_queryLanguage(L"WQL") +{ + auto status = CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER, + IID_IWbemLocator, (LPVOID *)&m_locator); + + if (CO_E_NOTINITIALIZED == status) + { + VALIDATE_COM(CoInitializeEx(nullptr, COINIT_MULTITHREADED), "Initialize COM"); + + status = CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER, + IID_IWbemLocator, (LPVOID *)&m_locator); + } + + VALIDATE_COM(status, "Create COM locator instance"); + + status = m_locator->ConnectServer(_bstr_t(LiteralNamespace(ns)), nullptr, nullptr, + nullptr, 0, nullptr, nullptr, &m_services); + + VALIDATE_COM(status, "Create COM services instance"); + + status = CoSetProxyBlanket(m_services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE); + + VALIDATE_COM(status, "Configure COM services auth"); +} + +ResultSet Connection::query(const wchar_t *query) +{ + CComPtr<IEnumWbemClassObject> result; + + auto status = m_services->ExecQuery(m_queryLanguage, _bstr_t(query), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, &result); + + VALIDATE_COM(status, "Execute WMI query"); + + return ResultSet(result); +} + +} diff --git a/windows/windns/src/windns/wmi/connection.h b/windows/windns/src/windns/wmi/connection.h new file mode 100644 index 0000000000..07e5066d1c --- /dev/null +++ b/windows/windns/src/windns/wmi/connection.h @@ -0,0 +1,44 @@ +#pragma once + +#include "iconnection.h" +#include <string> +#define _WIN32_DCOM +#include <windows.h> +#include <atlbase.h> +#include <comutil.h> +#include <comdef.h> + +#pragma comment(lib, "wbemuuid.lib") + +namespace wmi +{ + +class Connection : public IConnection +{ +public: + + enum class Namespace + { + Default, + Cimv2, + StandardCimv2 + }; + + explicit Connection(Namespace ns); + + ResultSet query(const wchar_t *query) override; + + CComPtr<IWbemServices> services() override + { + return m_services; + } + +private: + + CComPtr<IWbemLocator> m_locator; + CComPtr<IWbemServices> m_services; + + _bstr_t m_queryLanguage; +}; + +} diff --git a/windows/windns/src/windns/wmi/eventsink.cpp b/windows/windns/src/windns/wmi/eventsink.cpp new file mode 100644 index 0000000000..516660ff0a --- /dev/null +++ b/windows/windns/src/windns/wmi/eventsink.cpp @@ -0,0 +1,93 @@ +#include "stdafx.h" +#include "eventsink.h" +#include "windns/comhelpers.h" + +namespace wmi +{ + +EventSink::EventSink(std::shared_ptr<IEventSink> eventSink) + : m_references(0) + , m_callbacks(0) + , m_eventSink(eventSink) +{ +} + +EventSink::~EventSink() +{ +} + +bool EventSink::processing() const +{ + // + // Cancelling the notification registration WILL NOT wait for the completion of + // callbacks currently in progress :-( + // + // (Observed on Win10) + // + + return 0 != InterlockedAdd(&m_callbacks, 0); +} + +ULONG STDMETHODCALLTYPE EventSink::AddRef() +{ + return InterlockedIncrement(&m_references); +} + +ULONG STDMETHODCALLTYPE EventSink::Release() +{ + auto refs = InterlockedDecrement(&m_references); + + if (refs == 0) + { + delete this; + } + + return refs; +} + +HRESULT STDMETHODCALLTYPE EventSink::QueryInterface(REFIID riid, void **ppv) +{ + if (IID_IUnknown == riid || IID_IWbemObjectSink == riid) + { + *ppv = (IWbemObjectSink *)this; + AddRef(); + + return WBEM_S_NO_ERROR; + } + + return E_NOINTERFACE; +} + +HRESULT STDMETHODCALLTYPE EventSink::Indicate +( + LONG numObjects, + IWbemClassObject __RPC_FAR *__RPC_FAR *objects +) +{ + InterlockedIncrement(&m_callbacks); + + for (LONG i = 0; i < numObjects; ++i) + { + CComPtr<IWbemClassObject> eventRecord(objects[i]); + + auto rawTarget = ComGetPropertyAlways(eventRecord, L"TargetInstance"); + + CComQIPtr<IWbemClassObject> target(V_UNKNOWN(&rawTarget)); + + m_eventSink->update(target); + } + + InterlockedDecrement(&m_callbacks); + + return WBEM_S_NO_ERROR; +} + +HRESULT STDMETHODCALLTYPE EventSink::SetStatus +( + LONG, HRESULT, BSTR, IWbemClassObject __RPC_FAR * +) +{ + return WBEM_S_NO_ERROR; +} + +} diff --git a/windows/windns/src/windns/wmi/eventsink.h b/windows/windns/src/windns/wmi/eventsink.h new file mode 100644 index 0000000000..2f0783d81f --- /dev/null +++ b/windows/windns/src/windns/wmi/eventsink.h @@ -0,0 +1,55 @@ +#pragma once + +#include <memory> +#include <atlbase.h> +#include <wbemidl.h> + +namespace wmi +{ + +struct IEventSink +{ + virtual ~IEventSink() = 0 + { + } + + virtual void update(CComPtr<IWbemClassObject> instance) = 0; +}; + +class EventSink : public IWbemObjectSink +{ +public: + + EventSink(std::shared_ptr<IEventSink> eventSink); + ~EventSink(); + + bool processing() const; + + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) override; + + HRESULT STDMETHODCALLTYPE Indicate + ( + LONG numObjects, + IWbemClassObject __RPC_FAR *__RPC_FAR *objects + ) + override; + + HRESULT STDMETHODCALLTYPE SetStatus + ( + LONG flags, + HRESULT result, + BSTR param, + IWbemClassObject __RPC_FAR *object + ) + override; + +private: + + LONG m_references; + mutable LONG m_callbacks; + std::shared_ptr<IEventSink> m_eventSink; +}; + +} diff --git a/windows/windns/src/windns/wmi/iconnection.h b/windows/windns/src/windns/wmi/iconnection.h new file mode 100644 index 0000000000..8683dd6ac5 --- /dev/null +++ b/windows/windns/src/windns/wmi/iconnection.h @@ -0,0 +1,20 @@ +#pragma once + +#include "resultset.h" +#include <atlbase.h> +#include <wbemidl.h> + +namespace wmi +{ + +struct IConnection +{ + virtual ~IConnection() = 0 + { + } + + virtual ResultSet query(const wchar_t *query) = 0; + virtual CComPtr<IWbemServices> services() = 0; +}; + +} diff --git a/windows/windns/src/windns/wmi/methodcall.cpp b/windows/windns/src/windns/wmi/methodcall.cpp new file mode 100644 index 0000000000..fcd4f738c8 --- /dev/null +++ b/windows/windns/src/windns/wmi/methodcall.cpp @@ -0,0 +1,104 @@ +#include "stdafx.h" +#include "methodcall.h" +#include "windns/comhelpers.h" +#include <algorithm> + +namespace wmi +{ + +void MethodCall::addArgument(const std::wstring &name, _variant_t value) +{ + m_arguments.emplace_back(Argument(name, value)); +} + +void MethodCall::addNullArgument(const std::wstring &name, CIMTYPE type) +{ + m_arguments.emplace_back(Argument(name, type)); +} + +_variant_t MethodCall::invoke(IConnection &connection, CComPtr<IWbemClassObject> instance, const std::wstring &methodName) +{ + std::for_each(m_arguments.begin(), m_arguments.end(), [&](const Argument &arg) + { + HRESULT status; + + if (arg.nullValue()) + { + status = instance->Put(arg.name().c_str(), 0, nullptr, arg.type()); + } + else + { + _variant_t &value = const_cast<variant_t &>(arg.value()); + + status = instance->Put(arg.name().c_str(), 0, &value, 0); + } + + VALIDATE_COM(status, "Register COM method argument"); + }); + + _variant_t path; + + auto status = instance->Get(_bstr_t(L"__PATH"), 0, &path, nullptr, nullptr); + + VALIDATE_COM(status, "Get COM instance path"); + + CComPtr<IWbemClassObject> result; + + status = connection.services()->ExecMethod(V_BSTR(&path), _bstr_t(methodName.c_str()), 0, nullptr, instance, &result, nullptr); + + VALIDATE_COM(status, "Execute COM method call"); + + return ComGetProperty(result, L"ReturnValue"); +} + + +// +// the following code is almost what is needed for static method calls +// just remove the path and the in-arg instance +// also, update first arg to ExecMethod +// +//_variant_t MethodCall::call(Connection &connection, CComPtr<IWbemClassObject> instance, const std::wstring &methodName) +//{ +// CComPtr<IWbemClassObject> cls; +// +// auto status = connection.m_services->GetObject(_bstr_t(L"Win32_NetworkAdapterConfiguration"), 0, nullptr, &cls, nullptr); +// VALIDATE_COM(status, "Resolve COM class"); +// +// CComPtr<IWbemClassObject> methodDefinition; +// +// status = cls->GetMethod(methodName.c_str(), 0, &methodDefinition, nullptr); +// VALIDATE_COM(status, "Resolve COM instance method"); +// +// CComPtr<IWbemClassObject> methodInstance; +// +// status = methodDefinition->SpawnInstance(0, &methodInstance); +// VALIDATE_COM(status, "Instantiate COM class for method call"); +// +// std::for_each(m_arguments.begin(), m_arguments.end(), [&](const Argument &arg) +// { +// _variant_t value(arg.value); +// +// // This works for all values except NULL +// auto hr = methodInstance->Put(arg.name.c_str(), 0, &value, 0); +// +// VALIDATE_COM(hr, "Register COM method argument"); +// }); +// +// _variant_t path; +// +// status = instance->Get(_bstr_t(L"__PATH"), 0, &path, nullptr, nullptr); +// VALIDATE_COM(status, "Get COM instance path"); +// +// CComPtr<IWbemClassObject> result; +// +// status = connection.m_services->ExecMethod(path.bstrVal, _bstr_t(methodName.c_str()), 0, nullptr, methodInstance, &result, nullptr/*?*/); +// VALIDATE_COM(status, "Execute COM method call"); +// +// return ComGetProperty(result, L"ReturnValue"); +//} +// + + + + +} diff --git a/windows/windns/src/windns/wmi/methodcall.h b/windows/windns/src/windns/wmi/methodcall.h new file mode 100644 index 0000000000..4d964b60db --- /dev/null +++ b/windows/windns/src/windns/wmi/methodcall.h @@ -0,0 +1,75 @@ +#pragma once + +#include "iconnection.h" +#include <string> +#include <vector> +#include <stdexcept> +#include <comutil.h> + +namespace wmi +{ + +class MethodCall +{ +public: + + void addArgument(const std::wstring &name, _variant_t value); + void addNullArgument(const std::wstring &name, CIMTYPE type); + + _variant_t invoke(IConnection &connection, CComPtr<IWbemClassObject> instance, const std::wstring &methodName); + +private: + + class Argument + { + public: + + Argument(const std::wstring &name, _variant_t value) + : m_name(name) + , m_value(value) + { + if (VT_NULL == V_VT(&value) || VT_EMPTY == V_VT(&value)) + { + throw std::runtime_error("Cannot add null-argument without specifying type."); + } + } + + Argument(const std::wstring &name, CIMTYPE type) + : m_name(name) + , m_type(type) + { + } + + bool nullValue() const + { + return VT_EMPTY == V_VT(&m_value); + } + + const std::wstring &name() const + { + return m_name; + } + + const _variant_t &value() const + { + return m_value; + } + + CIMTYPE type() const + { + return m_type; + } + + private: + + std::wstring m_name; + _variant_t m_value; + + // Explicitly specify type when the value is NULL. + CIMTYPE m_type; + }; + + std::vector<Argument> m_arguments; +}; + +} diff --git a/windows/windns/src/windns/wmi/notification.cpp b/windows/windns/src/windns/wmi/notification.cpp new file mode 100644 index 0000000000..a9427f6520 --- /dev/null +++ b/windows/windns/src/windns/wmi/notification.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" +#include "notification.h" +#include "windns/comhelpers.h" + +namespace wmi +{ + +Notification::Notification(std::shared_ptr<IConnection> connection, CComPtr<EventSink> eventSink) + : m_connection(connection) + , m_eventSink(eventSink) +{ +} + +Notification::~Notification() +{ + // + // TODO: Revise to avoid exceptions in dtor. + // + deactivate(); +} + +void Notification::activate(const std::wstring &query) +{ + CComPtr<IUnsecuredApartment> apartment; + + auto status = CoCreateInstance(CLSID_UnsecuredApartment, nullptr, CLSCTX_LOCAL_SERVER, IID_IUnsecuredApartment, (void**)&apartment); + VALIDATE_COM(status, "Create unsecured COM apartment"); + + CComPtr<IUnknown> unknownEventSink; + + status = m_eventSink->QueryInterface(IID_IUnknown, (void**)&unknownEventSink); + VALIDATE_COM(status, "Retrieve IUnkown interface for event sink"); + + CComPtr<IUnknown> unknownForwarder; + + status = apartment->CreateObjectStub(unknownEventSink, &unknownForwarder); + VALIDATE_COM(status, "Create forwarder for event sink"); + + status = unknownForwarder->QueryInterface(IID_IWbemObjectSink, (void**)&m_forwarder); + VALIDATE_COM(status, "Retrieve sink interface on event sink forwarder"); + + status = m_connection->services()->ExecNotificationQueryAsync(_bstr_t("WQL"), _bstr_t(query.c_str()), 0, nullptr, m_forwarder); + VALIDATE_COM(status, "Register notification query with WMI"); +} + +void Notification::deactivate() +{ + if (nullptr == m_forwarder) + { + return; + } + + auto status = m_connection->services()->CancelAsyncCall(m_forwarder); + VALIDATE_COM(status, "Cancel notification query"); + + m_forwarder.Release(); + + // + // This is a hack-solution for a corner case issue. + // + // Since cancelling the notification registration does not wait for in-progress callbacks + // to complete, we have to implement the corresponding logic ourselves. + // + // Using a Sleep() here is preferable to introducing a critical section in the callback. + // + while (m_eventSink->processing()) + { + Sleep(100); + } +} + +} diff --git a/windows/windns/src/windns/wmi/notification.h b/windows/windns/src/windns/wmi/notification.h new file mode 100644 index 0000000000..40a57f1e57 --- /dev/null +++ b/windows/windns/src/windns/wmi/notification.h @@ -0,0 +1,34 @@ +#pragma once + +#include "iconnection.h" +#include "eventsink.h" +#include <atlbase.h> +#include <memory> + +namespace wmi +{ + +class Notification +{ +public: + + Notification(std::shared_ptr<IConnection> connection, CComPtr<EventSink> eventSink); + ~Notification(); + + Notification(const Notification &) = delete; + Notification &operator=(const Notification &) = delete; + Notification(Notification &&) = delete; + Notification &operator=(Notification &&) = delete; + + void activate(const std::wstring &query); + void deactivate(); + +private: + + std::shared_ptr<IConnection> m_connection; + CComPtr<EventSink> m_eventSink; + + CComPtr<IWbemObjectSink> m_forwarder; +}; + +} diff --git a/windows/windns/src/windns/wmi/resultset.cpp b/windows/windns/src/windns/wmi/resultset.cpp new file mode 100644 index 0000000000..cea147fa76 --- /dev/null +++ b/windows/windns/src/windns/wmi/resultset.cpp @@ -0,0 +1,34 @@ +#include "stdafx.h" +#include "resultset.h" +#include "windns/comhelpers.h" +#include "libcommon/error.h" + +namespace wmi +{ + +ResultSet::ResultSet(CComPtr<IEnumWbemClassObject> rs) : m_resultset(rs) +{ +} + +bool ResultSet::advance() +{ + if (nullptr != m_result) + { + m_result.Release(); + } + + ULONG dummy; + + const auto status = m_resultset->Next(WBEM_INFINITE, 1, &m_result, &dummy); + + VALIDATE_COM(status, "Retrieve next object in COM resultset"); + + return WBEM_S_FALSE != status; +} + +CComPtr<IWbemClassObject> ResultSet::result() +{ + return m_result; +} + +} diff --git a/windows/windns/src/windns/wmi/resultset.h b/windows/windns/src/windns/wmi/resultset.h new file mode 100644 index 0000000000..6139a6051a --- /dev/null +++ b/windows/windns/src/windns/wmi/resultset.h @@ -0,0 +1,31 @@ +#pragma once + +#include <string> +#include <atlbase.h> +#include <wbemidl.h> + +namespace wmi +{ + +class ResultSet +{ +public: + + ResultSet(CComPtr<IEnumWbemClassObject> rs); + + ResultSet(const ResultSet &) = delete; + ResultSet &operator=(const ResultSet &) = delete; + ResultSet(ResultSet &&) = default; + ResultSet &operator=(ResultSet &&) = default; + + bool advance(); + + CComPtr<IWbemClassObject> result(); + +private: + + CComPtr<IEnumWbemClassObject> m_resultset; + CComPtr<IWbemClassObject> m_result; +}; + +} |
