summaryrefslogtreecommitdiffhomepage
path: root/windows
diff options
context:
space:
mode:
authorEmīls Piņķis <emils@mullvad.net>2019-05-21 11:14:32 +0100
committerEmīls Piņķis <emils@mullvad.net>2019-05-21 11:14:32 +0100
commitb0ca9c2fc0d59be5d3a5612dadd85fc2d95e8867 (patch)
treec9b4b2026319513b4bc1f52f5f30b3cbf6be8e3b /windows
parent4073b27046c34e86c1117bbf70f5305e76c52a22 (diff)
parentde596ee1afe9818dacdf1f10966ba398338add16 (diff)
downloadmullvadvpn-b0ca9c2fc0d59be5d3a5612dadd85fc2d95e8867.tar.xz
mullvadvpn-b0ca9c2fc0d59be5d3a5612dadd85fc2d95e8867.zip
Merge branch 'win-detect-net'
Diffstat (limited to 'windows')
-rw-r--r--windows/winnet/src/extras/loader/loader.cpp44
-rw-r--r--windows/winnet/src/winnet/netmonitor.cpp217
-rw-r--r--windows/winnet/src/winnet/netmonitor.h58
-rw-r--r--windows/winnet/src/winnet/winnet.cpp117
-rw-r--r--windows/winnet/src/winnet/winnet.def3
-rw-r--r--windows/winnet/src/winnet/winnet.h54
-rw-r--r--windows/winnet/src/winnet/winnet.vcxproj2
-rw-r--r--windows/winnet/src/winnet/winnet.vcxproj.filters2
8 files changed, 464 insertions, 33 deletions
diff --git a/windows/winnet/src/extras/loader/loader.cpp b/windows/winnet/src/extras/loader/loader.cpp
index 43ec54978c..4a09a890eb 100644
--- a/windows/winnet/src/extras/loader/loader.cpp
+++ b/windows/winnet/src/extras/loader/loader.cpp
@@ -2,25 +2,39 @@
#include "../../winnet/winnet.h"
#include <iostream>
+void __stdcall ConnectivityChanged(uint8_t connected)
+{
+ std::wcout << (0 != connected? L"Connected" : L"NOT connected") << std::endl;
+}
+
int main()
{
- wchar_t *alias = nullptr;
+ //wchar_t *alias = nullptr;
+
+ //const auto status = WinNet_GetTapInterfaceAlias(&alias, nullptr, nullptr);
+
+ //switch (status)
+ //{
+ // case WINNET_GTIA_STATUS::FAILURE:
+ // {
+ // std::wcout << L"Could not determine alias" << std::endl;
+ // break;
+ // }
+ // case WINNET_GTIA_STATUS::SUCCESS:
+ // {
+ // std::wcout << L"Interface alias: " << alias << std::endl;
+ // WinNet_ReleaseString(alias);
+ // }
+ //};
+
+ uint8_t currentConnectivity = 0;
+
+ const auto status = WinNet_ActivateConnectivityMonitor(ConnectivityChanged, &currentConnectivity, nullptr, nullptr);
- const auto status = WinNet_GetTapInterfaceAlias(&alias, nullptr, nullptr);
+ std::wcout << L"Current connectivity: "
+ << (0 != currentConnectivity ? L"Connected" : L"NOT connected") << std::endl;
- switch (status)
- {
- case WINNET_GTIA_STATUS::FAILURE:
- {
- std::wcout << L"Could not determine alias" << std::endl;
- break;
- }
- case WINNET_GTIA_STATUS::SUCCESS:
- {
- std::wcout << L"Interface alias: " << alias << std::endl;
- WinNet_ReleaseString(alias);
- }
- };
+ _getwch();
return 0;
}
diff --git a/windows/winnet/src/winnet/netmonitor.cpp b/windows/winnet/src/winnet/netmonitor.cpp
new file mode 100644
index 0000000000..4de068f9cd
--- /dev/null
+++ b/windows/winnet/src/winnet/netmonitor.cpp
@@ -0,0 +1,217 @@
+#include "stdafx.h"
+#include "netmonitor.h"
+#include <libcommon/error.h>
+#include <libcommon/memory.h>
+#include <libcommon/synchronization.h>
+
+namespace
+{
+
+bool ValidInterfaceType(const MIB_IF_ROW2 &iface)
+{
+ switch (iface.InterfaceLuid.Info.IfType)
+ {
+ case IF_TYPE_SOFTWARE_LOOPBACK:
+ case IF_TYPE_TUNNEL:
+ {
+ return false;
+ }
+ }
+
+ if (FALSE == iface.InterfaceAndOperStatusFlags.ConnectorPresent
+ || FALSE != iface.InterfaceAndOperStatusFlags.EndPointInterface)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+} // anonyomus namespace
+
+NetMonitor::NetMonitor(NetMonitor::Notifier notifier, bool &currentConnectivity)
+ : m_connected(false)
+ , m_notifier(notifier)
+ , 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");
+}
+
+NetMonitor::~NetMonitor()
+{
+ CancelMibChangeNotify2(m_notificationHandle);
+}
+
+// static
+bool NetMonitor::CheckConnectivity()
+{
+ return CheckConnectivity(CreateCache());
+}
+
+// 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 (false == ValidInterfaceType(iface))
+ {
+ e.luid = iface.InterfaceLuid.Value;
+ e.valid = false;
+ e.connected = false;
+ }
+ else
+ {
+ e.luid = iface.InterfaceLuid.Value;
+ e.valid = true;
+ e.connected = (MediaConnectStateConnected == iface.MediaConnectState);
+ }
+
+ 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 thiz = reinterpret_cast<NetMonitor *>(context);
+
+ common::sync::ScopeLock<> processingLock(thiz->m_processingMutex);
+
+ switch (updateType)
+ {
+ case MibAddInstance:
+ {
+ MIB_IF_ROW2 iface = { 0 };
+ iface.InterfaceLuid = hint->InterfaceLuid;
+
+ if (NO_ERROR != GetIfEntry2(&iface))
+ {
+ // Failed to query interface.
+ return;
+ }
+
+ thiz->AddCacheEntry(thiz->m_cache, iface);
+
+ break;
+ }
+ case MibDeleteInstance:
+ {
+ const auto cacheEntry = thiz->m_cache.find(hint->InterfaceLuid.Value);
+
+ if (thiz->m_cache.end() != cacheEntry)
+ {
+ cacheEntry->second.connected = false;
+ }
+
+ break;
+ }
+ case MibParameterNotification:
+ {
+ auto cacheEntry = thiz->m_cache.find(hint->InterfaceLuid.Value);
+
+ if (thiz->m_cache.end() == cacheEntry)
+ {
+ //
+ // A change occurred on an interface that we're not tracking.
+ // Perhaps the MibAddInstance logic failed for some reason.
+ //
+
+ MIB_IF_ROW2 iface = { 0 };
+ iface.InterfaceLuid = hint->InterfaceLuid;
+
+ if (NO_ERROR != GetIfEntry2(&iface))
+ {
+ // Failed to query interface.
+ return;
+ }
+
+ thiz->AddCacheEntry(thiz->m_cache, iface);
+ }
+ else
+ {
+ //
+ // Abort processing if this is a known interface that we don't care about.
+ //
+ if (false == cacheEntry->second.valid)
+ {
+ return;
+ }
+
+ //
+ // Update cache.
+ //
+
+ MIB_IF_ROW2 iface = { 0 };
+ iface.InterfaceLuid = hint->InterfaceLuid;
+
+ const auto status = GetIfEntry2(&iface);
+
+ cacheEntry->second.connected =
+ (NO_ERROR == status ? MediaConnectStateConnected == iface.MediaConnectState : false);
+ }
+
+ break;
+ }
+ }
+
+ const auto previousConnectivity = thiz->m_connected;
+
+ thiz->updateConnectivity();
+
+ if (previousConnectivity != thiz->m_connected)
+ {
+ thiz->m_notifier(thiz->m_connected);
+ }
+}
diff --git a/windows/winnet/src/winnet/netmonitor.h b/windows/winnet/src/winnet/netmonitor.h
new file mode 100644
index 0000000000..77e4417b31
--- /dev/null
+++ b/windows/winnet/src/winnet/netmonitor.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#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(Notifier notifier, bool &currentConnectivity);
+ ~NetMonitor();
+
+ static bool CheckConnectivity();
+
+private:
+
+ 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;
+ Notifier m_notifier;
+
+ 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);
+};
diff --git a/windows/winnet/src/winnet/winnet.cpp b/windows/winnet/src/winnet/winnet.cpp
index ba14c737da..aa23adbc8d 100644
--- a/windows/winnet/src/winnet/winnet.cpp
+++ b/windows/winnet/src/winnet/winnet.cpp
@@ -3,9 +3,17 @@
#include "NetworkInterfaces.h"
#include "interfaceutils.h"
#include "libcommon/error.h"
+#include "netmonitor.h"
#include <cstdint>
#include <stdexcept>
+namespace
+{
+
+NetMonitor *g_NetMonitor = nullptr;
+
+} //anonymous namespace
+
extern "C"
WINNET_LINKAGE
WINNET_ETM_STATUS
@@ -13,7 +21,7 @@ WINNET_API
WinNet_EnsureTopMetric(
const wchar_t *deviceAlias,
WinNetErrorSink errorSink,
- void* errorSinkContext
+ void *errorSinkContext
)
{
try
@@ -43,7 +51,7 @@ WINNET_GTII_STATUS
WINNET_API
WinNet_GetTapInterfaceIpv6Status(
WinNetErrorSink errorSink,
- void* errorSinkContext
+ void *errorSinkContext
)
{
try
@@ -84,12 +92,12 @@ WinNet_GetTapInterfaceIpv6Status(
extern "C"
WINNET_LINKAGE
-WINNET_GTIA_STATUS
+bool
WINNET_API
WinNet_GetTapInterfaceAlias(
wchar_t **alias,
WinNetErrorSink errorSink,
- void* errorSinkContext
+ void *errorSinkContext
)
{
try
@@ -101,7 +109,7 @@ WinNet_GetTapInterfaceAlias(
*alias = stringBuffer;
- return WINNET_GTIA_STATUS::SUCCESS;
+ return true;
}
catch (std::exception &err)
{
@@ -110,11 +118,11 @@ WinNet_GetTapInterfaceAlias(
errorSink(err.what(), errorSinkContext);
}
- return WINNET_GTIA_STATUS::FAILURE;
+ return false;
}
catch (...)
{
- return WINNET_GTIA_STATUS::FAILURE;
+ return false;
}
}
@@ -134,3 +142,98 @@ WinNet_ReleaseString(
{
}
}
+
+extern "C"
+WINNET_LINKAGE
+bool
+WINNET_API
+WinNet_ActivateConnectivityMonitor(
+ WinNetConnectivityMonitorCallback callback,
+ void *callbackContext,
+ bool *currentConnectivity,
+ WinNetErrorSink errorSink,
+ void *errorSinkContext
+)
+{
+ try
+ {
+ if (nullptr != g_NetMonitor)
+ {
+ throw std::runtime_error("Cannot activate connectivity monitor twice");
+ }
+
+ auto forwarder = [callback, callbackContext](bool connected)
+ {
+ callback(connected, callbackContext);
+ };
+
+ bool connected = false;
+
+ g_NetMonitor = new NetMonitor(forwarder, connected);
+
+ if (nullptr != currentConnectivity)
+ {
+ *currentConnectivity = connected;
+ }
+
+ return true;
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != errorSink)
+ {
+ errorSink(err.what(), errorSinkContext);
+ }
+
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+}
+
+extern "C"
+WINNET_LINKAGE
+void
+WINNET_API
+WinNet_DeactivateConnectivityMonitor(
+)
+{
+ try
+ {
+ delete g_NetMonitor;
+ g_NetMonitor = nullptr;
+ }
+ catch (...)
+ {
+ }
+}
+
+extern "C"
+WINNET_LINKAGE
+WINNET_CC_STATUS
+WINNET_API
+WinNet_CheckConnectivity(
+ WinNetErrorSink errorSink,
+ void *errorSinkContext
+)
+{
+ try
+ {
+ return (NetMonitor::CheckConnectivity() ? WINNET_CC_STATUS::CONNECTED : WINNET_CC_STATUS::NOT_CONNECTED);
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != errorSink)
+ {
+ errorSink(err.what(), errorSinkContext);
+ }
+
+ return WINNET_CC_STATUS::CONNECTIVITY_UNKNOWN;
+ }
+ catch (...)
+ {
+ return WINNET_CC_STATUS::CONNECTIVITY_UNKNOWN;
+ }
+}
diff --git a/windows/winnet/src/winnet/winnet.def b/windows/winnet/src/winnet/winnet.def
index 267a797538..4c8cbfba9f 100644
--- a/windows/winnet/src/winnet/winnet.def
+++ b/windows/winnet/src/winnet/winnet.def
@@ -4,3 +4,6 @@ EXPORTS
WinNet_GetTapInterfaceIpv6Status
WinNet_GetTapInterfaceAlias
WinNet_ReleaseString
+ WinNet_ActivateConnectivityMonitor
+ WinNet_DeactivateConnectivityMonitor
+ WinNet_CheckConnectivity
diff --git a/windows/winnet/src/winnet/winnet.h b/windows/winnet/src/winnet/winnet.h
index b5771f5b5a..40ed25f567 100644
--- a/windows/winnet/src/winnet/winnet.h
+++ b/windows/winnet/src/winnet/winnet.h
@@ -1,5 +1,6 @@
#pragma once
-#include <cstdint>
+#include <stdint.h>
+#include <stdbool.h>
#ifdef WINNET_EXPORTS
#define WINNET_LINKAGE __declspec(dllexport)
@@ -25,7 +26,7 @@ WINNET_API
WinNet_EnsureTopMetric(
const wchar_t *deviceAlias,
WinNetErrorSink errorSink,
- void* errorSinkContext
+ void *errorSinkContext
);
enum class WINNET_GTII_STATUS : uint32_t
@@ -41,23 +42,17 @@ WINNET_GTII_STATUS
WINNET_API
WinNet_GetTapInterfaceIpv6Status(
WinNetErrorSink errorSink,
- void* errorSinkContext
+ void *errorSinkContext
);
-enum class WINNET_GTIA_STATUS : uint32_t
-{
- SUCCESS = 0,
- FAILURE = 1,
-};
-
extern "C"
WINNET_LINKAGE
-WINNET_GTIA_STATUS
+bool
WINNET_API
WinNet_GetTapInterfaceAlias(
wchar_t **alias,
WinNetErrorSink errorSink,
- void* errorSinkContext
+ void *errorSinkContext
);
//
@@ -71,3 +66,40 @@ WINNET_API
WinNet_ReleaseString(
wchar_t *str
);
+
+typedef void (WINNET_API *WinNetConnectivityMonitorCallback)(bool connected, void *context);
+
+extern "C"
+WINNET_LINKAGE
+bool
+WINNET_API
+WinNet_ActivateConnectivityMonitor(
+ WinNetConnectivityMonitorCallback callback,
+ void *callbackContext,
+ bool *currentConnectivity,
+ WinNetErrorSink errorSink,
+ void *errorSinkContext
+);
+
+extern "C"
+WINNET_LINKAGE
+void
+WINNET_API
+WinNet_DeactivateConnectivityMonitor(
+);
+
+enum class WINNET_CC_STATUS : uint32_t
+{
+ NOT_CONNECTED = 0,
+ CONNECTED = 1,
+ CONNECTIVITY_UNKNOWN = 2,
+};
+
+extern "C"
+WINNET_LINKAGE
+WINNET_CC_STATUS
+WINNET_API
+WinNet_CheckConnectivity(
+ WinNetErrorSink errorSink,
+ void *errorSinkContext
+);
diff --git a/windows/winnet/src/winnet/winnet.vcxproj b/windows/winnet/src/winnet/winnet.vcxproj
index e43b928d6c..fd10ec40fd 100644
--- a/windows/winnet/src/winnet/winnet.vcxproj
+++ b/windows/winnet/src/winnet/winnet.vcxproj
@@ -22,6 +22,7 @@
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="InterfacePair.cpp" />
<ClCompile Include="interfaceutils.cpp" />
+ <ClCompile Include="netmonitor.cpp" />
<ClCompile Include="NetworkInterfaces.cpp" />
<ClCompile Include="stdafx.cpp" />
<ClCompile Include="winnet.cpp" />
@@ -29,6 +30,7 @@
<ItemGroup>
<ClInclude Include="InterfacePair.h" />
<ClInclude Include="interfaceutils.h" />
+ <ClInclude Include="netmonitor.h" />
<ClInclude Include="NetworkInterfaces.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
diff --git a/windows/winnet/src/winnet/winnet.vcxproj.filters b/windows/winnet/src/winnet/winnet.vcxproj.filters
index 2d320e7908..3ac5a14717 100644
--- a/windows/winnet/src/winnet/winnet.vcxproj.filters
+++ b/windows/winnet/src/winnet/winnet.vcxproj.filters
@@ -7,6 +7,7 @@
<ClCompile Include="NetworkInterfaces.cpp" />
<ClCompile Include="InterfacePair.cpp" />
<ClCompile Include="interfaceutils.cpp" />
+ <ClCompile Include="netmonitor.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@@ -15,6 +16,7 @@
<ClInclude Include="NetworkInterfaces.h" />
<ClInclude Include="InterfacePair.h" />
<ClInclude Include="interfaceutils.h" />
+ <ClInclude Include="netmonitor.h" />
</ItemGroup>
<ItemGroup>
<None Include="winnet.def" />