summaryrefslogtreecommitdiffhomepage
path: root/windows/winnet
diff options
context:
space:
mode:
authorOdd Stranne <odd@mullvad.net>2019-05-10 13:19:12 +0200
committerEmīls Piņķis <emils@mullvad.net>2019-05-21 11:07:34 +0100
commitda0e3356fde77a2183b47bc8b20d05b402a075bf (patch)
tree609b496bf3b0f676a08b7c8ac60838245718e6f2 /windows/winnet
parent4073b27046c34e86c1117bbf70f5305e76c52a22 (diff)
downloadmullvadvpn-da0e3356fde77a2183b47bc8b20d05b402a075bf.tar.xz
mullvadvpn-da0e3356fde77a2183b47bc8b20d05b402a075bf.zip
Add WinNet_ActivateConnectivityMonitor()
Diffstat (limited to 'windows/winnet')
-rw-r--r--windows/winnet/src/winnet/netmonitor.cpp201
-rw-r--r--windows/winnet/src/winnet/netmonitor.h55
-rw-r--r--windows/winnet/src/winnet/winnet.cpp74
-rw-r--r--windows/winnet/src/winnet/winnet.h26
-rw-r--r--windows/winnet/src/winnet/winnet.vcxproj2
-rw-r--r--windows/winnet/src/winnet/winnet.vcxproj.filters2
6 files changed, 360 insertions, 0 deletions
diff --git a/windows/winnet/src/winnet/netmonitor.cpp b/windows/winnet/src/winnet/netmonitor.cpp
new file mode 100644
index 0000000000..6ccaf3d64a
--- /dev/null
+++ b/windows/winnet/src/winnet/netmonitor.cpp
@@ -0,0 +1,201 @@
+#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_notifier(notifier)
+ , m_connected(false)
+ , m_notificationHandle(nullptr)
+{
+ 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);
+}
+
+void 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);
+ };
+
+ for (ULONG i = 0; i < table->NumEntries; ++i)
+ {
+ addCacheEntry(table->Table[i]);
+ }
+}
+
+void NetMonitor::addCacheEntry(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);
+ }
+
+ m_cache.insert(std::make_pair(e.luid, e));
+}
+
+void NetMonitor::updateConnectivity()
+{
+ for (const auto cacheEntryIter : m_cache)
+ {
+ const auto entry = cacheEntryIter.second;
+
+ if (entry.valid && entry.connected)
+ {
+ m_connected = true;
+ return;
+ }
+ }
+
+ m_connected = false;
+}
+
+//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(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(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..b8e418b82d
--- /dev/null
+++ b/windows/winnet/src/winnet/netmonitor.h
@@ -0,0 +1,55 @@
+#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();
+
+private:
+
+ std::mutex m_processingMutex;
+
+ 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;
+ };
+
+ std::map<uint64_t, CacheEntry> m_cache;
+
+ bool m_connected;
+
+ HANDLE m_notificationHandle;
+
+ void createCache();
+ void addCacheEntry(const MIB_IF_ROW2 &iface);
+ 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..9741613457 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
@@ -134,3 +142,69 @@ WinNet_ReleaseString(
{
}
}
+
+extern "C"
+WINNET_LINKAGE
+WINNET_ACM_STATUS
+WINNET_API
+WinNet_ActivateConnectivityMonitor(
+ WinNetConnectivityMonitorCallback callback,
+ uint8_t *currentConnectivity,
+ WinNetErrorSink errorSink,
+ void* errorSinkContext
+)
+{
+ try
+ {
+ if (nullptr != g_NetMonitor)
+ {
+ throw std::runtime_error("Cannot activate connectivity monitor twice");
+ }
+
+ auto forwarder = [callback](bool connected)
+ {
+ callback(static_cast<uint8_t>(connected));
+ };
+
+ bool connected = false;
+
+ g_NetMonitor = new NetMonitor(forwarder, connected);
+
+ if (nullptr != currentConnectivity)
+ {
+ *currentConnectivity = static_cast<uint8_t>(connected);
+ }
+
+ return WINNET_ACM_STATUS::SUCCESS;
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != errorSink)
+ {
+ errorSink(err.what(), errorSinkContext);
+ }
+
+ return WINNET_ACM_STATUS::FAILURE;
+ }
+ catch (...)
+ {
+ return WINNET_ACM_STATUS::FAILURE;
+ }
+}
+
+extern "C"
+WINNET_LINKAGE
+void
+WINNET_API
+WinNet_DeactivateConnectivityMonitor(
+)
+{
+ try
+ {
+ delete g_NetMonitor;
+ g_NetMonitor = nullptr;
+ }
+ catch (...)
+ {
+ }
+}
diff --git a/windows/winnet/src/winnet/winnet.h b/windows/winnet/src/winnet/winnet.h
index b5771f5b5a..c3464d4c03 100644
--- a/windows/winnet/src/winnet/winnet.h
+++ b/windows/winnet/src/winnet/winnet.h
@@ -71,3 +71,29 @@ WINNET_API
WinNet_ReleaseString(
wchar_t *str
);
+
+typedef void (WINNET_API *WinNetConnectivityMonitorCallback)(uint8_t connected);
+
+enum class WINNET_ACM_STATUS : uint32_t
+{
+ SUCCESS = 0,
+ FAILURE = 1,
+};
+
+extern "C"
+WINNET_LINKAGE
+WINNET_ACM_STATUS
+WINNET_API
+WinNet_ActivateConnectivityMonitor(
+ WinNetConnectivityMonitorCallback callback,
+ uint8_t *currentConnectivity,
+ WinNetErrorSink errorSink,
+ void* errorSinkContext
+);
+
+extern "C"
+WINNET_LINKAGE
+void
+WINNET_API
+WinNet_DeactivateConnectivityMonitor(
+);
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" />