summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2019-10-30 14:59:29 +0100
committerDavid Lönnhager <david.l@mullvad.net>2019-10-30 14:59:29 +0100
commitde601d7b1f82dcb9e36c00e9efa9dce1e2c88e8b (patch)
tree71bdfffa0b454a956802bcf24ac77ad4120d8798
parentb8ca066c0ac87b26f2581aa10bcfc743c539c615 (diff)
parent0852213c7980e2b8e387fddaf072ea8ecff74c15 (diff)
downloadmullvadvpn-de601d7b1f82dcb9e36c00e9efa9dce1e2c88e8b.tar.xz
mullvadvpn-de601d7b1f82dcb9e36c00e9efa9dce1e2c88e8b.zip
Merge branch 'offline-update-3'
-rw-r--r--talpid-core/src/offline/windows.rs4
-rw-r--r--talpid-core/src/winnet.rs1
m---------windows/windows-libraries0
-rw-r--r--windows/winnet/extras.sln18
-rw-r--r--windows/winnet/src/extras/loader/loader.cpp38
-rw-r--r--windows/winnet/src/extras/loader/loader.vcxproj17
-rw-r--r--windows/winnet/src/extras/tests/adaptermonitor.cpp816
-rw-r--r--windows/winnet/src/extras/tests/networkadaptermonitor.vcxproj.filters42
-rw-r--r--windows/winnet/src/extras/tests/stdafx.cpp8
-rw-r--r--windows/winnet/src/extras/tests/stdafx.h13
-rw-r--r--windows/winnet/src/extras/tests/targetver.h8
-rw-r--r--windows/winnet/src/extras/tests/testadapterutil.cpp254
-rw-r--r--windows/winnet/src/extras/tests/testadapterutil.h76
-rw-r--r--windows/winnet/src/extras/tests/tests.vcxproj183
-rw-r--r--windows/winnet/src/winnet/netmonitor.cpp394
-rw-r--r--windows/winnet/src/winnet/netmonitor.h65
-rw-r--r--windows/winnet/src/winnet/networkadaptermonitor.cpp330
-rw-r--r--windows/winnet/src/winnet/networkadaptermonitor.h126
-rw-r--r--windows/winnet/src/winnet/offlinemonitor.cpp207
-rw-r--r--windows/winnet/src/winnet/offlinemonitor.h35
-rw-r--r--windows/winnet/src/winnet/winnet.cpp20
-rw-r--r--windows/winnet/src/winnet/winnet.h5
-rw-r--r--windows/winnet/src/winnet/winnet.vcxproj92
-rw-r--r--windows/winnet/src/winnet/winnet.vcxproj.filters6
-rw-r--r--windows/winnet/winnet.sln10
25 files changed, 2269 insertions, 499 deletions
diff --git a/talpid-core/src/offline/windows.rs b/talpid-core/src/offline/windows.rs
index 5c6029a268..816fe89b4d 100644
--- a/talpid-core/src/offline/windows.rs
+++ b/talpid-core/src/offline/windows.rs
@@ -194,18 +194,14 @@ impl BroadcastListener {
system_state: &Mutex<SystemState>,
) -> Result<(), Error> {
let callback_context = system_state as *const _ as *mut libc::c_void;
- let mut state = system_state.lock();
- let mut current_connectivity = true;
if !winnet::WinNet_ActivateConnectivityMonitor(
Some(Self::connectivity_callback),
callback_context,
- &mut current_connectivity as *mut _,
Some(winnet::log_sink),
ptr::null_mut(),
) {
return Err(Error::ConnectivityMonitorError);
}
- state.network_connectivity = current_connectivity;
Ok(())
}
diff --git a/talpid-core/src/winnet.rs b/talpid-core/src/winnet.rs
index 49a209fcbe..c6bd6c6115 100644
--- a/talpid-core/src/winnet.rs
+++ b/talpid-core/src/winnet.rs
@@ -158,7 +158,6 @@ mod api {
pub fn WinNet_ActivateConnectivityMonitor(
callback: Option<ConnectivityCallback>,
callbackContext: *mut libc::c_void,
- currentConnectivity: *mut bool,
sink: Option<LogSink>,
sink_context: *mut c_void,
) -> bool;
diff --git a/windows/windows-libraries b/windows/windows-libraries
-Subproject 57b2caeaae9d1250070a951e3dff25c029cce97
+Subproject 04755dd2dcd6d4f3403d0b8ba90025c0319e84e
diff --git a/windows/winnet/extras.sln b/windows/winnet/extras.sln
index a6fec71c96..4eb67d34b0 100644
--- a/windows/winnet/extras.sln
+++ b/windows/winnet/extras.sln
@@ -15,6 +15,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winnet", "src\winnet\winnet
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcommon", "..\windows-libraries\src\libcommon\libcommon.vcxproj", "{B52E2D10-A94A-4605-914A-2DCEF6A757EF}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "src\extras\tests\tests.vcxproj", "{01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}"
+ ProjectSection(ProjectDependencies) = postProject
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF}
+ {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4} = {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -31,8 +37,8 @@ Global
{227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Release|x64.Build.0 = Release|x64
{227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Release|x86.ActiveCfg = Release|Win32
{227C50F4-D9F6-4D9A-84A0-33CE84432D0D}.Release|x86.Build.0 = Release|Win32
- {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x64.ActiveCfg = Debug|x64
- {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x64.Build.0 = Debug|x64
+ {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x64.ActiveCfg = Debug Static|x64
+ {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x64.Build.0 = Debug Static|x64
{89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x86.ActiveCfg = Debug|Win32
{89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x86.Build.0 = Debug|Win32
{89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Release|x64.ActiveCfg = Release|x64
@@ -47,6 +53,14 @@ Global
{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
+ {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Debug|x64.ActiveCfg = Debug|x64
+ {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Debug|x64.Build.0 = Debug|x64
+ {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Debug|x86.ActiveCfg = Debug|Win32
+ {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Debug|x86.Build.0 = Debug|Win32
+ {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Release|x64.ActiveCfg = Release|x64
+ {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Release|x64.Build.0 = Release|x64
+ {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Release|x86.ActiveCfg = Release|Win32
+ {01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/windows/winnet/src/extras/loader/loader.cpp b/windows/winnet/src/extras/loader/loader.cpp
index 648c16f37b..bdacc1567d 100644
--- a/windows/winnet/src/extras/loader/loader.cpp
+++ b/windows/winnet/src/extras/loader/loader.cpp
@@ -7,6 +7,32 @@ void __stdcall ConnectivityChanged(bool connected, void *)
std::wcout << (0 != connected? L"Connected" : L"NOT connected") << std::endl;
}
+namespace
+{
+
+void __stdcall log(MULLVAD_LOG_SINK_SEVERITY severity, const char *msg, void *)
+{
+ switch (severity)
+ {
+ case MULLVAD_LOG_SINK_SEVERITY_ERROR:
+ std::cout << "Error: ";
+ break;
+ case MULLVAD_LOG_SINK_SEVERITY_WARNING:
+ std::cout << "Warning: ";
+ break;
+ case MULLVAD_LOG_SINK_SEVERITY_INFO:
+ std::cout << "Info: ";
+ break;
+ case MULLVAD_LOG_SINK_SEVERITY_TRACE:
+ std::cout << "Trace: ";
+ break;
+ }
+
+ std::cout << msg << std::endl;
+}
+
+}
+
int main()
{
//wchar_t *alias = nullptr;
@@ -27,12 +53,12 @@ int main()
// }
//};
- bool currentConnectivity = 0;
-
- const auto status = WinNet_ActivateConnectivityMonitor(ConnectivityChanged, nullptr, &currentConnectivity, nullptr, nullptr);
-
- std::wcout << L"Current connectivity: "
- << (currentConnectivity ? L"Connected" : L"NOT connected") << std::endl;
+ const auto status = WinNet_ActivateConnectivityMonitor(
+ ConnectivityChanged,
+ nullptr,
+ log,
+ nullptr
+ );
_getwch();
diff --git a/windows/winnet/src/extras/loader/loader.vcxproj b/windows/winnet/src/extras/loader/loader.vcxproj
index 325f813778..1ad96af7bf 100644
--- a/windows/winnet/src/extras/loader/loader.vcxproj
+++ b/windows/winnet/src/extras/loader/loader.vcxproj
@@ -104,7 +104,7 @@
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
- <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WINNET_STATIC;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
@@ -112,7 +112,7 @@
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
- <AdditionalDependencies>winnet.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>
+ <AdditionalDependencies>winnet-static.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|x64'">
@@ -121,15 +121,16 @@
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
- <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WINNET_STATIC;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <AdditionalIncludeDirectories>$(SolutionDir)src</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
- <AdditionalDependencies>winnet.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>
+ <AdditionalDependencies>winnet-static.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'">
@@ -140,7 +141,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
- <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WINNET_STATIC;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
@@ -150,7 +151,7 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
- <AdditionalDependencies>winnet.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>
+ <AdditionalDependencies>winnet-static.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'">
@@ -161,7 +162,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
- <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WINNET_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
@@ -171,7 +172,7 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
- <AdditionalDependencies>winnet.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>
+ <AdditionalDependencies>winnet-static.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>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/windows/winnet/src/extras/tests/adaptermonitor.cpp b/windows/winnet/src/extras/tests/adaptermonitor.cpp
new file mode 100644
index 0000000000..bfb3da8d5e
--- /dev/null
+++ b/windows/winnet/src/extras/tests/adaptermonitor.cpp
@@ -0,0 +1,816 @@
+#include "stdafx.h"
+#include "testadapterutil.h"
+#include <iostream>
+
+#include <CppUnitTest.h>
+#include <libcommon/trace/trace.h>
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+
+namespace
+{
+
+void logFunc(common::logging::Severity severity, const char *msg)
+{
+ using common::logging::Severity;
+
+ switch (severity)
+ {
+ case Severity::Error:
+ std::cout << "Error: ";
+ break;
+ case Severity::Warning:
+ std::cout << "Warning: ";
+ break;
+ case Severity::Info:
+ std::cout << "Info: ";
+ break;
+ case Severity::Trace:
+ std::cout << "Trace: ";
+ break;
+ }
+
+ std::cout << msg << std::endl;
+}
+
+enum class LastEvent
+{
+ NoEvent,
+ Add,
+ Delete,
+ Update
+};
+
+}
+
+namespace Microsoft::VisualStudio::CppUnitTestFramework
+{
+
+template<>
+static std::wstring ToString<LastEvent>(const enum class LastEvent& t)
+{
+ switch (t)
+ {
+ case LastEvent::NoEvent:
+ return L"LastEvent::NoEvent";
+ case LastEvent::Add:
+ return L"LastEvent::Add";
+ case LastEvent::Delete:
+ return L"LastEvent::Delete";
+ case LastEvent::Update:
+ return L"LastEvent::Update";
+ }
+ return L"LastEvent::<Unknown value>";
+}
+
+}
+
+TEST_CLASS(NetworkAdapterMonitorTests)
+{
+public:
+
+ TEST_METHOD(addAdapter)
+ {
+ auto logSink = std::make_shared<common::logging::LogSink>(logFunc);
+
+ const auto filter = [](const MIB_IF_ROW2 &row) -> bool
+ {
+ return true;
+ };
+
+ const auto testProvider = std::make_shared<TestDataProvider>();
+ size_t adapterCount = 0;
+
+ NetworkAdapterMonitor inst(
+ logSink,
+ [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, UpdateType updateType) -> void
+ {
+ adapterCount = adapters.size();
+ },
+ filter,
+ testProvider
+ );
+
+ Assert::AreEqual(
+ 0ULL,
+ adapterCount,
+ L"Expected 0 adapters initially"
+ );
+
+ //
+ // Add new adapter
+ //
+
+ constexpr size_t currentLuid = 100;
+
+ MIB_IF_ROW2 adapter = { 0 };
+ adapter.InterfaceLuid.Value = currentLuid;
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+
+ MIB_IPINTERFACE_ROW iface = { 0 };
+ iface.InterfaceLuid.Value = currentLuid;
+ iface.Family = AF_INET;
+
+ testProvider->addIpInterface(adapter, iface);
+
+ testProvider->sendEvent(&iface, MibAddInstance);
+
+ Assert::AreEqual(
+ 1ULL,
+ adapterCount,
+ L"Expected new adapter"
+ );
+ }
+
+ TEST_METHOD(addAdapter_Duplicate)
+ {
+ auto logSink = std::make_shared<common::logging::LogSink>(logFunc);
+
+ const auto filter = [](const MIB_IF_ROW2 &row) -> bool
+ {
+ return true;
+ };
+
+ const auto testProvider = std::make_shared<TestDataProvider>();
+ size_t adapterCount = 0;
+
+ NetworkAdapterMonitor inst(
+ logSink,
+ [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, UpdateType updateType) -> void
+ {
+ adapterCount = adapters.size();
+ },
+ filter,
+ testProvider
+ );
+
+ //
+ // Create a fake adapter
+ //
+
+ MIB_IF_ROW2 adapter = { 0 };
+ adapter.InterfaceLuid.Value = 1;
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+
+ MIB_IPINTERFACE_ROW iface = { 0 };
+ iface.InterfaceLuid.Value = 1;
+ iface.Family = AF_INET;
+
+ testProvider->addIpInterface(adapter, iface);
+
+ testProvider->sendEvent(&iface, MibAddInstance);
+
+ Assert::AreEqual(
+ 1ULL,
+ adapterCount,
+ L"Expected new adapter"
+ );
+
+ //
+ // Register the same interface twice
+ //
+ testProvider->sendEvent(&iface, MibAddInstance);
+
+ Assert::AreEqual(
+ 1ULL,
+ adapterCount,
+ L"Expected ignored duplicate interface event"
+ );
+ }
+
+ TEST_METHOD(removeAdapter_AdminStatus)
+ {
+ auto logSink = std::make_shared<common::logging::LogSink>(logFunc);
+
+ const auto filter = [](const MIB_IF_ROW2 &row) -> bool
+ {
+ return true;
+ };
+
+ const auto testProvider = std::make_shared<TestDataProvider>();
+ size_t adapterCount = 0;
+
+ NetworkAdapterMonitor inst(
+ logSink,
+ [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, UpdateType updateType) -> void
+ {
+ adapterCount = adapters.size();
+ },
+ filter,
+ testProvider
+ );
+
+ //
+ // Create fake adapter
+ //
+ constexpr size_t luidValue = 1;
+
+ MIB_IF_ROW2 adapter = { 0 };
+ adapter.InterfaceLuid.Value = luidValue;
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+
+ MIB_IPINTERFACE_ROW iface = { 0 };
+ iface.InterfaceLuid.Value = luidValue;
+ iface.Family = AF_INET;
+
+ testProvider->addIpInterface(adapter, iface);
+
+ testProvider->sendEvent(&iface, MibAddInstance);
+
+ Assert::AreEqual(
+ luidValue,
+ adapterCount,
+ L"Expected new adapter"
+ );
+
+ //
+ // Delete adapter (AdminStatus)
+ //
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_DOWN;
+ testProvider->addAdapter(adapter);
+
+ testProvider->sendEvent(&iface, MibDeleteInstance);
+
+ testProvider->removeAdapter(adapter);
+
+ Assert::AreEqual(
+ 0ULL,
+ adapterCount,
+ L"Expected removed adapter"
+ );
+ }
+
+ TEST_METHOD(removeAdapter_NoInterfaces)
+ {
+ auto logSink = std::make_shared<common::logging::LogSink>(logFunc);
+
+ const auto filter = [](const MIB_IF_ROW2 &row) -> bool
+ {
+ return true;
+ };
+
+ const auto testProvider = std::make_shared<TestDataProvider>();
+ size_t adapterCount = 0;
+
+ NetworkAdapterMonitor inst(
+ logSink,
+ [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, UpdateType updateType) -> void
+ {
+ adapterCount = adapters.size();
+ },
+ filter,
+ testProvider
+ );
+
+ //
+ // Create fake adapter
+ //
+
+ constexpr size_t luidValue = 1;
+
+ MIB_IF_ROW2 adapter = { 0 };
+ adapter.InterfaceLuid.Value = luidValue;
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+
+ MIB_IPINTERFACE_ROW iface = { 0 };
+ iface.InterfaceLuid.Value = luidValue;
+ iface.Family = AF_INET;
+
+ testProvider->addIpInterface(adapter, iface);
+
+ testProvider->sendEvent(&iface, MibAddInstance);
+
+ Assert::AreEqual(
+ luidValue,
+ adapterCount,
+ L"Expected new adapter (with IPv4 interface)"
+ );
+
+ //
+ // Delete IP interfaces on the adapter (should report Delete)
+ //
+
+ testProvider->removeIpInterface(iface);
+ testProvider->sendEvent(&iface, MibDeleteInstance);
+
+ adapter = { 0 };
+ adapter.InterfaceLuid.Value = luidValue;
+ testProvider->removeAdapter(adapter);
+
+ Assert::AreEqual(
+ 0ULL,
+ adapterCount,
+ L"Expected removed adapter"
+ );
+ }
+
+ TEST_METHOD(removeAdapter_Duplicate)
+ {
+ auto logSink = std::make_shared<common::logging::LogSink>(logFunc);
+
+ const auto filter = [](const MIB_IF_ROW2 &row) -> bool
+ {
+ return true;
+ };
+
+ const auto testProvider = std::make_shared<TestDataProvider>();
+ size_t adapterCount = 0;
+
+ NetworkAdapterMonitor inst(
+ logSink,
+ [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, UpdateType updateType) -> void
+ {
+ adapterCount = adapters.size();
+ },
+ filter,
+ testProvider
+ );
+
+ //
+ // Create fake adapter
+ //
+ MIB_IF_ROW2 adapter = { 0 };
+ adapter.InterfaceLuid.Value = 1;
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+
+ MIB_IPINTERFACE_ROW iface = { 0 };
+ iface.InterfaceLuid.Value = 1;
+ iface.Family = AF_INET;
+
+ testProvider->addIpInterface(adapter, iface);
+
+ testProvider->sendEvent(&iface, MibAddInstance);
+
+ Assert::AreEqual(
+ 1ULL,
+ adapterCount,
+ L"Expected new adapter"
+ );
+
+ //
+ // Duplicate deletion events
+ //
+
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_DOWN;
+ testProvider->addAdapter(adapter); // update status
+
+ testProvider->removeIpInterface(iface);
+
+ testProvider->sendEvent(&iface, MibDeleteInstance);
+ testProvider->sendEvent(&iface, MibDeleteInstance);
+ testProvider->sendEvent(&iface, MibDeleteInstance);
+ testProvider->sendEvent(&iface, MibDeleteInstance);
+
+ Assert::AreEqual(
+ 0ULL,
+ adapterCount,
+ L"State inconsistent after duplicate Delete event"
+ );
+ }
+
+ TEST_METHOD(addIPv6Interface)
+ {
+ auto logSink = std::make_shared<common::logging::LogSink>(logFunc);
+
+ const auto filter = [](const MIB_IF_ROW2 &row) -> bool
+ {
+ return true;
+ };
+
+ const auto testProvider = std::make_shared<TestDataProvider>();
+ size_t adapterCount = 0;
+
+ NetworkAdapterMonitor inst(
+ logSink,
+ [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, UpdateType updateType) -> void
+ {
+ adapterCount = adapters.size();
+ },
+ filter,
+ testProvider
+ );
+
+ //
+ // Add IPv6 interface
+ //
+
+ constexpr size_t currentLuid = 100;
+
+ MIB_IF_ROW2 adapter = { 0 };
+ adapter.InterfaceLuid.Value = currentLuid;
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+
+ MIB_IPINTERFACE_ROW iface = { 0 };
+ iface.InterfaceLuid.Value = currentLuid;
+ iface.Family = AF_INET6;
+
+ testProvider->addIpInterface(adapter, iface);
+
+ testProvider->sendEvent(&iface, MibAddInstance);
+
+ Assert::AreEqual(
+ 1ULL,
+ adapterCount,
+ L"Expected new adapter"
+ );
+ }
+
+ TEST_METHOD(addIPv4And6Interface)
+ {
+ auto logSink = std::make_shared<common::logging::LogSink>(logFunc);
+
+ const auto filter = [](const MIB_IF_ROW2 &row) -> bool
+ {
+ return true;
+ };
+
+ const auto testProvider = std::make_shared<TestDataProvider>();
+ size_t adapterCount = 0;
+
+ NetworkAdapterMonitor inst(
+ logSink,
+ [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, UpdateType updateType) -> void
+ {
+ adapterCount = adapters.size();
+ },
+ filter,
+ testProvider
+ );
+
+ constexpr size_t currentLuid = 1;
+ constexpr size_t expectedCount = 1;
+
+ MIB_IF_ROW2 adapter = { 0 };
+ adapter.InterfaceLuid.Value = currentLuid;
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+
+ //
+ // Add IPv4 interface
+ //
+
+ MIB_IPINTERFACE_ROW iface4 = { 0 };
+ iface4.InterfaceLuid.Value = currentLuid;
+ iface4.Family = AF_INET;
+ testProvider->addIpInterface(adapter, iface4);
+ testProvider->sendEvent(&iface4, MibAddInstance);
+
+ //
+ // Add IPv6 interface
+ //
+
+ MIB_IPINTERFACE_ROW iface6 = { 0 };
+ iface6.InterfaceLuid.Value = currentLuid;
+ iface6.Family = AF_INET6;
+ testProvider->addIpInterface(adapter, iface6);
+ testProvider->sendEvent(&iface6, MibAddInstance);
+
+ Assert::AreEqual(
+ expectedCount,
+ adapterCount,
+ L"Expected single adapter with two IP interfaces"
+ );
+ }
+
+ TEST_METHOD(addIPv4And6Interface_RemoveIPv4)
+ {
+ auto logSink = std::make_shared<common::logging::LogSink>(logFunc);
+
+ const auto filter = [](const MIB_IF_ROW2 &row) -> bool
+ {
+ return true;
+ };
+
+ const auto testProvider = std::make_shared<TestDataProvider>();
+ size_t adapterCount = 0;
+
+ NetworkAdapterMonitor inst(
+ logSink,
+ [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, UpdateType updateType) -> void
+ {
+ adapterCount = adapters.size();
+ },
+ filter,
+ testProvider
+ );
+
+ constexpr size_t currentLuid = 1;
+
+ MIB_IF_ROW2 adapter = { 0 };
+ adapter.InterfaceLuid.Value = currentLuid;
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+
+ //
+ // Add IPv4 interface
+ //
+
+ MIB_IPINTERFACE_ROW iface4 = { 0 };
+ iface4.InterfaceLuid.Value = currentLuid;
+ iface4.Family = AF_INET;
+ testProvider->addIpInterface(adapter, iface4);
+ testProvider->sendEvent(&iface4, MibAddInstance);
+
+ //
+ // Add IPv6 interface
+ //
+
+ MIB_IPINTERFACE_ROW iface6 = { 0 };
+ iface6.InterfaceLuid.Value = currentLuid;
+ iface6.Family = AF_INET6;
+ testProvider->addIpInterface(adapter, iface6);
+ testProvider->sendEvent(&iface6, MibAddInstance);
+
+ //
+ // Remove IPv4 interface
+ //
+ testProvider->removeIpInterface(iface4);
+ testProvider->sendEvent(&iface4, MibDeleteInstance);
+
+ constexpr size_t expectedCount = 1;
+
+ Assert::AreEqual(
+ expectedCount,
+ adapterCount,
+ L"Expected single adapter (with IPv6 interface)"
+ );
+ }
+
+ TEST_METHOD(addIPv4And6Interface_RemoveIPv6)
+ {
+ auto logSink = std::make_shared<common::logging::LogSink>(logFunc);
+
+ const auto filter = [](const MIB_IF_ROW2 &row) -> bool
+ {
+ return true;
+ };
+
+ const auto testProvider = std::make_shared<TestDataProvider>();
+ size_t adapterCount = 0;
+
+ NetworkAdapterMonitor inst(
+ logSink,
+ [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, UpdateType updateType) -> void
+ {
+ adapterCount = adapters.size();
+ },
+ filter,
+ testProvider
+ );
+
+ constexpr size_t currentLuid = 1;
+
+ MIB_IF_ROW2 adapter = { 0 };
+ adapter.InterfaceLuid.Value = currentLuid;
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+
+ //
+ // Add IPv4 interface
+ //
+
+ MIB_IPINTERFACE_ROW iface4 = { 0 };
+ iface4.InterfaceLuid.Value = currentLuid;
+ iface4.Family = AF_INET;
+ testProvider->addIpInterface(adapter, iface4);
+ testProvider->sendEvent(&iface4, MibAddInstance);
+
+ //
+ // Add IPv6 interface
+ //
+
+ MIB_IPINTERFACE_ROW iface6 = { 0 };
+ iface6.InterfaceLuid.Value = currentLuid;
+ iface6.Family = AF_INET6;
+ testProvider->addIpInterface(adapter, iface6);
+ testProvider->sendEvent(&iface6, MibAddInstance);
+
+ //
+ // Remove IPv6 interface
+ //
+ testProvider->removeIpInterface(iface6);
+ testProvider->sendEvent(&iface6, MibDeleteInstance);
+
+ constexpr size_t expectedCount = 1;
+
+ Assert::AreEqual(
+ expectedCount,
+ adapterCount,
+ L"Expected single adapter (with IPv4 interface)"
+ );
+ }
+
+ TEST_METHOD(addIPv4And6Interface_RemoveBoth)
+ {
+ auto logSink = std::make_shared<common::logging::LogSink>(logFunc);
+
+ const auto filter = [](const MIB_IF_ROW2 &row) -> bool
+ {
+ return true;
+ };
+
+ const auto testProvider = std::make_shared<TestDataProvider>();
+ size_t adapterCount = 0;
+
+ NetworkAdapterMonitor inst(
+ logSink,
+ [&adapterCount](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, UpdateType updateType) -> void
+ {
+ adapterCount = adapters.size();
+ },
+ filter,
+ testProvider
+ );
+
+ constexpr size_t currentLuid = 1;
+
+ MIB_IF_ROW2 adapter = { 0 };
+ adapter.InterfaceLuid.Value = currentLuid;
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+
+ //
+ // Add IPv4 interface
+ //
+
+ MIB_IPINTERFACE_ROW iface4 = { 0 };
+ iface4.InterfaceLuid.Value = currentLuid;
+ iface4.Family = AF_INET;
+ testProvider->addIpInterface(adapter, iface4);
+ testProvider->sendEvent(&iface4, MibAddInstance);
+
+ //
+ // Add IPv6 interface
+ //
+
+ MIB_IPINTERFACE_ROW iface6 = { 0 };
+ iface6.InterfaceLuid.Value = currentLuid;
+ iface6.Family = AF_INET6;
+ testProvider->addIpInterface(adapter, iface6);
+ testProvider->sendEvent(&iface6, MibAddInstance);
+
+ //
+ // Remove IP interfaces
+ //
+ testProvider->removeIpInterface(iface4);
+ testProvider->sendEvent(&iface4, MibDeleteInstance);
+ testProvider->removeIpInterface(iface6);
+ testProvider->sendEvent(&iface6, MibDeleteInstance);
+
+ constexpr size_t expectedCount = 0;
+
+ Assert::AreEqual(
+ expectedCount,
+ adapterCount,
+ L"Expected no adapter (0 IP interfaces)"
+ );
+ }
+
+ TEST_METHOD(filter)
+ {
+ auto logSink = std::make_shared<common::logging::LogSink>(logFunc);
+
+ const auto testProvider = std::make_shared<TestDataProvider>();
+
+ //
+ // Exclude adapters not connected to the internet,
+ // loopback devices, and software adapters
+ //
+
+ const auto filter = [](const MIB_IF_ROW2 &row) -> bool
+ {
+ switch (row.InterfaceLuid.Info.IfType)
+ {
+ case IF_TYPE_SOFTWARE_LOOPBACK:
+ {
+ return false;
+ }
+ }
+
+ if (FALSE == row.InterfaceAndOperStatusFlags.HardwareInterface)
+ {
+ return false;
+ }
+ return IfOperStatusUp == row.OperStatus
+ && MediaConnectStateConnected == row.MediaConnectState;
+ };
+
+ size_t adapterCount = 0;
+ LastEvent lastEvent = LastEvent::NoEvent;
+
+ NetworkAdapterMonitor inst(
+ logSink,
+ [&adapterCount, &lastEvent](const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, UpdateType updateType) -> void
+ {
+ switch (updateType)
+ {
+ case UpdateType::Add:
+ lastEvent = LastEvent::Add;
+ break;
+ case UpdateType::Delete:
+ lastEvent = LastEvent::Delete;
+ break;
+ case UpdateType::Update:
+ lastEvent = LastEvent::Update;
+ break;
+ default:
+ Assert::Fail(L"Unhandled update type");
+ }
+
+ adapterCount = adapters.size();
+ },
+ filter,
+ testProvider
+ );
+
+ //
+ // Our filter should ignore loopback devices
+ //
+
+ constexpr size_t loopbackLuid = 1;
+
+ MIB_IF_ROW2 adapter = { 0 };
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+ adapter.InterfaceLuid.Value = loopbackLuid;
+ adapter.InterfaceLuid.Info.IfType = IF_TYPE_SOFTWARE_LOOPBACK;
+ adapter.MediaConnectState = MediaConnectStateConnected;
+ adapter.InterfaceAndOperStatusFlags.HardwareInterface = TRUE;
+ adapter.OperStatus = IfOperStatusUp;
+
+ MIB_IPINTERFACE_ROW iface4 = { 0 };
+ iface4.InterfaceLuid.Value = loopbackLuid;
+ iface4.Family = AF_INET;
+
+ lastEvent = LastEvent::NoEvent;
+
+ testProvider->addIpInterface(adapter, iface4);
+ testProvider->sendEvent(&iface4, MibAddInstance);
+
+ Assert::AreEqual(LastEvent::NoEvent, lastEvent, L"Unexpectedly received event for loopback adapter");
+
+ Assert::AreEqual(
+ 0ULL,
+ adapterCount,
+ L"Loopback adapter was not filtered correctly"
+ );
+
+ testProvider->removeIpInterface(iface4);
+ testProvider->sendEvent(&iface4, MibDeleteInstance);
+ testProvider->removeAdapter(adapter);
+
+ Assert::AreEqual(LastEvent::NoEvent, lastEvent, L"Unexpectedly received event for loopback adapter");
+
+ //
+ // Our filter should ignore devices not connected to the internet
+ //
+
+ constexpr size_t disconnectedLuid = 2;
+
+ adapter = { 0 };
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+ adapter.InterfaceLuid.Value = disconnectedLuid;
+ adapter.MediaConnectState = MediaConnectStateDisconnected;
+ adapter.InterfaceAndOperStatusFlags.HardwareInterface = TRUE;
+ adapter.OperStatus = IfOperStatusUp;
+
+ iface4 = { 0 };
+ iface4.InterfaceLuid.Value = disconnectedLuid;
+ iface4.Family = AF_INET;
+ testProvider->addIpInterface(adapter, iface4);
+ testProvider->sendEvent(&iface4, MibAddInstance);
+
+ Assert::AreEqual(LastEvent::NoEvent, lastEvent, L"Unexpectedly received event for disconnected adapter");
+
+ testProvider->removeIpInterface(iface4);
+ testProvider->sendEvent(&iface4, MibDeleteInstance);
+ testProvider->removeAdapter(adapter);
+
+ Assert::AreEqual(LastEvent::NoEvent, lastEvent, L"Unexpectedly received event for disconnected adapter");
+
+ //
+ // Report events for hardware devices
+ //
+
+ constexpr size_t onlineHardwareLuid = 3;
+
+ adapter = { 0 };
+ adapter.AdminStatus = NET_IF_ADMIN_STATUS_UP;
+ adapter.InterfaceLuid.Value = onlineHardwareLuid;
+ adapter.MediaConnectState = MediaConnectStateConnected;
+ adapter.InterfaceAndOperStatusFlags.HardwareInterface = TRUE;
+ adapter.OperStatus = IfOperStatusUp;
+
+ iface4 = { 0 };
+ iface4.InterfaceLuid.Value = onlineHardwareLuid;
+ iface4.Family = AF_INET;
+ testProvider->addIpInterface(adapter, iface4);
+ testProvider->sendEvent(&iface4, MibAddInstance);
+
+ Assert::AreEqual(LastEvent::Add, lastEvent, L"Expected event for connected adapter was not received");
+
+ lastEvent = LastEvent::NoEvent;
+
+ testProvider->removeIpInterface(iface4);
+ testProvider->sendEvent(&iface4, MibDeleteInstance);
+ testProvider->removeAdapter(adapter);
+
+ Assert::AreEqual(LastEvent::Delete, lastEvent, L"Expected event for connected adapter was not received");
+ }
+};
diff --git a/windows/winnet/src/extras/tests/networkadaptermonitor.vcxproj.filters b/windows/winnet/src/extras/tests/networkadaptermonitor.vcxproj.filters
new file mode 100644
index 0000000000..08573b397e
--- /dev/null
+++ b/windows/winnet/src/extras/tests/networkadaptermonitor.vcxproj.filters
@@ -0,0 +1,42 @@
+<?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;ipp;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>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="stdafx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="targetver.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="testadapterutil.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="stdafx.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="adaptermonitor.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="offlinemonitor.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="testadapterutil.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/windows/winnet/src/extras/tests/stdafx.cpp b/windows/winnet/src/extras/tests/stdafx.cpp
new file mode 100644
index 0000000000..c5bc6dac72
--- /dev/null
+++ b/windows/winnet/src/extras/tests/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// networkadaptermonitor.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/winnet/src/extras/tests/stdafx.h b/windows/winnet/src/extras/tests/stdafx.h
new file mode 100644
index 0000000000..6505f3e020
--- /dev/null
+++ b/windows/winnet/src/extras/tests/stdafx.h
@@ -0,0 +1,13 @@
+// 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"
+
+// Headers for CppUnitTest
+#include <CppUnitTest.h>
+
+// TODO: reference additional headers your program requires here
diff --git a/windows/winnet/src/extras/tests/targetver.h b/windows/winnet/src/extras/tests/targetver.h
new file mode 100644
index 0000000000..87c0086de7
--- /dev/null
+++ b/windows/winnet/src/extras/tests/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/winnet/src/extras/tests/testadapterutil.cpp b/windows/winnet/src/extras/tests/testadapterutil.cpp
new file mode 100644
index 0000000000..09d488bc27
--- /dev/null
+++ b/windows/winnet/src/extras/tests/testadapterutil.cpp
@@ -0,0 +1,254 @@
+#include "stdafx.h"
+
+#include "testadapterutil.h"
+
+#include <CppUnitTest.h>
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+#include <libcommon/logging/logsink.h>
+#include "../../winnet/networkadaptermonitor.h"
+
+using FilterType = NetworkAdapterMonitor::FilterType;
+using UpdateSinkType = NetworkAdapterMonitor::UpdateSinkType;
+using UpdateType = NetworkAdapterMonitor::UpdateType;
+
+//
+// MibIfTable
+//
+
+#include <algorithm>
+
+void MibIfTable::add(const MIB_IF_ROW2 &row)
+{
+ const auto it = std::find_if(
+ m_table.begin(),
+ m_table.end(),
+ [&row](const MIB_IF_ROW2 &elem)
+ {
+ return elem.InterfaceLuid.Value == row.InterfaceLuid.Value;
+ }
+ );
+
+ if (m_table.end() == it)
+ {
+ m_table.push_back(row);
+ }
+ else
+ {
+ *it = row;
+ }
+}
+
+void MibIfTable::remove(const MIB_IF_ROW2 &row)
+{
+ const auto it = std::find_if(
+ m_table.begin(),
+ m_table.end(),
+ [&row](const MIB_IF_ROW2 &elem)
+ {
+ return elem.InterfaceLuid.Value == row.InterfaceLuid.Value;
+ }
+ );
+
+ if (m_table.end() != it)
+ {
+ m_table.erase(it);
+ }
+}
+
+
+//
+// TestDataProvider
+//
+
+DWORD TestDataProvider::notifyIpInterfaceChange(
+ ADDRESS_FAMILY Family,
+ PIPINTERFACE_CHANGE_CALLBACK Callback,
+ PVOID CallerContext,
+ BOOLEAN InitialNotification,
+ HANDLE *NotificationHandle
+)
+{
+ // TODO: assert: m_callback == nullptr
+ // TODO: multiple callbacks?
+ m_callback = Callback;
+ m_context = CallerContext;
+
+ return NO_ERROR;
+}
+
+DWORD TestDataProvider::cancelMibChangeNotify2(HANDLE NotificationHandle)
+{
+ // TODO: assert: m_callback != nullptr
+ // TODO: multiple callbacks?
+ m_callback = nullptr;
+ m_context = nullptr;
+
+ return NO_ERROR;
+}
+
+DWORD TestDataProvider::getIfTable2(PMIB_IF_TABLE2 *tableOut)
+{
+ MIB_IF_TABLE2 *tableCopy = reinterpret_cast<MIB_IF_TABLE2*>(new uint8_t[
+ sizeof(MIB_IF_TABLE2)
+ + sizeof(MIB_IF_ROW2) * m_adapterTable.entries().size()
+ ]);
+
+ tableCopy->NumEntries = static_cast<ULONG>(m_adapterTable.entries().size());
+
+ std::copy(
+ m_adapterTable.entries().begin(),
+ m_adapterTable.entries().end(),
+ static_cast<MIB_IF_ROW2*>(tableCopy->Table)
+ );
+
+ *tableOut = tableCopy;
+ return NO_ERROR;
+}
+
+void TestDataProvider::freeMibTable(PVOID Memory)
+{
+ delete[] reinterpret_cast<uint8_t*>(Memory);
+}
+
+DWORD TestDataProvider::getIfEntry2(PMIB_IF_ROW2 Row)
+{
+ // TODO: accept InterfaceIndex as well
+ // FIXME: should ERROR_INVALID_PARAMETER be returned if LUID = 0?
+
+ if (nullptr == Row)
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ const auto it = std::find_if(
+ m_adapterTable.entries().begin(),
+ m_adapterTable.entries().end(),
+ [&Row](const MIB_IF_ROW2 &entry)
+ {
+ return entry.InterfaceLuid.Value == Row->InterfaceLuid.Value;
+ }
+ );
+
+ if (m_adapterTable.entries().end() == it)
+ {
+ return ERROR_FILE_NOT_FOUND;
+ }
+
+ *Row = *it;
+
+ return NO_ERROR;
+}
+
+DWORD TestDataProvider::getIpInterfaceEntry(PMIB_IPINTERFACE_ROW Row)
+{
+ // TODO: accept InterfaceIndex as well
+ // FIXME: should ERROR_INVALID_PARAMETER be returned if LUID = 0?
+
+ if (nullptr == Row)
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+ if (AF_INET != Row->Family && AF_INET6 != Row->Family)
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ bool foundMatchingLuid = false;
+
+ for (const auto &candidate : m_ipInterfaces)
+ {
+ if (candidate.InterfaceLuid.Value != Row->InterfaceLuid.Value)
+ {
+ continue;
+ }
+
+ foundMatchingLuid = true;
+
+ if (Row->Family == candidate.Family)
+ {
+ *Row = candidate;
+ return NO_ERROR;
+ }
+ }
+
+ if (foundMatchingLuid)
+ {
+ // "the Row parameter does not match the IP address family
+ // specified in the Family member in the
+ // MIB_IPINTERFACE_ROW structure."
+ return ERROR_NOT_FOUND;
+ }
+
+ //
+ // The LUID is also valid if it exists among adapters
+ // without an IP interface
+ //
+ const auto it = std::find_if(
+ m_adapterTable.entries().begin(),
+ m_adapterTable.entries().end(),
+ [Row](const MIB_IF_ROW2 &elem)
+ {
+ return Row->InterfaceLuid.Value == elem.InterfaceLuid.Value;
+ }
+ );
+
+ if (m_adapterTable.entries().end() != it)
+ {
+ return ERROR_NOT_FOUND;
+ }
+
+ return ERROR_FILE_NOT_FOUND;
+}
+
+void TestDataProvider::addAdapter(const MIB_IF_ROW2& adapter)
+{
+ m_adapterTable.add(adapter);
+}
+
+void TestDataProvider::addIpInterface(const MIB_IF_ROW2& adapter, const MIB_IPINTERFACE_ROW& iface)
+{
+ addAdapter(adapter);
+ m_ipInterfaces.push_back(iface);
+}
+
+void TestDataProvider::removeAdapter(const MIB_IF_ROW2& adapter)
+{
+ for (auto it = m_ipInterfaces.begin(); m_ipInterfaces.end() != it; )
+ {
+ if (it->InterfaceLuid.Value == adapter.InterfaceLuid.Value)
+ {
+ it = m_ipInterfaces.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ m_adapterTable.remove(adapter);
+}
+
+void TestDataProvider::removeIpInterface(const MIB_IPINTERFACE_ROW& iface)
+{
+ const auto it = std::find_if(
+ m_ipInterfaces.begin(),
+ m_ipInterfaces.end(),
+ [&iface](const MIB_IPINTERFACE_ROW &elem)
+ {
+ return iface.InterfaceLuid.Value == elem.InterfaceLuid.Value
+ && iface.Family == elem.Family;
+ }
+ );
+
+ if (m_ipInterfaces.end() != it)
+ {
+ m_ipInterfaces.erase(it);
+ }
+}
+
+void TestDataProvider::sendEvent(MIB_IPINTERFACE_ROW *hint, MIB_NOTIFICATION_TYPE updateType)
+{
+ m_callback(m_context, hint, updateType);
+}
diff --git a/windows/winnet/src/extras/tests/testadapterutil.h b/windows/winnet/src/extras/tests/testadapterutil.h
new file mode 100644
index 0000000000..4b4ae80799
--- /dev/null
+++ b/windows/winnet/src/extras/tests/testadapterutil.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <functional>
+
+#include <WinSock2.h>
+
+#include <libcommon/logging/logsink.h>
+#include "../../winnet/networkadaptermonitor.h"
+
+using FilterType = NetworkAdapterMonitor::FilterType;
+using UpdateSinkType = NetworkAdapterMonitor::UpdateSinkType;
+using UpdateType = NetworkAdapterMonitor::UpdateType;
+
+
+class MibIfTable
+{
+ std::vector<MIB_IF_ROW2> m_table;
+
+public:
+
+ void add(const MIB_IF_ROW2 &row);
+ void remove(const MIB_IF_ROW2 &row);
+
+ const std::vector<MIB_IF_ROW2>& entries() const
+ {
+ return m_table;
+ }
+};
+
+class TestDataProvider : public NetworkAdapterMonitor::IDataProvider
+{
+
+ PIPINTERFACE_CHANGE_CALLBACK m_callback;
+ void *m_context;
+
+ MibIfTable m_adapterTable;
+ std::vector<MIB_IPINTERFACE_ROW> m_ipInterfaces;
+
+public:
+
+ TestDataProvider() : m_callback(nullptr), m_context(nullptr)
+ {
+ }
+
+ TestDataProvider(const TestDataProvider&) = delete;
+ TestDataProvider(TestDataProvider&&) = delete;
+ TestDataProvider& operator=(const TestDataProvider&) = delete;
+ TestDataProvider& operator=(const TestDataProvider&&) = delete;
+
+ DWORD notifyIpInterfaceChange(
+ ADDRESS_FAMILY Family,
+ PIPINTERFACE_CHANGE_CALLBACK Callback,
+ PVOID CallerContext,
+ BOOLEAN InitialNotification,
+ HANDLE *NotificationHandle
+ ) override;
+ DWORD cancelMibChangeNotify2(HANDLE NotificationHandle) override;
+
+ DWORD getIfTable2(PMIB_IF_TABLE2 *Table) override;
+ void freeMibTable(PVOID Memory) override;
+
+ DWORD getIfEntry2(PMIB_IF_ROW2 Row) override;
+ DWORD getIpInterfaceEntry(PMIB_IPINTERFACE_ROW Row) override;
+
+ //
+ // Test utilities
+ //
+
+ void addAdapter(const MIB_IF_ROW2 &adapter);
+ void addIpInterface(const MIB_IF_ROW2 &adapter, const MIB_IPINTERFACE_ROW &iface);
+
+ void removeAdapter(const MIB_IF_ROW2 &adapter);
+ void removeIpInterface(const MIB_IPINTERFACE_ROW &iface);
+
+ void sendEvent(MIB_IPINTERFACE_ROW *hint, MIB_NOTIFICATION_TYPE updateType);
+};
diff --git a/windows/winnet/src/extras/tests/tests.vcxproj b/windows/winnet/src/extras/tests/tests.vcxproj
new file mode 100644
index 0000000000..ea65c53af2
--- /dev/null
+++ b/windows/winnet/src/extras/tests/tests.vcxproj
@@ -0,0 +1,183 @@
+<?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>{01A4E766-CC61-40B7-A3D6-7A37F6BF5CCB}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>tests</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
+ <ProjectSubType>NativeUnitTestProject</ProjectSubType>
+ <ProjectName>tests</ProjectName>
+ </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>
+ <UseOfMfc>false</UseOfMfc>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ <UseOfMfc>false</UseOfMfc>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ <UseOfMfc>false</UseOfMfc>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ <UseOfMfc>false</UseOfMfc>
+ </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>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>$(SolutionDir)..\windows-libraries\src;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <UseFullPaths>true</UseFullPaths>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration);$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libcommon.lib;Iphlpapi.lib;winnet-static.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>
+ <AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <UseFullPaths>true</UseFullPaths>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <UseFullPaths>true</UseFullPaths>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>$(SolutionDir)..\windows-libraries\src;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <UseFullPaths>true</UseFullPaths>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration);$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libcommon.lib;winnet.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" />
+ <ClInclude Include="testadapterutil.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <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="adaptermonitor.cpp" />
+ <ClCompile Include="testadapterutil.cpp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/windows/winnet/src/winnet/netmonitor.cpp b/windows/winnet/src/winnet/netmonitor.cpp
deleted file mode 100644
index bf6636fcd1..0000000000
--- a/windows/winnet/src/winnet/netmonitor.cpp
+++ /dev/null
@@ -1,394 +0,0 @@
-#include "stdafx.h"
-#include "netmonitor.h"
-#include <libcommon/error.h>
-#include <libcommon/memory.h>
-#include <libcommon/string.h>
-#include <sstream>
-
-namespace
-{
-
-bool ValidInterfaceType(const MIB_IF_ROW2 &iface)
-{
- switch (iface.InterfaceLuid.Info.IfType)
- {
- case IF_TYPE_SOFTWARE_LOOPBACK:
- case IF_TYPE_TUNNEL:
- {
- return false;
- }
- }
-
- //
- // (Windows 10, and possibly others.)
- // The BT adapter is erronously not marked as representing hardware.
- // By filtering on this we currently do not support BT tethering.
- //
-
- if (FALSE == iface.InterfaceAndOperStatusFlags.HardwareInterface
- || FALSE != iface.InterfaceAndOperStatusFlags.FilterInterface
- || 0 == iface.PhysicalAddressLength
- || FALSE != iface.InterfaceAndOperStatusFlags.EndPointInterface)
- {
- return false;
- }
-
- return true;
-}
-
-} // anonymous namespace
-
-NetMonitor::NetMonitor
-(
- std::shared_ptr<common::logging::ILogSink> logSink,
- NetMonitor::Notifier notifier,
- bool &currentConnectivity
-)
- : m_logSink(logSink)
- , m_notifier(notifier)
- , m_connected(false)
- , m_notificationHandle(nullptr)
-{
- m_cache = CreateCache();
- updateConnectivity();
-
- currentConnectivity = m_connected;
-
- const auto status = NotifyIpInterfaceChange(AF_UNSPEC, Callback, this, FALSE, &m_notificationHandle);
-
- THROW_UNLESS(NO_ERROR, status, "Register interface change notification");
-
- if (false == m_connected)
- {
- LogOfflineState(m_logSink);
- }
-}
-
-NetMonitor::~NetMonitor()
-{
- CancelMibChangeNotify2(m_notificationHandle);
-}
-
-// static
-bool NetMonitor::CheckConnectivity(std::shared_ptr<common::logging::ILogSink> logSink)
-{
- static bool loggedOffline = false;
-
- const auto connected = CheckConnectivity(CreateCache());
-
- if (connected)
- {
- loggedOffline = false;
- }
- else if (false == loggedOffline)
- {
- LogOfflineState(logSink);
- loggedOffline = true;
- }
-
- return connected;
-}
-
-// static
-NetMonitor::Cache NetMonitor::CreateCache()
-{
- MIB_IF_TABLE2 *table;
-
- const auto status = GetIfTable2(&table);
-
- THROW_UNLESS(NO_ERROR, status, "Acquire network interface table");
-
- common::memory::ScopeDestructor sd;
-
- sd += [table]()
- {
- FreeMibTable(table);
- };
-
- std::map<uint64_t, CacheEntry> cache;
-
- for (ULONG i = 0; i < table->NumEntries; ++i)
- {
- AddCacheEntry(cache, table->Table[i]);
- }
-
- return cache;
-}
-
-// static
-void NetMonitor::AddCacheEntry(Cache &cache, const MIB_IF_ROW2 &iface)
-{
- CacheEntry e;
-
- if (ValidInterfaceType(iface))
- {
- e.luid = iface.InterfaceLuid.Value;
- e.valid = true;
- e.connected =
- (
- NET_IF_ADMIN_STATUS_UP == iface.AdminStatus
- && IfOperStatusUp == iface.OperStatus
- && MediaConnectStateConnected == iface.MediaConnectState
- );
- }
- else
- {
- e.luid = iface.InterfaceLuid.Value;
- e.valid = false;
- e.connected = false;
- }
-
- cache.insert(std::make_pair(e.luid, e));
-}
-
-// static
-bool NetMonitor::CheckConnectivity(const Cache &cache)
-{
- for (const auto cacheEntryIter : cache)
- {
- const auto entry = cacheEntryIter.second;
-
- if (entry.valid && entry.connected)
- {
- return true;
- }
- }
-
- return false;
-}
-
-void NetMonitor::updateConnectivity()
-{
- m_connected = NetMonitor::CheckConnectivity(m_cache);
-}
-
-//static
-void __stdcall NetMonitor::Callback(void *context, MIB_IPINTERFACE_ROW *hint, MIB_NOTIFICATION_TYPE updateType)
-{
- auto nm = reinterpret_cast<NetMonitor *>(context);
-
- try
- {
- nm->callback(hint, updateType);
- }
- catch (const std::exception &err)
- {
- nm->m_logSink->error(err.what());
- }
- catch (...)
- {
- nm->m_logSink->error("Unspecified error in NetMonitor::Callback()");
- }
-}
-
-void NetMonitor::callback(MIB_IPINTERFACE_ROW *hint, MIB_NOTIFICATION_TYPE updateType)
-{
- std::scoped_lock<std::mutex> processingLock(m_processingMutex);
-
- switch (updateType)
- {
- case MibAddInstance:
- {
- MIB_IF_ROW2 iface = { 0 };
- iface.InterfaceLuid = hint->InterfaceLuid;
-
- const auto status = GetIfEntry2(&iface);
-
- if (NO_ERROR != status)
- {
- std::stringstream ss;
-
- ss << "GetIfEntry2() failed for LUID 0x" << std::hex << iface.InterfaceLuid.Value
- << " during processing of MibAddInstance, error: 0x" << status;
-
- throw std::runtime_error(ss.str());
- }
-
- //
- // The reason for removing an existing entry is that enabling
- // an interface on the adapter might change the overall properties in the
- // "row" which is merely an abstraction over all interfaces.
- //
-
- m_cache.erase(iface.InterfaceLuid.Value);
- AddCacheEntry(m_cache, iface);
-
- break;
- }
- case MibDeleteInstance:
- {
- m_cache.erase(hint->InterfaceLuid.Value);
-
- MIB_IF_ROW2 iface = { 0 };
- iface.InterfaceLuid = hint->InterfaceLuid;
-
- const auto status = GetIfEntry2(&iface);
-
- if (NO_ERROR == status)
- {
- AddCacheEntry(m_cache, iface);
- }
-
- break;
- }
- case MibParameterNotification:
- {
- MIB_IF_ROW2 iface = { 0 };
- iface.InterfaceLuid = hint->InterfaceLuid;
-
- const auto status = GetIfEntry2(&iface);
-
- if (NO_ERROR != status)
- {
- //
- // Only update the cache if we can look up the interface details.
- // This way, if the interface was connected and continues to be so, we don't
- // mistakenly switch the status to "offline".
- //
-
- std::stringstream ss;
-
- ss << "GetIfEntry2() failed for LUID 0x" << std::hex << iface.InterfaceLuid.Value
- << " during processing of MibParameterNotification, error: 0x" << status;
-
- throw std::runtime_error(ss.str());
- }
-
- m_cache.erase(iface.InterfaceLuid.Value);
- AddCacheEntry(m_cache, iface);
-
- break;
- }
- }
-
- const auto previousConnectivity = m_connected;
-
- updateConnectivity();
-
- if (previousConnectivity != m_connected)
- {
- m_notifier(m_connected);
-
- if (false == m_connected)
- {
- LogOfflineState(m_logSink);
- }
- }
-}
-
-//static
-void NetMonitor::LogOfflineState(std::shared_ptr<common::logging::ILogSink> logSink)
-{
- //
- // There is a race condition here because logging is not done using the
- // same data set that the online/offline logic processes.
- //
- // Not much of a problem really, this is temporary logging.
- //
-
- logSink->info("Machine is offline");
-
- MIB_IF_TABLE2 *table;
-
- const auto status = GetIfTable2(&table);
-
- if (NO_ERROR != status)
- {
- logSink->error("Failed to acquire list of network interfaces. Aborting detailed logging");
- return;
- }
-
- common::memory::ScopeDestructor sd;
-
- sd += [table]()
- {
- FreeMibTable(table);
- };
-
- logSink->info("Begin detailed listing of network interfaces");
-
- for (ULONG i = 0; i < table->NumEntries; ++i)
- {
- const auto &iface = table->Table[i];
-
- //
- // Don't flood the log with garbage.
- //
- const auto blacklist = std::vector<std::wstring>
- {
- L"WFP Native MAC Layer LightWeight Filter",
- L"QoS Packet Scheduler",
- L"WFP 802.3 MAC Layer LightWeight Filter",
- L"Microsoft Kernel Debug Network Adapter",
- L"Software Loopback Interface",
- L"Microsoft Teredo Tunneling Adapter",
- L"Microsoft IP-HTTPS Platform Adapter",
- L"Microsoft 6to4 Adapter",
- L"WAN Miniport",
- L"WiFi Filter Driver",
- L"Microsoft Wi-Fi Direct Virtual Adapter",
- };
-
- bool blacklisted = false;
-
- for (const auto &black : blacklist)
- {
- if (nullptr != wcsstr(iface.Description, black.c_str()))
- {
- blacklisted = true;
- break;
- }
- }
-
- if (blacklisted)
- {
- continue;
- }
-
- std::stringstream ss;
-
- ss << "Detailed interface logging" << std::endl;
- ss << "Interface ordinal " << i << std::endl;
-
- {
- const auto s = std::wstring(L" Alias: ").append(iface.Alias);
- ss << common::string::ToAnsi(s) << std::endl;
- }
-
- {
- const auto s = std::wstring(L" Description: ").append(iface.Description);
- ss << common::string::ToAnsi(s) << std::endl;
- }
-
- ss << " PhysicalAddressLength: " << iface.PhysicalAddressLength << std::endl;
- ss << " Type: " << iface.Type << std::endl;
- ss << " MediaType: " << iface.MediaType << std::endl;
- ss << " PhysicalMediumType: " << iface.PhysicalMediumType << std::endl;
- ss << " AccessType: " << iface.AccessType << std::endl;
-
- //
- // Bool cast prevents idiot stream from inserting literal 0/1.
- //
-
- ss << " InterfaceAndOperStatusFlags.HardwareInterface: " << (bool)iface.InterfaceAndOperStatusFlags.HardwareInterface << std::endl;
- ss << " InterfaceAndOperStatusFlags.FilterInterface: " << (bool)iface.InterfaceAndOperStatusFlags.FilterInterface << std::endl;
- ss << " InterfaceAndOperStatusFlags.ConnectorPresent: " << (bool)iface.InterfaceAndOperStatusFlags.ConnectorPresent << std::endl;
- ss << " InterfaceAndOperStatusFlags.NotAuthenticated: " << (bool)iface.InterfaceAndOperStatusFlags.NotAuthenticated << std::endl;
- ss << " InterfaceAndOperStatusFlags.NotMediaConnected: " << (bool)iface.InterfaceAndOperStatusFlags.NotMediaConnected << std::endl;
- ss << " InterfaceAndOperStatusFlags.Paused: " << (bool)iface.InterfaceAndOperStatusFlags.Paused << std::endl;
- ss << " InterfaceAndOperStatusFlags.LowPower: " << (bool)iface.InterfaceAndOperStatusFlags.LowPower << std::endl;
- ss << " InterfaceAndOperStatusFlags.EndPointInterface: " << (bool)iface.InterfaceAndOperStatusFlags.EndPointInterface << std::endl;
-
- ss << " OperStatus: " << iface.OperStatus << std::endl;
- ss << " AdminStatus: " << iface.AdminStatus << std::endl;
- ss << " MediaConnectState: " << iface.MediaConnectState << std::endl;
- ss << " TransmitLinkSpeed: " << iface.TransmitLinkSpeed << std::endl;
-
- ss << " ReceiveLinkSpeed: " << iface.ReceiveLinkSpeed << std::endl;
- ss << " InUcastPkts:" << iface.InUcastPkts;
-
- logSink->info(ss.str().c_str());
- }
-
- logSink->info("End detailed listing of network interfaces");
-}
diff --git a/windows/winnet/src/winnet/netmonitor.h b/windows/winnet/src/winnet/netmonitor.h
deleted file mode 100644
index a3e0a28281..0000000000
--- a/windows/winnet/src/winnet/netmonitor.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#pragma once
-
-#include <libcommon/logging/ilogsink.h>
-#include <memory>
-#include <map>
-#include <string>
-#include <cstdint>
-#include <mutex>
-#include <functional>
-#include <winsock2.h>
-#include <ws2ipdef.h>
-#include <iphlpapi.h>
-#include <windows.h>
-
-class NetMonitor
-{
-public:
-
- //
- // Connectivity changed.
- // true = connected, false = disconnected.
- //
- using Notifier = std::function<void(bool)>;
-
- NetMonitor(std::shared_ptr<common::logging::ILogSink> logSink, Notifier notifier, bool &currentConnectivity);
- ~NetMonitor();
-
- static bool CheckConnectivity(std::shared_ptr<common::logging::ILogSink> logSink);
-
-private:
-
- std::shared_ptr<common::logging::ILogSink> m_logSink;
- Notifier m_notifier;
-
- struct CacheEntry
- {
- // Unique interface identifier.
- uint64_t luid;
-
- // Whether this is a physical adapter or not.
- bool valid;
-
- // Last known state.
- bool connected;
- };
-
- using Cache = std::map<uint64_t, CacheEntry>;
-
- std::mutex m_processingMutex;
- Cache m_cache;
- bool m_connected;
-
- HANDLE m_notificationHandle;
-
- static Cache CreateCache();
- static void AddCacheEntry(Cache &cache, const MIB_IF_ROW2 &iface);
- static bool CheckConnectivity(const Cache &cache);
-
- void updateConnectivity();
-
- static void __stdcall Callback(void *context, MIB_IPINTERFACE_ROW *hint, MIB_NOTIFICATION_TYPE updateType);
- void callback(MIB_IPINTERFACE_ROW *hint, MIB_NOTIFICATION_TYPE updateType);
-
- static void LogOfflineState(std::shared_ptr<common::logging::ILogSink> logSink);
-};
diff --git a/windows/winnet/src/winnet/networkadaptermonitor.cpp b/windows/winnet/src/winnet/networkadaptermonitor.cpp
new file mode 100644
index 0000000000..77ce292501
--- /dev/null
+++ b/windows/winnet/src/winnet/networkadaptermonitor.cpp
@@ -0,0 +1,330 @@
+#include "stdafx.h"
+
+#include "networkadaptermonitor.h"
+#include <libcommon/memory.h>
+#include <sstream>
+#include <cstring>
+
+using namespace std::placeholders;
+
+
+NetworkAdapterMonitor::NetworkAdapterMonitor(
+ std::shared_ptr<common::logging::ILogSink> logSink,
+ UpdateSinkType updateSink,
+ FilterType filter,
+ std::shared_ptr<IDataProvider> dataProvider
+)
+ : m_logSink(logSink)
+ , m_notificationHandle(nullptr)
+ , m_updateSink(updateSink)
+ , m_filter(filter)
+ , m_dataProvider(dataProvider)
+{
+ //
+ // Initialize adapters
+ //
+
+ MIB_IF_TABLE2 *table;
+
+ const auto status = m_dataProvider->getIfTable2(&table);
+
+ THROW_UNLESS(NO_ERROR, status, "Acquire network interface table");
+
+ common::memory::ScopeDestructor sd;
+
+ sd += [this, table]()
+ {
+ m_dataProvider->freeMibTable(table);
+ };
+
+ for (ULONG i = 0; i < table->NumEntries; ++i)
+ {
+ m_adapters[table->Table[i].InterfaceLuid.Value] = table->Table[i];
+
+ if (filter(table->Table[i]))
+ {
+ m_filteredAdapters.push_back(table->Table[i]);
+ }
+ }
+
+ //
+ // Send initial notification
+ //
+
+ if (m_filteredAdapters.empty())
+ {
+ m_updateSink(m_filteredAdapters, nullptr, UpdateType::Update);
+ }
+ else
+ {
+ m_updateSink(m_filteredAdapters, nullptr, UpdateType::Add);
+ }
+
+ //
+ // Listen to adapter events
+ //
+
+ const auto statusCb = m_dataProvider->notifyIpInterfaceChange(
+ AF_UNSPEC,
+ Callback,
+ this,
+ FALSE,
+ &m_notificationHandle
+ );
+
+ THROW_UNLESS(NO_ERROR, statusCb, "Register interface change notification");
+}
+
+NetworkAdapterMonitor::NetworkAdapterMonitor(
+ std::shared_ptr<common::logging::ILogSink> logSink
+ , UpdateSinkType updateSink
+ , FilterType filter
+) : NetworkAdapterMonitor(logSink, updateSink, filter, std::make_shared<SystemDataProvider>())
+{
+}
+
+NetworkAdapterMonitor::~NetworkAdapterMonitor()
+{
+ if (nullptr != m_notificationHandle)
+ {
+ m_dataProvider->cancelMibChangeNotify2(m_notificationHandle);
+ m_notificationHandle = nullptr;
+ }
+}
+
+bool NetworkAdapterMonitor::hasIPv4Interface(NET_LUID luid) const
+{
+ MIB_IPINTERFACE_ROW iprow = { 0 };
+ iprow.InterfaceLuid = luid;
+ iprow.Family = AF_INET;
+
+ const auto status = m_dataProvider->getIpInterfaceEntry(&iprow);
+
+ if (NO_ERROR == status)
+ {
+ return true;
+ }
+ else if (ERROR_NOT_FOUND != status)
+ {
+ common::error::Throw("Resolve IPv4 interface", status);
+ }
+ return false;
+}
+
+bool NetworkAdapterMonitor::hasIPv6Interface(NET_LUID luid) const
+{
+ MIB_IPINTERFACE_ROW iprow = { 0 };
+ iprow.InterfaceLuid = luid;
+ iprow.Family = AF_INET6;
+
+ const auto status = m_dataProvider->getIpInterfaceEntry(&iprow);
+
+ if (NO_ERROR == status)
+ {
+ return true;
+ }
+ else if (ERROR_NOT_FOUND != status)
+ {
+ common::error::Throw("Resolve IPv6 interface", status);
+ }
+ return false;
+}
+
+std::vector<MIB_IF_ROW2>::iterator NetworkAdapterMonitor::findFilteredAdapter(const MIB_IF_ROW2 &adapter)
+{
+ return std::find_if(m_filteredAdapters.begin(), m_filteredAdapters.end(), [&adapter](const MIB_IF_ROW2 &elem)
+ {
+ return elem.InterfaceLuid.Value == adapter.InterfaceLuid.Value;
+ });
+}
+
+MIB_IF_ROW2 NetworkAdapterMonitor::getAdapter(NET_LUID luid) const
+{
+ MIB_IF_ROW2 rowOut;
+ rowOut.InterfaceLuid = luid;
+ const auto status = m_dataProvider->getIfEntry2(&rowOut);
+
+ if (NO_ERROR == status)
+ {
+ return rowOut;
+ }
+
+ std::stringstream ss;
+
+ ss << "GetIfEntry2() failed for LUID 0x" << std::hex << rowOut.InterfaceLuid.Value
+ << " in NetworkAdapterMonitor::getAdapter(), error: 0x" << status;
+
+ throw std::runtime_error(ss.str());
+}
+
+void NetworkAdapterMonitor::callback(const MIB_IPINTERFACE_ROW *hint, MIB_NOTIFICATION_TYPE)
+{
+ MIB_IF_ROW2 iface = getAdapter(hint->InterfaceLuid);
+
+ bool adapterEnabled = NET_IF_ADMIN_STATUS_UP == iface.AdminStatus
+ && (hasIPv4Interface(iface.InterfaceLuid)
+ || hasIPv6Interface(iface.InterfaceLuid));
+
+ const auto adapterIt = m_adapters.find(hint->InterfaceLuid.Value);
+
+ if (adapterEnabled)
+ {
+ //
+ // Check if the adapter has been added or updated
+ //
+
+ bool fieldsChanged;
+
+ if (m_adapters.end() == adapterIt)
+ {
+ const auto pair = m_adapters.emplace(
+ iface.InterfaceLuid.Value,
+ iface
+ );
+ fieldsChanged = true;
+ }
+ else
+ {
+ //
+ // Only send an Update event if the fields have changed
+ //
+ fieldsChanged = std::memcmp(
+ &adapterIt->second,
+ &iface,
+ sizeof(MIB_IF_ROW2)
+ ) != 0;
+
+ // update stored adapter
+ adapterIt->second = iface;
+ }
+
+ if (m_filter(iface))
+ {
+ //
+ // Report Add event if this is new
+ //
+ if (m_filteredAdapters.end() == findFilteredAdapter(iface))
+ {
+ m_filteredAdapters.push_back(iface);
+ m_updateSink(m_filteredAdapters, &iface, UpdateType::Add);
+ }
+ else if (fieldsChanged)
+ {
+ m_updateSink(m_filteredAdapters, &iface, UpdateType::Update);
+ }
+ }
+ else
+ {
+ //
+ // Synthesize a Delete event if we're no longer interested
+ // in this adapter
+ //
+ const auto filteredIt = findFilteredAdapter(iface);
+
+ if (m_filteredAdapters.end() != filteredIt)
+ {
+ m_filteredAdapters.erase(filteredIt);
+ m_updateSink(
+ m_filteredAdapters,
+ &iface,
+ UpdateType::Delete
+ );
+ }
+ }
+ }
+ else
+ {
+ if (m_adapters.end() == adapterIt)
+ {
+ return;
+ }
+
+ //
+ // Remove the adapter
+ //
+
+ m_adapters.erase(adapterIt);
+
+ const auto filteredIt = findFilteredAdapter(iface);
+
+ if (m_filteredAdapters.end() != filteredIt)
+ {
+ m_filteredAdapters.erase(filteredIt);
+
+ //
+ // We report 'Delete' for any adapter that was
+ // approved by the filter when reported.
+ //
+ m_updateSink(
+ m_filteredAdapters,
+ &iface,
+ UpdateType::Delete
+ );
+ }
+ }
+}
+
+//static
+void __stdcall NetworkAdapterMonitor::Callback(void *context, MIB_IPINTERFACE_ROW *hint, MIB_NOTIFICATION_TYPE updateType)
+{
+ auto inst = reinterpret_cast<NetworkAdapterMonitor *>(context);
+
+ try
+ {
+ inst->callback(hint, updateType);
+ }
+ catch (const std::exception &err)
+ {
+ inst->m_logSink->error(err.what());
+ }
+ catch (...)
+ {
+ inst->m_logSink->error("Unspecified error in NetworkAdapterMonitor::Callback()");
+ }
+}
+
+//
+// SystemDataProvider
+//
+
+DWORD NetworkAdapterMonitor::SystemDataProvider::notifyIpInterfaceChange(
+ ADDRESS_FAMILY Family,
+ PIPINTERFACE_CHANGE_CALLBACK Callback,
+ PVOID CallerContext,
+ BOOLEAN InitialNotification,
+ HANDLE *NotificationHandle
+)
+{
+ return NotifyIpInterfaceChange(
+ Family,
+ Callback,
+ CallerContext,
+ InitialNotification,
+ NotificationHandle
+ );
+}
+
+DWORD NetworkAdapterMonitor::SystemDataProvider::cancelMibChangeNotify2(HANDLE NotificationHandle)
+{
+ return CancelMibChangeNotify2(NotificationHandle);
+}
+
+DWORD NetworkAdapterMonitor::SystemDataProvider::getIfEntry2(PMIB_IF_ROW2 Row)
+{
+ return GetIfEntry2(Row);
+}
+
+DWORD NetworkAdapterMonitor::SystemDataProvider::getIfTable2(PMIB_IF_TABLE2 *Table)
+{
+ return GetIfTable2(Table);
+}
+
+DWORD NetworkAdapterMonitor::SystemDataProvider::getIpInterfaceEntry(PMIB_IPINTERFACE_ROW Row)
+{
+ return GetIpInterfaceEntry(Row);
+}
+
+void NetworkAdapterMonitor::SystemDataProvider::freeMibTable(PVOID Memory)
+{
+ FreeMibTable(Memory);
+}
diff --git a/windows/winnet/src/winnet/networkadaptermonitor.h b/windows/winnet/src/winnet/networkadaptermonitor.h
new file mode 100644
index 0000000000..fe78bb7e2d
--- /dev/null
+++ b/windows/winnet/src/winnet/networkadaptermonitor.h
@@ -0,0 +1,126 @@
+#pragma once
+
+#include <libcommon/logging/ilogsink.h>
+#include <libcommon/error.h>
+#include <map>
+#include <winsock2.h>
+#include <ws2ipdef.h>
+#include <iphlpapi.h>
+#include <windows.h>
+#include <functional>
+#include <vector>
+
+
+class NetworkAdapterMonitor
+{
+public:
+
+ enum class UpdateType
+ {
+ Add,
+ Delete,
+ Update
+ };
+
+ using FilterType = std::function<bool(const MIB_IF_ROW2 &adapter)>;
+
+ //
+ // An event may apply to a specific adapter, or it may apply to all adapters.
+ // In the latter case, 'adapter' will be set to nullptr.
+ //
+ using UpdateSinkType = std::function<void(const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, UpdateType updateType)>;
+
+ struct IDataProvider;
+ class SystemDataProvider;
+
+ NetworkAdapterMonitor(
+ std::shared_ptr<common::logging::ILogSink> logSink
+ , UpdateSinkType updateSink
+ , FilterType filter
+ , std::shared_ptr<IDataProvider> dataProvider
+ );
+ NetworkAdapterMonitor(
+ std::shared_ptr<common::logging::ILogSink> logSink
+ , UpdateSinkType updateSink
+ , FilterType filter
+ );
+ ~NetworkAdapterMonitor();
+
+ NetworkAdapterMonitor(const NetworkAdapterMonitor &) = delete;
+ NetworkAdapterMonitor& operator=(const NetworkAdapterMonitor &) = delete;
+ NetworkAdapterMonitor(NetworkAdapterMonitor &&) = delete;
+ NetworkAdapterMonitor& operator=(NetworkAdapterMonitor &&) = delete;
+
+private:
+
+ std::shared_ptr<common::logging::ILogSink> m_logSink;
+ UpdateSinkType m_updateSink;
+ FilterType m_filter;
+
+ std::shared_ptr<IDataProvider> m_dataProvider;
+
+ MIB_IF_ROW2 getAdapter(NET_LUID luid) const;
+
+ bool hasIPv4Interface(NET_LUID luid) const;
+ bool hasIPv6Interface(NET_LUID luid) const;
+
+ std::map<ULONG64, MIB_IF_ROW2> m_adapters;
+ std::vector<MIB_IF_ROW2> m_filteredAdapters;
+
+ std::vector<MIB_IF_ROW2>::iterator findFilteredAdapter(const MIB_IF_ROW2 &adapter);
+
+ HANDLE m_notificationHandle;
+ static void __stdcall Callback(void *context, MIB_IPINTERFACE_ROW *hint, MIB_NOTIFICATION_TYPE updateType);
+ virtual void callback(const MIB_IPINTERFACE_ROW *hint, MIB_NOTIFICATION_TYPE updateType);
+};
+
+
+struct NetworkAdapterMonitor::IDataProvider
+{
+ virtual ~IDataProvider() = 0
+ {
+ }
+
+ virtual DWORD notifyIpInterfaceChange(
+ ADDRESS_FAMILY Family,
+ PIPINTERFACE_CHANGE_CALLBACK Callback,
+ PVOID CallerContext,
+ BOOLEAN InitialNotification,
+ HANDLE *NotificationHandle
+ ) = 0;
+ virtual DWORD cancelMibChangeNotify2(HANDLE NotificationHandle) = 0;
+
+ virtual DWORD getIfTable2(PMIB_IF_TABLE2 *Table) = 0;
+ virtual void freeMibTable(PVOID Memory) = 0;
+
+ virtual DWORD getIfEntry2(PMIB_IF_ROW2 Row) = 0;
+ virtual DWORD getIpInterfaceEntry(PMIB_IPINTERFACE_ROW Row) = 0;
+};
+
+class NetworkAdapterMonitor::SystemDataProvider : public IDataProvider
+{
+public:
+
+ SystemDataProvider() = default;
+ virtual ~SystemDataProvider() = default;
+
+ SystemDataProvider(const SystemDataProvider&) = delete;
+ SystemDataProvider(SystemDataProvider&&) = delete;
+ SystemDataProvider& operator=(const SystemDataProvider&) = delete;
+ SystemDataProvider& operator=(const SystemDataProvider&&) = delete;
+
+ DWORD notifyIpInterfaceChange(
+ ADDRESS_FAMILY Family,
+ PIPINTERFACE_CHANGE_CALLBACK Callback,
+ PVOID CallerContext,
+ BOOLEAN InitialNotification,
+ HANDLE *NotificationHandle
+ ) override;
+ DWORD cancelMibChangeNotify2(HANDLE NotificationHandle) override;
+
+ DWORD getIfTable2(PMIB_IF_TABLE2 *Table) override;
+ void freeMibTable(PVOID Memory) override;
+
+ DWORD getIfEntry2(PMIB_IF_ROW2 Row) override;
+ DWORD getIpInterfaceEntry(PMIB_IPINTERFACE_ROW Row) override;
+};
diff --git a/windows/winnet/src/winnet/offlinemonitor.cpp b/windows/winnet/src/winnet/offlinemonitor.cpp
new file mode 100644
index 0000000000..4f93a01def
--- /dev/null
+++ b/windows/winnet/src/winnet/offlinemonitor.cpp
@@ -0,0 +1,207 @@
+#include "stdafx.h"
+#include "offlinemonitor.h"
+#include <libcommon/error.h>
+#include <libcommon/memory.h>
+#include <libcommon/string.h>
+#include <sstream>
+
+
+using namespace std::placeholders; // for _1, _2 etc.
+
+namespace
+{
+
+bool IsConnectedAdapter(const MIB_IF_ROW2 &iface)
+{
+ switch (iface.InterfaceLuid.Info.IfType)
+ {
+ case IF_TYPE_SOFTWARE_LOOPBACK:
+ case IF_TYPE_TUNNEL:
+ {
+ return false;
+ }
+ }
+
+ //
+ // (Windows 10, and possibly others.)
+ // The BT adapter is erronously not marked as representing hardware.
+ // By filtering on this we currently do not support BT tethering.
+ //
+
+ if (FALSE == iface.InterfaceAndOperStatusFlags.HardwareInterface
+ || FALSE != iface.InterfaceAndOperStatusFlags.FilterInterface
+ || 0 == iface.PhysicalAddressLength
+ || FALSE != iface.InterfaceAndOperStatusFlags.EndPointInterface)
+ {
+ return false;
+ }
+
+ bool connected = (
+ IfOperStatusUp == iface.OperStatus
+ && MediaConnectStateConnected == iface.MediaConnectState
+ );
+ return connected;
+}
+
+} // anonymous namespace
+
+
+OfflineMonitor::OfflineMonitor
+(
+ std::shared_ptr<common::logging::ILogSink> logSink,
+ Notifier notifier,
+ std::shared_ptr<NetworkAdapterMonitor::IDataProvider> dataProvider
+)
+ : m_logSink(logSink)
+ , m_notifier(notifier)
+ , m_connected(false)
+ , m_netAdapterMonitor(
+ m_logSink,
+ std::bind(&OfflineMonitor::callback, this, _1, _2, _3),
+ IsConnectedAdapter,
+ dataProvider
+ )
+{
+}
+
+
+OfflineMonitor::OfflineMonitor
+(
+ std::shared_ptr<common::logging::ILogSink> logSink,
+ Notifier notifier
+) : OfflineMonitor(logSink, notifier, std::make_shared<NetworkAdapterMonitor::SystemDataProvider>())
+{
+}
+
+
+void OfflineMonitor::callback(const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *, NetworkAdapterMonitor::UpdateType)
+{
+ const auto previousConnectivity = m_connected;
+ m_connected = !adapters.empty();
+
+ if (previousConnectivity != m_connected)
+ {
+ m_notifier(m_connected);
+
+ if (false == m_connected)
+ {
+ LogOfflineState();
+ }
+ }
+}
+
+void OfflineMonitor::LogOfflineState()
+{
+ //
+ // There is a race condition here because logging is not done using the
+ // same data set that the online/offline logic processes.
+ //
+ // Not much of a problem really, this is temporary logging.
+ //
+
+ m_logSink->info("Machine is offline");
+
+ MIB_IF_TABLE2 *table;
+
+ const auto status = GetIfTable2(&table);
+
+ if (NO_ERROR != status)
+ {
+ m_logSink->error("Failed to acquire list of network interfaces. Aborting detailed logging");
+ return;
+ }
+
+ common::memory::ScopeDestructor sd;
+
+ sd += [table]()
+ {
+ FreeMibTable(table);
+ };
+
+ m_logSink->info("Begin detailed listing of network interfaces");
+
+ for (ULONG i = 0; i < table->NumEntries; ++i)
+ {
+ const auto &iface = table->Table[i];
+
+ //
+ // Don't flood the log with garbage.
+ //
+ const auto blacklist = std::vector<std::wstring>
+ {
+ L"WFP Native MAC Layer LightWeight Filter",
+ L"QoS Packet Scheduler",
+ L"WFP 802.3 MAC Layer LightWeight Filter",
+ L"Microsoft Kernel Debug Network Adapter",
+ L"Software Loopback Interface",
+ L"Microsoft Teredo Tunneling Adapter",
+ L"Microsoft IP-HTTPS Platform Adapter",
+ L"Microsoft 6to4 Adapter",
+ L"WAN Miniport",
+ L"WiFi Filter Driver",
+ L"Microsoft Wi-Fi Direct Virtual Adapter",
+ };
+
+ bool blacklisted = false;
+
+ for (const auto &black : blacklist)
+ {
+ if (nullptr != wcsstr(iface.Description, black.c_str()))
+ {
+ blacklisted = true;
+ break;
+ }
+ }
+
+ if (blacklisted)
+ {
+ continue;
+ }
+
+ std::stringstream ss;
+
+ ss << "Detailed interface logging" << std::endl;
+ ss << "Interface ordinal " << i << std::endl;
+
+ {
+ const auto s = std::wstring(L" Alias: ").append(iface.Alias);
+ ss << common::string::ToAnsi(s) << std::endl;
+ }
+
+ {
+ const auto s = std::wstring(L" Description: ").append(iface.Description);
+ ss << common::string::ToAnsi(s) << std::endl;
+ }
+
+ ss << " PhysicalAddressLength: " << iface.PhysicalAddressLength << std::endl;
+ ss << " Type: " << iface.Type << std::endl;
+ ss << " MediaType: " << iface.MediaType << std::endl;
+ ss << " PhysicalMediumType: " << iface.PhysicalMediumType << std::endl;
+ ss << " AccessType: " << iface.AccessType << std::endl;
+
+ //
+ // Bool cast prevents idiot stream from inserting literal 0/1.
+ //
+
+ ss << " InterfaceAndOperStatusFlags.HardwareInterface: " << (bool)iface.InterfaceAndOperStatusFlags.HardwareInterface << std::endl;
+ ss << " InterfaceAndOperStatusFlags.FilterInterface: " << (bool)iface.InterfaceAndOperStatusFlags.FilterInterface << std::endl;
+ ss << " InterfaceAndOperStatusFlags.ConnectorPresent: " << (bool)iface.InterfaceAndOperStatusFlags.ConnectorPresent << std::endl;
+ ss << " InterfaceAndOperStatusFlags.NotAuthenticated: " << (bool)iface.InterfaceAndOperStatusFlags.NotAuthenticated << std::endl;
+ ss << " InterfaceAndOperStatusFlags.NotMediaConnected: " << (bool)iface.InterfaceAndOperStatusFlags.NotMediaConnected << std::endl;
+ ss << " InterfaceAndOperStatusFlags.Paused: " << (bool)iface.InterfaceAndOperStatusFlags.Paused << std::endl;
+ ss << " InterfaceAndOperStatusFlags.LowPower: " << (bool)iface.InterfaceAndOperStatusFlags.LowPower << std::endl;
+ ss << " InterfaceAndOperStatusFlags.EndPointInterface: " << (bool)iface.InterfaceAndOperStatusFlags.EndPointInterface << std::endl;
+
+ ss << " OperStatus: " << iface.OperStatus << std::endl;
+ ss << " AdminStatus: " << iface.AdminStatus << std::endl;
+ ss << " MediaConnectState: " << iface.MediaConnectState << std::endl;
+ ss << " TransmitLinkSpeed: " << iface.TransmitLinkSpeed << std::endl;
+
+ ss << " ReceiveLinkSpeed: " << iface.ReceiveLinkSpeed << std::endl;
+ ss << " InUcastPkts:" << iface.InUcastPkts;
+
+ m_logSink->info(ss.str().c_str());
+ }
+
+ m_logSink->info("End detailed listing of network interfaces");
+}
diff --git a/windows/winnet/src/winnet/offlinemonitor.h b/windows/winnet/src/winnet/offlinemonitor.h
new file mode 100644
index 0000000000..230fef48d7
--- /dev/null
+++ b/windows/winnet/src/winnet/offlinemonitor.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <libcommon/logging/ilogsink.h>
+#include <mutex>
+#include "networkadaptermonitor.h"
+
+class OfflineMonitor
+{
+public:
+
+ //
+ // Connectivity changed.
+ // true = connected, false = disconnected.
+ //
+ using Notifier = std::function<void(bool)>;
+
+ OfflineMonitor(
+ std::shared_ptr<common::logging::ILogSink> logSink,
+ Notifier notifier,
+ std::shared_ptr<NetworkAdapterMonitor::IDataProvider> dataProvider
+ );
+ OfflineMonitor(std::shared_ptr<common::logging::ILogSink> logSink, Notifier notifier);
+
+private:
+
+ std::shared_ptr<common::logging::ILogSink> m_logSink;
+ Notifier m_notifier;
+
+ bool m_connected;
+ NetworkAdapterMonitor m_netAdapterMonitor;
+
+ void LogOfflineState();
+
+ void callback(const std::vector<MIB_IF_ROW2> &adapters, const MIB_IF_ROW2 *adapter, NetworkAdapterMonitor::UpdateType type);
+};
diff --git a/windows/winnet/src/winnet/winnet.cpp b/windows/winnet/src/winnet/winnet.cpp
index 329002d4f9..4b006964a6 100644
--- a/windows/winnet/src/winnet/winnet.cpp
+++ b/windows/winnet/src/winnet/winnet.cpp
@@ -2,7 +2,7 @@
#include "winnet.h"
#include "NetworkInterfaces.h"
#include "interfaceutils.h"
-#include "netmonitor.h"
+#include "offlinemonitor.h"
#include "../../shared/logsinkadapter.h"
#include <libcommon/error.h>
#include <cstdint>
@@ -12,7 +12,7 @@
namespace
{
-NetMonitor *g_NetMonitor = nullptr;
+OfflineMonitor *g_OfflineMonitor = nullptr;
void UnwindAndLog(MullvadLogSink logSink, void *logSinkContext, const std::exception &err)
{
@@ -152,14 +152,13 @@ WINNET_API
WinNet_ActivateConnectivityMonitor(
WinNetConnectivityMonitorCallback callback,
void *callbackContext,
- bool *currentConnectivity,
MullvadLogSink logSink,
void *logSinkContext
)
{
try
{
- if (nullptr != g_NetMonitor)
+ if (nullptr != g_OfflineMonitor)
{
throw std::runtime_error("Cannot activate connectivity monitor twice");
}
@@ -169,16 +168,9 @@ WinNet_ActivateConnectivityMonitor(
callback(connected, callbackContext);
};
- bool connected = false;
-
auto logger = std::make_shared<shared::LogSinkAdapter>(logSink, logSinkContext);
- g_NetMonitor = new NetMonitor(logger, forwarder, connected);
-
- if (nullptr != currentConnectivity)
- {
- *currentConnectivity = connected;
- }
+ g_OfflineMonitor = new OfflineMonitor(logger, forwarder);
return true;
}
@@ -202,8 +194,8 @@ WinNet_DeactivateConnectivityMonitor(
{
try
{
- delete g_NetMonitor;
- g_NetMonitor = nullptr;
+ delete g_OfflineMonitor;
+ g_OfflineMonitor = nullptr;
}
catch (...)
{
diff --git a/windows/winnet/src/winnet/winnet.h b/windows/winnet/src/winnet/winnet.h
index c56faad951..9b1af52e36 100644
--- a/windows/winnet/src/winnet/winnet.h
+++ b/windows/winnet/src/winnet/winnet.h
@@ -3,11 +3,15 @@
#include "../../shared/logsink.h"
#include <stdbool.h>
+#ifndef WINNET_STATIC
#ifdef WINNET_EXPORTS
#define WINNET_LINKAGE __declspec(dllexport)
#else
#define WINNET_LINKAGE __declspec(dllimport)
#endif
+#else
+#define WINNET_LINKAGE
+#endif
#define WINNET_API __stdcall
@@ -75,7 +79,6 @@ WINNET_API
WinNet_ActivateConnectivityMonitor(
WinNetConnectivityMonitorCallback callback,
void *callbackContext,
- bool *currentConnectivity,
MullvadLogSink logSink,
void *logSinkContext
);
diff --git a/windows/winnet/src/winnet/winnet.vcxproj b/windows/winnet/src/winnet/winnet.vcxproj
index fd10ec40fd..141c992c4e 100644
--- a/windows/winnet/src/winnet/winnet.vcxproj
+++ b/windows/winnet/src/winnet/winnet.vcxproj
@@ -1,6 +1,14 @@
<?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 Static|Win32">
+ <Configuration>Debug Static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug Static|x64">
+ <Configuration>Debug Static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
@@ -19,18 +27,20 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
+ <ClCompile Include="networkadaptermonitor.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="InterfacePair.cpp" />
<ClCompile Include="interfaceutils.cpp" />
- <ClCompile Include="netmonitor.cpp" />
+ <ClCompile Include="offlinemonitor.cpp" />
<ClCompile Include="NetworkInterfaces.cpp" />
<ClCompile Include="stdafx.cpp" />
<ClCompile Include="winnet.cpp" />
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="networkadaptermonitor.h" />
<ClInclude Include="InterfacePair.h" />
<ClInclude Include="interfaceutils.h" />
- <ClInclude Include="netmonitor.h" />
+ <ClInclude Include="offlinemonitor.h" />
<ClInclude Include="NetworkInterfaces.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
@@ -56,6 +66,12 @@
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</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>
@@ -69,6 +85,12 @@
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</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>
@@ -84,12 +106,18 @@
<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 Condition="'$(Configuration)|$(Platform)'=='Debug Static|Win32'" Label="PropertySheets">
+ <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 Condition="'$(Configuration)|$(Platform)'=='Debug Static|x64'" Label="PropertySheets">
+ <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>
@@ -99,11 +127,23 @@
<OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
</PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)\bin\$(Platform)-Debug\</OutDir>
+ <IntDir>$(SolutionDir)\bin\temp\$(Platform)-Debug\$(ProjectName)\</IntDir>
+ <TargetName>$(ProjectName)-static</TargetName>
+ </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
</PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)\bin\$(Platform)-Debug\</OutDir>
+ <IntDir>$(SolutionDir)\bin\temp\$(Platform)-Debug\$(ProjectName)\</IntDir>
+ <TargetName>$(ProjectName)-static</TargetName>
+ </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir>
@@ -134,6 +174,30 @@
<AdditionalDependencies>libcommon.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Create</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WINNET_STATIC;WIN32;_DEBUG;WINNET_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\..\..\windows-libraries\src\%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ModuleDefinitionFile>winnet.def</ModuleDefinitionFile>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libcommon.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <Lib>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-Debug</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libcommon.lib;Iphlpapi.lib</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Create</PrecompiledHeader>
@@ -154,6 +218,30 @@
<ModuleDefinitionFile>winnet.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Static|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Create</PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WINNET_STATIC;_DEBUG;WINNET_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\..\..\windows-libraries\src\%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>libcommon.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories>
+ <ModuleDefinitionFile>winnet.def</ModuleDefinitionFile>
+ </Link>
+ <Lib>
+ <AdditionalDependencies>libcommon.lib;Iphlpapi.lib</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-Debug</AdditionalLibraryDirectories>
+ </Lib>
+ </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Create</PrecompiledHeader>
diff --git a/windows/winnet/src/winnet/winnet.vcxproj.filters b/windows/winnet/src/winnet/winnet.vcxproj.filters
index 3ac5a14717..9a901d3203 100644
--- a/windows/winnet/src/winnet/winnet.vcxproj.filters
+++ b/windows/winnet/src/winnet/winnet.vcxproj.filters
@@ -7,7 +7,8 @@
<ClCompile Include="NetworkInterfaces.cpp" />
<ClCompile Include="InterfacePair.cpp" />
<ClCompile Include="interfaceutils.cpp" />
- <ClCompile Include="netmonitor.cpp" />
+ <ClCompile Include="networkadaptermonitor.cpp" />
+ <ClCompile Include="offlinemonitor.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@@ -16,7 +17,8 @@
<ClInclude Include="NetworkInterfaces.h" />
<ClInclude Include="InterfacePair.h" />
<ClInclude Include="interfaceutils.h" />
- <ClInclude Include="netmonitor.h" />
+ <ClInclude Include="networkadaptermonitor.h" />
+ <ClInclude Include="offlinemonitor.h" />
</ItemGroup>
<ItemGroup>
<None Include="winnet.def" />
diff --git a/windows/winnet/winnet.sln b/windows/winnet/winnet.sln
index 8d95777b4b..a592bed402 100644
--- a/windows/winnet/winnet.sln
+++ b/windows/winnet/winnet.sln
@@ -12,12 +12,18 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcommon", "..\windows-lib
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug Static|x64 = Debug Static|x64
+ Debug Static|x86 = Debug Static|x86
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug Static|x64.ActiveCfg = Debug Static|x64
+ {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug Static|x64.Build.0 = Debug Static|x64
+ {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug Static|x86.ActiveCfg = Debug Static|Win32
+ {89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug Static|x86.Build.0 = Debug Static|Win32
{89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x64.ActiveCfg = Debug|x64
{89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x64.Build.0 = Debug|x64
{89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Debug|x86.ActiveCfg = Debug|Win32
@@ -26,6 +32,10 @@ Global
{89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Release|x64.Build.0 = Release|x64
{89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Release|x86.ActiveCfg = Release|Win32
{89C5CDE8-04DB-4D9C-A8D8-7F786DAFB6D4}.Release|x86.Build.0 = Release|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug Static|x64.ActiveCfg = Debug|x64
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug Static|x64.Build.0 = Debug|x64
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug Static|x86.ActiveCfg = Debug|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug Static|x86.Build.0 = Debug|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