summaryrefslogtreecommitdiffhomepage
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
parent4073b27046c34e86c1117bbf70f5305e76c52a22 (diff)
parentde596ee1afe9818dacdf1f10966ba398338add16 (diff)
downloadmullvadvpn-b0ca9c2fc0d59be5d3a5612dadd85fc2d95e8867.tar.xz
mullvadvpn-b0ca9c2fc0d59be5d3a5612dadd85fc2d95e8867.zip
Merge branch 'win-detect-net'
-rw-r--r--CHANGELOG.md3
-rw-r--r--talpid-core/src/offline/windows.rs154
-rw-r--r--talpid-core/src/winnet.rs55
-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
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, &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" />