diff options
Diffstat (limited to 'windows')
| -rw-r--r-- | windows/winnet/src/extras/loader/loader.cpp | 44 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/netmonitor.cpp | 217 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/netmonitor.h | 58 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/winnet.cpp | 117 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/winnet.def | 3 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/winnet.h | 54 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/winnet.vcxproj | 2 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/winnet.vcxproj.filters | 2 |
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, ¤tConnectivity, 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 ¤tConnectivity) + : 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 ¤tConnectivity); + ~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" /> |
