diff options
| author | Emīls Piņķis <emils@mullvad.net> | 2019-05-21 11:14:32 +0100 |
|---|---|---|
| committer | Emīls Piņķis <emils@mullvad.net> | 2019-05-21 11:14:32 +0100 |
| commit | b0ca9c2fc0d59be5d3a5612dadd85fc2d95e8867 (patch) | |
| tree | c9b4b2026319513b4bc1f52f5f30b3cbf6be8e3b | |
| parent | 4073b27046c34e86c1117bbf70f5305e76c52a22 (diff) | |
| parent | de596ee1afe9818dacdf1f10966ba398338add16 (diff) | |
| download | mullvadvpn-b0ca9c2fc0d59be5d3a5612dadd85fc2d95e8867.tar.xz mullvadvpn-b0ca9c2fc0d59be5d3a5612dadd85fc2d95e8867.zip | |
Merge branch 'win-detect-net'
| -rw-r--r-- | CHANGELOG.md | 3 | ||||
| -rw-r--r-- | talpid-core/src/offline/windows.rs | 154 | ||||
| -rw-r--r-- | talpid-core/src/winnet.rs | 55 | ||||
| -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 |
11 files changed, 632 insertions, 77 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 86cd05bb70..1bb910b163 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,9 @@ Line wrap the file at 100 chars. Th #### macOS - Raise max number of open files for the daemon to 1024. Should prevent threads from panicking. +#### Windows +- Add better offline detection + ## [2019.4] - 2019-05-08 This release is identical to 2019.4-beta1 diff --git a/talpid-core/src/offline/windows.rs b/talpid-core/src/offline/windows.rs index e40d8fe44f..81a9344d3f 100644 --- a/talpid-core/src/offline/windows.rs +++ b/talpid-core/src/offline/windows.rs @@ -6,17 +6,20 @@ //! GNU General Public License as published by the Free Software Foundation, either version 3 of //! the License, or (at your option) any later version. -use crate::tunnel_state_machine::TunnelCommand; +use crate::{tunnel_state_machine::TunnelCommand, winnet}; use futures::sync::mpsc::UnboundedSender; -use log::debug; +use parking_lot::Mutex; use std::{ ffi::c_void, io, mem::zeroed, os::windows::io::{IntoRawHandle, RawHandle}, - ptr, thread, + ptr, + sync::Arc, + thread, time::Duration, }; +use talpid_types::ErrorExt; use winapi::{ shared::{ basetsd::LONG_PTR, @@ -46,32 +49,61 @@ const REQUEST_THREAD_SHUTDOWN: UINT = WM_USER + 1; pub enum Error { #[error(display = "Unable to create listener thread")] ThreadCreationError(#[error(cause)] io::Error), + #[error(display = "Failed to start connectivity monitor")] + ConnectivityMonitorError, } pub struct BroadcastListener { thread_handle: RawHandle, thread_id: DWORD, + _system_state: Arc<Mutex<SystemState>>, } unsafe impl Send for BroadcastListener {} impl BroadcastListener { - pub fn start<F>(client_callback: F) -> Result<Self, Error> - where - F: Fn(UINT, WPARAM, LPARAM) + 'static + Send, - { + pub fn start(sender: UnboundedSender<TunnelCommand>) -> Result<Self, Error> { + let mut system_state = Arc::new(Mutex::new(SystemState { + network_connectivity: false, + suspended: false, + daemon_channel: sender, + })); + + let power_broadcast_state_ref = system_state.clone(); + + let power_broadcast_callback = move |message: UINT, wparam: WPARAM, _lparam: LPARAM| { + let state = power_broadcast_state_ref.clone(); + if message == WM_POWERBROADCAST { + if wparam == PBT_APMSUSPEND { + log::debug!("Machine is preparing to enter sleep mode"); + apply_system_state_change(state, StateChange::Suspended(true)); + } else if wparam == PBT_APMRESUMEAUTOMATIC { + log::debug!("Machine is returning from sleep mode"); + thread::spawn(move || { + // TAP will be unavailable for approximately 2 seconds on a healthy machine. + thread::sleep(Duration::from_secs(5)); + log::debug!("TAP is presumed to have been re-initialized"); + apply_system_state_change(state, StateChange::Suspended(false)); + }); + } + } + }; + let join_handle = thread::Builder::new() .spawn(move || unsafe { - Self::message_pump(client_callback); + Self::message_pump(power_broadcast_callback); }) .map_err(Error::ThreadCreationError)?; let real_handle = join_handle.into_raw_handle(); + unsafe { Self::setup_network_connectivity_listener(&mut system_state)? }; + Ok(BroadcastListener { thread_handle: real_handle, thread_id: unsafe { GetThreadId(real_handle) }, + _system_state: system_state, }) } @@ -156,6 +188,33 @@ impl BroadcastListener { DefWindowProcW(window, message, wparam, lparam) } + + /// The caller must make sure the `system_state` reference is valid + /// until after `WinNet_DeactivateConnectivityMonitor` has been called. + unsafe fn setup_network_connectivity_listener( + 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::error_sink), + ptr::null_mut(), + ) { + return Err(Error::ConnectivityMonitorError); + } + state.network_connectivity = current_connectivity; + Ok(()) + } + + unsafe extern "system" fn connectivity_callback(connectivity: bool, context: *mut c_void) { + let state_lock: &mut Mutex<SystemState> = &mut *(context as *mut _); + let mut state = state_lock.lock(); + state.apply_change(StateChange::NetworkConnectivity(connectivity)); + } } impl Drop for BroadcastListener { @@ -164,35 +223,74 @@ impl Drop for BroadcastListener { PostThreadMessageW(self.thread_id, REQUEST_THREAD_SHUTDOWN, 0, 0); WaitForSingleObject(self.thread_handle, INFINITE); CloseHandle(self.thread_handle); + if !winnet::WinNet_DeactivateConnectivityMonitor() { + log::error!("Failed to deactivate connectivity monitor"); + } } } } +#[derive(Debug)] +enum StateChange { + NetworkConnectivity(bool), + Suspended(bool), +} + +struct SystemState { + network_connectivity: bool, + suspended: bool, + daemon_channel: UnboundedSender<TunnelCommand>, +} + +impl SystemState { + fn apply_change(&mut self, change: StateChange) { + let old_state = self.is_offline_currently(); + match change { + StateChange::NetworkConnectivity(connectivity) => { + self.network_connectivity = connectivity; + } + + StateChange::Suspended(suspended) => { + self.suspended = suspended; + } + }; + + let new_state = self.is_offline_currently(); + if old_state != new_state { + if let Err(e) = self + .daemon_channel + .unbounded_send(TunnelCommand::IsOffline(new_state)) + { + log::error!("Failed to send new offline state to daemon: {}", e); + } + } + } + + fn is_offline_currently(&self) -> bool { + !self.network_connectivity || self.suspended + } +} + pub type MonitorHandle = BroadcastListener; pub fn spawn_monitor(sender: UnboundedSender<TunnelCommand>) -> Result<MonitorHandle, Error> { - let listener = - BroadcastListener::start(move |message: UINT, wparam: WPARAM, _lparam: LPARAM| { - if message == WM_POWERBROADCAST { - if wparam == PBT_APMSUSPEND { - debug!("Machine is preparing to enter sleep mode"); - let _ = sender.unbounded_send(TunnelCommand::IsOffline(true)); - } else if wparam == PBT_APMRESUMEAUTOMATIC { - debug!("Machine is returning from sleep mode"); - let cloned_sender = sender.clone(); - thread::spawn(move || { - // TAP will be unavailable for approximately 2 seconds on a healthy machine. - thread::sleep(Duration::from_secs(5)); - debug!("TAP is presumed to have been re-initialized"); - let _ = cloned_sender.unbounded_send(TunnelCommand::IsOffline(false)); - }); - } - } - })?; + BroadcastListener::start(sender) +} - Ok(listener) +fn apply_system_state_change(state: Arc<Mutex<SystemState>>, change: StateChange) { + let mut state = state.lock(); + state.apply_change(change); } pub fn is_offline() -> bool { - false + match winnet::is_offline() { + Ok(state) => state, + Err(e) => { + log::error!( + "{}", + e.display_chain_with_msg("Failed to get current connectivity") + ); + false + } + } } diff --git a/talpid-core/src/winnet.rs b/talpid-core/src/winnet.rs index d659534e1f..eb8694bf27 100644 --- a/talpid-core/src/winnet.rs +++ b/talpid-core/src/winnet.rs @@ -1,5 +1,7 @@ -pub use self::api::ErrorSink; use self::api::*; +pub use self::api::{ + ErrorSink, WinNet_ActivateConnectivityMonitor, WinNet_DeactivateConnectivityMonitor, +}; use libc::{c_char, c_void, wchar_t}; use std::{ffi::OsString, ptr}; use widestring::WideCString; @@ -22,6 +24,10 @@ pub enum Error { /// Failed to determine alias of TAP adapter. #[error(display = "Failed to determine alias of TAP adapter")] GetTapAlias, + + /// Can't establish whether host is connected to a non-virtual network + #[error(display = "Network connectivity undecideable")] + ConnectivityUnkown, } /// Error callback used with `winnet.dll`. @@ -92,14 +98,7 @@ pub fn get_tap_interface_alias() -> Result<OsString, Error> { WinNet_GetTapInterfaceAlias(&mut alias_ptr as *mut _, Some(error_sink), ptr::null_mut()) }; - if status != 0 { - if status != 1 { - log::error!( - "Unexpected return code from WinNet_GetTapInterfaceAlias: {}", - status - ); - } - + if !status { return Err(Error::GetTapAlias); } @@ -109,6 +108,19 @@ pub fn get_tap_interface_alias() -> Result<OsString, Error> { Ok(alias.to_os_string()) } +/// Returns true if current host is not connected to any network +pub fn is_offline() -> Result<bool, Error> { + match unsafe { WinNet_CheckConnectivity(Some(error_sink), ptr::null_mut()) } { + // Not connected + 0 => Ok(true), + // Connected + 1 => Ok(false), + // 2 means that connectivity can't be determined, but any other return value is unexpected + // and as such, is considered to be an error. + _ => Err(Error::ConnectivityUnkown), + } +} + #[allow(non_snake_case)] mod api { use libc::{c_char, c_void, wchar_t}; @@ -116,6 +128,8 @@ mod api { /// Error callback type for use with `winnet.dll`. pub type ErrorSink = extern "system" fn(msg: *const c_char, ctx: *mut c_void); + pub type ConnectivityCallback = unsafe extern "system" fn(is_connected: bool, ctx: *mut c_void); + extern "system" { #[link_name = "WinNet_EnsureTopMetric"] pub fn WinNet_EnsureTopMetric( @@ -123,27 +137,36 @@ mod api { sink: Option<ErrorSink>, sink_context: *mut c_void, ) -> u32; - } - extern "system" { #[link_name = "WinNet_GetTapInterfaceIpv6Status"] pub fn WinNet_GetTapInterfaceIpv6Status( sink: Option<ErrorSink>, sink_context: *mut c_void, ) -> u32; - } - extern "system" { #[link_name = "WinNet_GetTapInterfaceAlias"] pub fn WinNet_GetTapInterfaceAlias( tunnel_interface_alias: *mut *mut wchar_t, sink: Option<ErrorSink>, sink_context: *mut c_void, - ) -> u32; - } + ) -> bool; - extern "system" { #[link_name = "WinNet_ReleaseString"] pub fn WinNet_ReleaseString(string: *mut wchar_t) -> u32; + + #[link_name = "WinNet_ActivateConnectivityMonitor"] + pub fn WinNet_ActivateConnectivityMonitor( + callback: Option<ConnectivityCallback>, + callbackContext: *mut libc::c_void, + currentConnectivity: *mut bool, + sink: Option<ErrorSink>, + sink_context: *mut c_void, + ) -> bool; + + #[link_name = "WinNet_DeactivateConnectivityMonitor"] + pub fn WinNet_DeactivateConnectivityMonitor() -> bool; + + #[link_name = "WinNet_CheckConnectivity"] + pub fn WinNet_CheckConnectivity(sink: Option<ErrorSink>, sink_context: *mut c_void) -> u32; } } 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" /> |
