summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--gui/packages/desktop/src/main/daemon-rpc.js4
-rw-r--r--gui/packages/desktop/src/renderer/components/NotificationArea.js2
-rw-r--r--talpid-core/src/tunnel/mod.rs26
-rw-r--r--talpid-core/src/tunnel/openvpn.rs106
-rw-r--r--talpid-core/src/tunnel_state_machine/connected_state.rs16
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs63
-rw-r--r--talpid-core/src/tunnel_state_machine/disconnecting_state.rs18
-rw-r--r--talpid-types/src/tunnel.rs3
9 files changed, 188 insertions, 53 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0278a1bc91..d83a18fd5c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,6 +48,9 @@ Line wrap the file at 100 chars. Th
- Fix error printed from the CLI when issuing `relay update`.
- Fix relay list update interval. Should now handle sleep better.
+#### Windows
+- Gracefully block when TAP adapter is missing or disabled, instead of retrying to connect.
+
## [2018.6] - 2018-12-12
This release is identical to 2018.6-beta1
diff --git a/gui/packages/desktop/src/main/daemon-rpc.js b/gui/packages/desktop/src/main/daemon-rpc.js
index 26da6e14af..bb55e5d335 100644
--- a/gui/packages/desktop/src/main/daemon-rpc.js
+++ b/gui/packages/desktop/src/main/daemon-rpc.js
@@ -52,7 +52,8 @@ export type BlockReason =
| 'set_dns_error'
| 'start_tunnel_error'
| 'no_matching_relay'
- | 'is_offline',
+ | 'is_offline'
+ | 'tap_adapter_problem',
}
| { reason: 'auth_failed', details: ?string };
@@ -314,6 +315,7 @@ const TunnelStateTransitionSchema = oneOf(
'start_tunnel_error',
'no_matching_relay',
'is_offline',
+ 'tap_adapter_problem',
),
}),
object({
diff --git a/gui/packages/desktop/src/renderer/components/NotificationArea.js b/gui/packages/desktop/src/renderer/components/NotificationArea.js
index 646dd236c2..3eba431a52 100644
--- a/gui/packages/desktop/src/renderer/components/NotificationArea.js
+++ b/gui/packages/desktop/src/renderer/components/NotificationArea.js
@@ -55,6 +55,8 @@ function getBlockReasonMessage(blockReason: BlockReason): string {
return 'No relay server matches the current settings';
case 'is_offline':
return 'This device is offline, no tunnels can be established';
+ case 'tap_adapter_problem':
+ return "Unable to detect a working TAP adapter on this device. If you've disabled it, enable it again. Otherwise, please reinstall the app";
default:
return `Unknown error: ${(blockReason.reason: empty)}`;
}
diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs
index 078f23cf16..dc0d935ded 100644
--- a/talpid-core/src/tunnel/mod.rs
+++ b/talpid-core/src/tunnel/mod.rs
@@ -38,9 +38,9 @@ const OPENVPN_BIN_FILENAME: &str = "openvpn.exe";
error_chain! {
errors {
- /// An error indicating there was an error listening for events from the VPN tunnel.
- TunnelMonitoringError {
- description("Error while setting up or processing events from the VPN tunnel")
+ /// There was an error preparing to listen for events from the VPN tunnel.
+ TunnelMonitorSetUpError {
+ description("Error while setting up to listen for events from the VPN tunnel")
}
/// The OpenVPN binary was not found.
OpenVpnNotFound(path: PathBuf) {
@@ -74,6 +74,12 @@ error_chain! {
description("This tunnel protocol is not supported")
}
}
+
+ links {
+ OpenVpnTunnelMonitoringError(openvpn::Error, openvpn::ErrorKind)
+ /// There was an error listening for events from the OpenVPN tunnel
+ ;
+ }
}
@@ -156,7 +162,7 @@ impl TunnelMonitor {
tunnel_options: &TunnelOptions,
tunnel_alias: Option<OsString>,
username: &str,
- log: Option<&Path>,
+ log: Option<PathBuf>,
resource_dir: &Path,
on_event: L,
) -> Result<Self>
@@ -181,7 +187,6 @@ impl TunnelMonitor {
Some(ref file) => Some(file.as_ref()),
_ => None,
},
- log,
resource_dir,
)?;
@@ -212,8 +217,9 @@ impl TunnelMonitor {
cmd,
on_openvpn_event,
Self::get_plugin_path(resource_dir)?,
+ log,
)
- .chain_err(|| ErrorKind::TunnelMonitoringError)?;
+ .chain_err(|| ErrorKind::TunnelMonitorSetUpError)?;
Ok(TunnelMonitor {
monitor,
_user_pass_file: user_pass_file,
@@ -242,7 +248,6 @@ impl TunnelMonitor {
options: &TunnelOptions,
user_pass_file: &Path,
proxy_auth_file: Option<&Path>,
- log: Option<&Path>,
resource_dir: &Path,
) -> Result<OpenVpnCommand> {
let mut cmd = OpenVpnCommand::new(Self::get_openvpn_bin(resource_dir)?);
@@ -261,9 +266,6 @@ impl TunnelMonitor {
.enable_ipv6(options.enable_ipv6)
.tunnel_alias(tunnel_alias)
.ca(resource_dir.join("ca.crt"));
- if let Some(log) = log {
- cmd.log(log);
- }
if let Some(proxy_auth_file) = proxy_auth_file {
cmd.proxy_auth(proxy_auth_file);
}
@@ -344,9 +346,7 @@ impl TunnelMonitor {
/// Consumes the monitor and blocks until the tunnel exits or there is an error.
pub fn wait(self) -> Result<()> {
- self.monitor
- .wait()
- .chain_err(|| ErrorKind::TunnelMonitoringError)
+ self.monitor.wait().map_err(Error::from)
}
}
diff --git a/talpid-core/src/tunnel/openvpn.rs b/talpid-core/src/tunnel/openvpn.rs
index fd2437f2e6..9d7e6eed93 100644
--- a/talpid-core/src/tunnel/openvpn.rs
+++ b/talpid-core/src/tunnel/openvpn.rs
@@ -5,7 +5,7 @@ use crate::process::{
use std::{
collections::HashMap,
io,
- path::Path,
+ path::{Path, PathBuf},
process::ExitStatus,
sync::{
atomic::{AtomicBool, Ordering},
@@ -29,6 +29,16 @@ mod errors {
EventDispatcherError {
description("Unable to start or manage the event dispatcher IPC server")
}
+ #[cfg(windows)]
+ /// No TAP adapter was detected
+ MissingTapAdapter {
+ description("No TAP adapter was detected")
+ }
+ #[cfg(windows)]
+ /// TAP adapter seems to be disabled
+ DisabledTapAdapter {
+ description("The TAP adapter appears to be disabled")
+ }
}
}
}
@@ -46,38 +56,49 @@ static OPENVPN_DIE_TIMEOUT: Duration = Duration::from_secs(30);
pub struct OpenVpnMonitor<C: OpenVpnBuilder = OpenVpnCommand> {
child: Arc<C::ProcessHandle>,
event_dispatcher: Option<talpid_ipc::IpcServer>,
+ log_path: Option<PathBuf>,
closed: Arc<AtomicBool>,
}
impl OpenVpnMonitor<OpenVpnCommand> {
/// Creates a new `OpenVpnMonitor` with the given listener and using the plugin at the given
/// path.
- pub fn start<L, P>(cmd: OpenVpnCommand, on_event: L, plugin_path: P) -> Result<Self>
+ pub fn start<L>(
+ cmd: OpenVpnCommand,
+ on_event: L,
+ plugin_path: impl AsRef<Path>,
+ log_path: Option<PathBuf>,
+ ) -> Result<Self>
where
L: Fn(openvpn_plugin::EventType, HashMap<String, String>) + Send + Sync + 'static,
- P: AsRef<Path>,
{
- Self::new_internal(cmd, on_event, plugin_path)
+ Self::new_internal(cmd, on_event, plugin_path, log_path)
}
}
impl<C: OpenVpnBuilder> OpenVpnMonitor<C> {
- fn new_internal<L, P>(mut cmd: C, on_event: L, plugin_path: P) -> Result<OpenVpnMonitor<C>>
+ fn new_internal<L>(
+ mut cmd: C,
+ on_event: L,
+ plugin_path: impl AsRef<Path>,
+ log_path: Option<PathBuf>,
+ ) -> Result<OpenVpnMonitor<C>>
where
L: Fn(openvpn_plugin::EventType, HashMap<String, String>) + Send + Sync + 'static,
- P: AsRef<Path>,
{
let event_dispatcher =
event_server::start(on_event).chain_err(|| ErrorKind::EventDispatcherError)?;
- cmd.plugin(plugin_path, vec![event_dispatcher.path().to_owned()]);
let child = cmd
+ .plugin(plugin_path, vec![event_dispatcher.path().to_owned()])
+ .log(log_path.as_ref())
.start()
.chain_err(|| ErrorKind::ChildProcessError("Failed to start"))?;
Ok(OpenVpnMonitor {
child: Arc::new(child),
event_dispatcher: Some(event_dispatcher),
+ log_path,
closed: Arc::new(AtomicBool::new(false)),
})
}
@@ -106,7 +127,7 @@ impl<C: OpenVpnBuilder> OpenVpnMonitor<C> {
Ok(())
} else {
log::error!("OpenVPN died unexpectedly with status: {}", exit_status);
- Err(ErrorKind::ChildProcessError("Died unexpectedly").into())
+ Err(self.postmortem())
}
}
WaitResult::Child(Err(e), _) => {
@@ -148,6 +169,27 @@ impl<C: OpenVpnBuilder> OpenVpnMonitor<C> {
let _ = rx.recv().unwrap();
result
}
+
+ /// Performs a postmortem analysis to attempt to provide a more detailed error result.
+ fn postmortem(self) -> Error {
+ #[cfg(windows)]
+ {
+ use std::fs;
+
+ if let Some(log_path) = self.log_path {
+ if let Ok(log) = fs::read_to_string(log_path) {
+ if log.contains("There are no TAP-Windows adapters on this system") {
+ return ErrorKind::MissingTapAdapter.into();
+ }
+ if log.contains("CreateFile failed on TAP device") {
+ return ErrorKind::DisabledTapAdapter.into();
+ }
+ }
+ }
+ }
+
+ ErrorKind::ChildProcessError("Died unexpectedly").into()
+ }
}
/// A handle to an `OpenVpnMonitor` for closing it.
@@ -181,7 +223,10 @@ pub trait OpenVpnBuilder {
type ProcessHandle: ProcessHandle;
/// Set the OpenVPN plugin to the given values.
- fn plugin<P: AsRef<Path>>(&mut self, path: P, args: Vec<String>) -> &mut Self;
+ fn plugin(&mut self, path: impl AsRef<Path>, args: Vec<String>) -> &mut Self;
+
+ /// Set the OpenVPN log file path to use.
+ fn log(&mut self, log_path: Option<impl AsRef<Path>>) -> &mut Self;
/// Spawn the subprocess and return a handle.
fn start(&self) -> io::Result<Self::ProcessHandle>;
@@ -199,10 +244,18 @@ pub trait ProcessHandle: Send + Sync + 'static {
impl OpenVpnBuilder for OpenVpnCommand {
type ProcessHandle = OpenVpnProcHandle;
- fn plugin<P: AsRef<Path>>(&mut self, path: P, args: Vec<String>) -> &mut Self {
+ fn plugin(&mut self, path: impl AsRef<Path>, args: Vec<String>) -> &mut Self {
self.plugin(path, args)
}
+ fn log(&mut self, log_path: Option<impl AsRef<Path>>) -> &mut Self {
+ if let Some(log_path) = log_path {
+ self.log(log_path)
+ } else {
+ self
+ }
+ }
+
fn start(&self) -> io::Result<OpenVpnProcHandle> {
OpenVpnProcHandle::new(self.build())
}
@@ -284,17 +337,23 @@ mod tests {
#[derive(Debug, Default, Clone)]
struct TestOpenVpnBuilder {
pub plugin: Arc<Mutex<Option<PathBuf>>>,
+ pub log: Arc<Mutex<Option<PathBuf>>>,
pub process_handle: Option<TestProcessHandle>,
}
impl OpenVpnBuilder for TestOpenVpnBuilder {
type ProcessHandle = TestProcessHandle;
- fn plugin<P: AsRef<Path>>(&mut self, path: P, _args: Vec<String>) -> &mut Self {
+ fn plugin(&mut self, path: impl AsRef<Path>, _args: Vec<String>) -> &mut Self {
*self.plugin.lock().unwrap() = Some(path.as_ref().to_path_buf());
self
}
+ fn log(&mut self, log: Option<impl AsRef<Path>>) -> &mut Self {
+ *self.log.lock().unwrap() = log.as_ref().map(|path| path.as_ref().to_path_buf());
+ self
+ }
+
fn start(&self) -> io::Result<Self::ProcessHandle> {
self.process_handle
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "failed to start"))
@@ -325,7 +384,7 @@ mod tests {
#[test]
fn sets_plugin() {
let builder = TestOpenVpnBuilder::default();
- let _ = OpenVpnMonitor::new_internal(builder.clone(), |_, _| {}, "./my_test_plugin");
+ let _ = OpenVpnMonitor::new_internal(builder.clone(), |_, _| {}, "./my_test_plugin", None);
assert_eq!(
Some(PathBuf::from("./my_test_plugin")),
*builder.plugin.lock().unwrap()
@@ -333,10 +392,25 @@ mod tests {
}
#[test]
+ fn sets_log() {
+ let builder = TestOpenVpnBuilder::default();
+ let _ = OpenVpnMonitor::new_internal(
+ builder.clone(),
+ |_, _| {},
+ "./my_test_plugin",
+ Some(PathBuf::from("./my_test_log_file")),
+ );
+ assert_eq!(
+ Some(PathBuf::from("./my_test_log_file")),
+ *builder.log.lock().unwrap()
+ );
+ }
+
+ #[test]
fn exit_successfully() {
let mut builder = TestOpenVpnBuilder::default();
builder.process_handle = Some(TestProcessHandle(0));
- let testee = OpenVpnMonitor::new_internal(builder, |_, _| {}, "").unwrap();
+ let testee = OpenVpnMonitor::new_internal(builder, |_, _| {}, "", None).unwrap();
assert!(testee.wait().is_ok());
}
@@ -344,7 +418,7 @@ mod tests {
fn exit_error() {
let mut builder = TestOpenVpnBuilder::default();
builder.process_handle = Some(TestProcessHandle(1));
- let testee = OpenVpnMonitor::new_internal(builder, |_, _| {}, "").unwrap();
+ let testee = OpenVpnMonitor::new_internal(builder, |_, _| {}, "", None).unwrap();
assert!(testee.wait().is_err());
}
@@ -352,7 +426,7 @@ mod tests {
fn wait_closed() {
let mut builder = TestOpenVpnBuilder::default();
builder.process_handle = Some(TestProcessHandle(1));
- let testee = OpenVpnMonitor::new_internal(builder, |_, _| {}, "").unwrap();
+ let testee = OpenVpnMonitor::new_internal(builder, |_, _| {}, "", None).unwrap();
testee.close_handle().close().unwrap();
assert!(testee.wait().is_ok());
}
@@ -360,7 +434,7 @@ mod tests {
#[test]
fn failed_process_start() {
let builder = TestOpenVpnBuilder::default();
- let error = OpenVpnMonitor::new_internal(builder, |_, _| {}, "").unwrap_err();
+ let error = OpenVpnMonitor::new_internal(builder, |_, _| {}, "", None).unwrap_err();
match error.kind() {
ErrorKind::ChildProcessError(_) => (),
_ => panic!("Wrong error"),
diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs
index 961889d6c2..db28a200ef 100644
--- a/talpid-core/src/tunnel_state_machine/connected_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connected_state.rs
@@ -9,9 +9,9 @@ use talpid_types::{
};
use super::{
- AfterDisconnect, ConnectingState, DisconnectingState, EventConsequence, Result, ResultExt,
- SharedTunnelStateValues, TunnelCommand, TunnelParameters, TunnelState, TunnelStateTransition,
- TunnelStateWrapper,
+ AfterDisconnect, BlockedState, ConnectingState, DisconnectingState, EventConsequence, Result,
+ ResultExt, SharedTunnelStateValues, TunnelCommand, TunnelParameters, TunnelState,
+ TunnelStateTransition, TunnelStateWrapper,
};
use crate::{
security::SecurityPolicy,
@@ -22,7 +22,7 @@ pub struct ConnectedStateBootstrap {
pub metadata: TunnelMetadata,
pub tunnel_events: mpsc::UnboundedReceiver<TunnelEvent>,
pub tunnel_parameters: TunnelParameters,
- pub tunnel_close_event: oneshot::Receiver<()>,
+ pub tunnel_close_event: oneshot::Receiver<Option<BlockReason>>,
pub close_handle: CloseHandle,
}
@@ -31,7 +31,7 @@ pub struct ConnectedState {
metadata: TunnelMetadata,
tunnel_events: mpsc::UnboundedReceiver<TunnelEvent>,
tunnel_parameters: TunnelParameters,
- tunnel_close_event: oneshot::Receiver<()>,
+ tunnel_close_event: oneshot::Receiver<Option<BlockReason>>,
close_handle: CloseHandle,
}
@@ -170,7 +170,11 @@ impl ConnectedState {
use self::EventConsequence::*;
match self.tunnel_close_event.poll() {
- Ok(Async::Ready(_)) => {}
+ Ok(Async::Ready(block_reason)) => {
+ if let Some(reason) = block_reason {
+ return NewState(BlockedState::enter(shared_values, reason));
+ }
+ }
Ok(Async::NotReady) => return NoEvents(self),
Err(_cancelled) => log::warn!("Tunnel monitor thread has stopped unexpectedly"),
}
diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs
index 20fdb249ff..a549145e90 100644
--- a/talpid-core/src/tunnel_state_machine/connecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs
@@ -54,7 +54,7 @@ error_chain! {
pub struct ConnectingState {
tunnel_events: mpsc::UnboundedReceiver<TunnelEvent>,
tunnel_parameters: TunnelParameters,
- tunnel_close_event: oneshot::Receiver<()>,
+ tunnel_close_event: oneshot::Receiver<Option<BlockReason>>,
close_handle: CloseHandle,
retry_attempt: u32,
}
@@ -124,7 +124,7 @@ impl ConnectingState {
&parameters.options,
TUNNEL_INTERFACE_ALIAS.to_owned().map(OsString::from),
&parameters.username,
- log_file.as_ref().map(PathBuf::as_path),
+ log_file.clone(),
resource_dir,
on_tunnel_event,
)?)
@@ -147,25 +147,23 @@ impl ConnectingState {
}
}
- fn spawn_tunnel_monitor_wait_thread(tunnel_monitor: TunnelMonitor) -> oneshot::Receiver<()> {
+ fn spawn_tunnel_monitor_wait_thread(
+ tunnel_monitor: TunnelMonitor,
+ ) -> oneshot::Receiver<Option<BlockReason>> {
let (tunnel_close_event_tx, tunnel_close_event_rx) = oneshot::channel();
thread::spawn(move || {
let start = Instant::now();
- match tunnel_monitor.wait() {
- Ok(_) => debug!("Tunnel has finished without errors"),
- Err(error) => {
- let chained_error = error.chain_err(|| "Tunnel has stopped unexpectedly");
- warn!("{}", chained_error.display_chain());
- }
- }
+ let block_reason = Self::wait_for_tunnel_monitor(tunnel_monitor);
- if let Some(remaining_time) = MIN_TUNNEL_ALIVE_TIME.checked_sub(start.elapsed()) {
- thread::sleep(remaining_time);
+ if block_reason.is_none() {
+ if let Some(remaining_time) = MIN_TUNNEL_ALIVE_TIME.checked_sub(start.elapsed()) {
+ thread::sleep(remaining_time);
+ }
}
- if tunnel_close_event_tx.send(()).is_err() {
+ if tunnel_close_event_tx.send(block_reason).is_err() {
warn!("Tunnel state machine stopped before receiving tunnel closed event");
}
@@ -175,6 +173,39 @@ impl ConnectingState {
tunnel_close_event_rx
}
+ fn wait_for_tunnel_monitor(tunnel_monitor: TunnelMonitor) -> Option<BlockReason> {
+ match tunnel_monitor.wait() {
+ Ok(_) => {
+ debug!("Tunnel has finished without errors");
+ None
+ }
+ Err(error) => match error {
+ #[cfg(windows)]
+ error @ tunnel::Error(
+ tunnel::ErrorKind::OpenVpnTunnelMonitoringError(
+ tunnel::openvpn::ErrorKind::DisabledTapAdapter,
+ ),
+ _,
+ )
+ | error @ tunnel::Error(
+ tunnel::ErrorKind::OpenVpnTunnelMonitoringError(
+ tunnel::openvpn::ErrorKind::MissingTapAdapter,
+ ),
+ _,
+ ) => {
+ let chained_error = error.chain_err(|| "TAP adapter problem detected");
+ warn!("{}", chained_error.display_chain());
+ Some(BlockReason::TapAdapterProblem)
+ }
+ error => {
+ let chained_error = error.chain_err(|| "Tunnel has stopped unexpectedly");
+ warn!("{}", chained_error.display_chain());
+ None
+ }
+ },
+ }
+ }
+
fn into_connected_state_bootstrap(self, metadata: TunnelMetadata) -> ConnectedStateBootstrap {
ConnectedStateBootstrap {
metadata,
@@ -300,7 +331,11 @@ impl ConnectingState {
shared_values: &mut SharedTunnelStateValues,
) -> EventConsequence<Self> {
match self.tunnel_close_event.poll() {
- Ok(Async::Ready(_)) => {}
+ Ok(Async::Ready(block_reason)) => {
+ if let Some(reason) = block_reason {
+ return EventConsequence::NewState(BlockedState::enter(shared_values, reason));
+ }
+ }
Ok(Async::NotReady) => return EventConsequence::NoEvents(self),
Err(_cancelled) => warn!("Tunnel monitor thread has stopped unexpectedly"),
}
diff --git a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
index ec8be8cfe1..6f470fe370 100644
--- a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
@@ -16,7 +16,7 @@ use crate::tunnel::CloseHandle;
/// This state is active from when we manually trigger a tunnel kill until the tunnel wait
/// operation (TunnelExit) returned.
pub struct DisconnectingState {
- exited: oneshot::Receiver<()>,
+ exited: oneshot::Receiver<Option<BlockReason>>,
after_disconnect: AfterDisconnect,
}
@@ -103,14 +103,22 @@ impl DisconnectingState {
match self.exited.poll() {
Ok(Async::NotReady) => NoEvents(self),
- Ok(Async::Ready(_)) | Err(_) => NewState(self.after_disconnect(shared_values)),
+ Ok(Async::Ready(block_reason)) => {
+ NewState(self.after_disconnect(block_reason, shared_values))
+ }
+ Err(_) => NewState(self.after_disconnect(None, shared_values)),
}
}
fn after_disconnect(
self,
+ block_reason: Option<BlockReason>,
shared_values: &mut SharedTunnelStateValues,
) -> (TunnelStateWrapper, TunnelStateTransition) {
+ if let Some(reason) = block_reason {
+ return BlockedState::enter(shared_values, reason);
+ }
+
match self.after_disconnect {
AfterDisconnect::Nothing => DisconnectedState::enter(shared_values, ()),
AfterDisconnect::Block(reason) => BlockedState::enter(shared_values, reason),
@@ -122,7 +130,11 @@ impl DisconnectingState {
}
impl TunnelState for DisconnectingState {
- type Bootstrap = (CloseHandle, oneshot::Receiver<()>, AfterDisconnect);
+ type Bootstrap = (
+ CloseHandle,
+ oneshot::Receiver<Option<BlockReason>>,
+ AfterDisconnect,
+ );
fn enter(
_: &mut SharedTunnelStateValues,
diff --git a/talpid-types/src/tunnel.rs b/talpid-types/src/tunnel.rs
index f3b28e8bfe..613204ab19 100644
--- a/talpid-types/src/tunnel.rs
+++ b/talpid-types/src/tunnel.rs
@@ -56,6 +56,8 @@ pub enum BlockReason {
NoMatchingRelay,
/// This device is offline, no tunnels can be established.
IsOffline,
+ /// A problem with the TAP adapter has been detected.
+ TapAdapterProblem,
}
impl fmt::Display for BlockReason {
@@ -78,6 +80,7 @@ impl fmt::Display for BlockReason {
StartTunnelError => "Failed to start connection to remote server",
NoMatchingRelay => "No relay server matches the current settings",
IsOffline => "This device is offline, no tunnels can be established",
+ TapAdapterProblem => "A problem with the TAP adapter has been detected",
};
write!(f, "{}", description)