mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
fix(rust): introduce dedicated downcast functions for anyhow (#10966)
The downcasting abilities of `anyhow` are pretty powerful. Unfortunately, they can also be a bit tricky to get right. Whilst `is` and `downcast` work fine for any errors that are within the `anyhow` error chain, they don't check the chain of errors prior to that. In other words, if we already have a nested `std::error::Error` with several causes, `anyhow` cannot downcast to these causes directly. In order to avoid this footgun, we create a thin-layer on top of the `anyhow` crate with some downcasting functions that always try to do the right thing.
This commit is contained in:
58
rust/Cargo.lock
generated
58
rust/Cargo.lock
generated
@@ -311,6 +311,14 @@ version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow-ext"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
@@ -1293,7 +1301,7 @@ name = "client-ffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"android_log-sys",
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"backoff",
|
||||
"client-shared",
|
||||
"connlib-model",
|
||||
@@ -1327,7 +1335,7 @@ dependencies = [
|
||||
name = "client-shared"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"backoff",
|
||||
"bimap",
|
||||
"chrono",
|
||||
@@ -2043,7 +2051,7 @@ dependencies = [
|
||||
name = "dns-over-tcp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"dns-types",
|
||||
"firezone-bin-shared",
|
||||
"firezone-logging",
|
||||
@@ -2352,7 +2360,7 @@ dependencies = [
|
||||
name = "firezone-bin-shared"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"atomicwrites",
|
||||
"axum",
|
||||
"bufferpool",
|
||||
@@ -2405,7 +2413,7 @@ dependencies = [
|
||||
name = "firezone-cli"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"clap",
|
||||
"nix 0.30.1",
|
||||
"rpassword",
|
||||
@@ -2418,7 +2426,7 @@ dependencies = [
|
||||
name = "firezone-gateway"
|
||||
version = "1.4.19"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"backoff",
|
||||
"boringtun",
|
||||
"caps",
|
||||
@@ -2468,7 +2476,7 @@ name = "firezone-gui-client"
|
||||
version = "1.5.9"
|
||||
dependencies = [
|
||||
"admx-macro",
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"arboard",
|
||||
"atomicwrites",
|
||||
"backoff",
|
||||
@@ -2536,7 +2544,7 @@ dependencies = [
|
||||
name = "firezone-headless-client"
|
||||
version = "1.5.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"backoff",
|
||||
"clap",
|
||||
"client-shared",
|
||||
@@ -2571,7 +2579,7 @@ dependencies = [
|
||||
name = "firezone-logging"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"firezone-telemetry",
|
||||
"nu-ansi-term",
|
||||
"output_vt100",
|
||||
@@ -2591,7 +2599,7 @@ dependencies = [
|
||||
name = "firezone-relay"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"axum",
|
||||
"aya",
|
||||
"aya-build",
|
||||
@@ -2643,7 +2651,7 @@ dependencies = [
|
||||
name = "firezone-telemetry"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"flume",
|
||||
"futures",
|
||||
"hex",
|
||||
@@ -2668,7 +2676,7 @@ dependencies = [
|
||||
name = "firezone-tunnel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"base64 0.22.1",
|
||||
"bimap",
|
||||
"boringtun",
|
||||
@@ -3293,7 +3301,7 @@ dependencies = [
|
||||
name = "gui-smoke-test"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"clap",
|
||||
"subprocess",
|
||||
"tracing",
|
||||
@@ -3554,7 +3562,7 @@ dependencies = [
|
||||
name = "http-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"bytes",
|
||||
"futures",
|
||||
"http 1.3.1",
|
||||
@@ -3576,7 +3584,7 @@ dependencies = [
|
||||
name = "http-test-server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"axum",
|
||||
"futures",
|
||||
"serde",
|
||||
@@ -3908,7 +3916,7 @@ dependencies = [
|
||||
name = "ip-packet"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"arbitrary",
|
||||
"bufferpool",
|
||||
"etherparse",
|
||||
@@ -4164,7 +4172,7 @@ dependencies = [
|
||||
name = "l3-tcp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"ip-packet",
|
||||
"smoltcp",
|
||||
"tracing",
|
||||
@@ -4174,7 +4182,7 @@ dependencies = [
|
||||
name = "l3-udp-dns-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"dns-types",
|
||||
"ip-packet",
|
||||
"rand 0.8.5",
|
||||
@@ -4185,7 +4193,7 @@ dependencies = [
|
||||
name = "l4-tcp-dns-server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"dns-types",
|
||||
"futures",
|
||||
"tokio",
|
||||
@@ -4196,7 +4204,7 @@ dependencies = [
|
||||
name = "l4-udp-dns-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"dns-types",
|
||||
"futures",
|
||||
"socket-factory",
|
||||
@@ -4208,7 +4216,7 @@ dependencies = [
|
||||
name = "l4-udp-dns-server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"dns-types",
|
||||
"futures",
|
||||
"tokio",
|
||||
@@ -5492,7 +5500,7 @@ dependencies = [
|
||||
name = "phoenix-channel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"backoff",
|
||||
"base64 0.22.1",
|
||||
"firezone-logging",
|
||||
@@ -7072,7 +7080,7 @@ dependencies = [
|
||||
name = "snownet"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"bnum",
|
||||
"boringtun",
|
||||
"bufferpool",
|
||||
@@ -7099,7 +7107,7 @@ dependencies = [
|
||||
name = "socket-factory"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"bufferpool",
|
||||
"bytes",
|
||||
"derive_more 2.0.1",
|
||||
@@ -8533,7 +8541,7 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
name = "tun"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anyhow-ext",
|
||||
"ip-packet",
|
||||
"libc",
|
||||
"tokio",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"anyhow-ext",
|
||||
"bin-shared",
|
||||
"cli",
|
||||
"client-ffi",
|
||||
@@ -44,7 +45,7 @@ edition = "2024"
|
||||
|
||||
[workspace.dependencies]
|
||||
admx-macro = { path = "gui-client/src-admx-macro" }
|
||||
anyhow = "1.0.99"
|
||||
anyhow = { package = "anyhow-ext", path = "anyhow-ext" }
|
||||
arbitrary = "1.4.2"
|
||||
arboard = { version = "3.6.1", default-features = false }
|
||||
async-trait = { version = "0.1", default-features = false }
|
||||
|
||||
14
rust/anyhow-ext/Cargo.toml
Normal file
14
rust/anyhow-ext/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "anyhow-ext"
|
||||
version = "0.1.0"
|
||||
edition = { workspace = true }
|
||||
license = { workspace = true }
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.99"
|
||||
|
||||
[dev-dependencies]
|
||||
thiserror = { workspace = true }
|
||||
152
rust/anyhow-ext/lib.rs
Normal file
152
rust/anyhow-ext/lib.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
pub use anyhow::*;
|
||||
|
||||
pub trait ErrorExt {
|
||||
fn any_is<T>(&self) -> bool
|
||||
where
|
||||
T: std::error::Error + Send + Sync + 'static;
|
||||
fn any_downcast_ref<T>(&self) -> Option<&T>
|
||||
where
|
||||
T: std::error::Error + Send + Sync + 'static;
|
||||
}
|
||||
|
||||
impl ErrorExt for anyhow::Error {
|
||||
#[expect(
|
||||
clippy::disallowed_methods,
|
||||
reason = "We are implementing the alternative."
|
||||
)]
|
||||
fn any_is<T>(&self) -> bool
|
||||
where
|
||||
T: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
self.is::<T>() || self.chain().any(|e| e.is::<T>())
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::disallowed_methods,
|
||||
reason = "We are implementing the alternative."
|
||||
)]
|
||||
fn any_downcast_ref<T>(&self) -> Option<&T>
|
||||
where
|
||||
T: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
std::iter::empty()
|
||||
.chain(self.downcast_ref::<T>())
|
||||
.chain(self.chain().flat_map(|e| e.downcast_ref::<T>()))
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn any_is_works_for_context() {
|
||||
let error = Result::<(), _>::Err(io::Error::other("Test"))
|
||||
.context("Foobar")
|
||||
.unwrap_err();
|
||||
|
||||
assert!(error.any_is::<io::Error>())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn any_is_works_for_typed_context() {
|
||||
let error = Result::<(), _>::Err(io::Error::other("Test"))
|
||||
.context(FooError)
|
||||
.unwrap_err();
|
||||
|
||||
assert!(error.any_is::<FooError>())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn any_is_works_for_anyhow_new() {
|
||||
let error = Result::<(), _>::Err(anyhow::Error::new(io::Error::other("Test")))
|
||||
.context("Foobar")
|
||||
.unwrap_err();
|
||||
|
||||
assert!(error.any_is::<io::Error>())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn any_is_works_for_custom_error_with_source() {
|
||||
let error = Result::<(), _>::Err(BazError(BarError(FooError)))
|
||||
.context("Foobar")
|
||||
.unwrap_err();
|
||||
|
||||
assert!(error.any_is::<BazError>());
|
||||
assert!(error.any_is::<BarError>());
|
||||
assert!(error.any_is::<FooError>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn any_downcast_ref_works_for_context() {
|
||||
let error = Result::<(), _>::Err(io::Error::other("Test"))
|
||||
.context("Foobar")
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
error.any_downcast_ref::<io::Error>().unwrap().to_string(),
|
||||
"Test"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn any_downcast_ref_works_for_typed() {
|
||||
let error = Result::<(), _>::Err(io::Error::other("Test"))
|
||||
.context(FooError)
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
error.any_downcast_ref::<FooError>().unwrap().to_string(),
|
||||
"Foo"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn any_downcast_ref_works_for_anyhow_new() {
|
||||
let error = Result::<(), _>::Err(anyhow::Error::new(io::Error::other("Test")))
|
||||
.context("Foobar")
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
error.any_downcast_ref::<io::Error>().unwrap().to_string(),
|
||||
"Test"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn any_downcast_ref_works_for_custom_error_with_source() {
|
||||
let error = Result::<(), _>::Err(BazError(BarError(FooError)))
|
||||
.context("Foobar")
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
error.any_downcast_ref::<BazError>().unwrap().to_string(),
|
||||
"Baz"
|
||||
);
|
||||
assert_eq!(
|
||||
error.any_downcast_ref::<BarError>().unwrap().to_string(),
|
||||
"Bar"
|
||||
);
|
||||
assert_eq!(
|
||||
error.any_downcast_ref::<FooError>().unwrap().to_string(),
|
||||
"Foo"
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("Foo")]
|
||||
struct FooError;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("Bar")]
|
||||
struct BarError(#[from] FooError);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("Baz")]
|
||||
struct BazError(#[from] BarError);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::PHOENIX_TOPIC;
|
||||
use anyhow::{Context as _, Result};
|
||||
use anyhow::{Context as _, ErrorExt as _, Result};
|
||||
use connlib_model::{PublicKey, ResourceView};
|
||||
use firezone_tunnel::messages::RelaysPresence;
|
||||
use firezone_tunnel::messages::client::{
|
||||
@@ -88,7 +88,7 @@ impl From<anyhow::Error> for DisconnectError {
|
||||
|
||||
impl DisconnectError {
|
||||
pub fn is_authentication_error(&self) -> bool {
|
||||
let Some(e) = self.0.downcast_ref::<phoenix_channel::Error>() else {
|
||||
let Some(e) = self.0.any_downcast_ref::<phoenix_channel::Error>() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -308,8 +308,7 @@ impl Eventloop {
|
||||
|
||||
fn handle_tunnel_error(&mut self, mut e: TunnelError) -> Result<()> {
|
||||
for e in e.drain() {
|
||||
if e.root_cause()
|
||||
.downcast_ref::<io::Error>()
|
||||
if e.any_downcast_ref::<io::Error>()
|
||||
.is_some_and(is_unreachable)
|
||||
{
|
||||
tracing::debug!("{e:#}"); // Log these on DEBUG so they don't go completely unnoticed.
|
||||
@@ -317,16 +316,14 @@ impl Eventloop {
|
||||
}
|
||||
|
||||
// Invalid Input can be all sorts of things but we mostly see it with unreachable addresses.
|
||||
if e.root_cause()
|
||||
.downcast_ref::<io::Error>()
|
||||
if e.any_downcast_ref::<io::Error>()
|
||||
.is_some_and(|e| e.kind() == io::ErrorKind::InvalidInput)
|
||||
{
|
||||
tracing::debug!("{e:#}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if e.root_cause()
|
||||
.downcast_ref::<io::Error>()
|
||||
if e.any_downcast_ref::<io::Error>()
|
||||
.is_some_and(|e| e.kind() == io::ErrorKind::PermissionDenied)
|
||||
{
|
||||
if !mem::replace(&mut self.logged_permission_denied, true) {
|
||||
@@ -338,9 +335,7 @@ impl Eventloop {
|
||||
continue;
|
||||
}
|
||||
|
||||
if e.root_cause()
|
||||
.is::<firezone_tunnel::UdpSocketThreadStopped>()
|
||||
{
|
||||
if e.any_is::<firezone_tunnel::UdpSocketThreadStopped>() {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,4 +6,9 @@ disallowed-methods = [
|
||||
{ path = "std::collections::HashSet::iter", reason = "HashSet has non-deterministic iteration order, use BTreeSet instead" },
|
||||
{ path = "std::collections::HashMap::iter_mut", reason = "HashMap has non-deterministic iteration order, use BTreeMap instead" },
|
||||
{ path = "tracing::subscriber::set_global_default", reason = "Does not init `LogTracer`, use `firezone_logging::init` instead." },
|
||||
{ path = "anyhow::Error::is", reason = "Does not check entire source chain, use `any_is` instead" },
|
||||
{ path = "anyhow::Error::downcast_ref", reason = "Does not check entire source chain, use `any_downcast_ref` instead" },
|
||||
{ path = "anyhow::Error::downcast_mut", reason = "Does not check entire source chain, use `any_downcast_ref` instead" },
|
||||
{ path = "anyhow::Error::downcast", reason = "Does not check entire source chain, use `any_downcast_ref` instead" },
|
||||
{ path = "anyhow::Error::root_cause", reason = "Does not check entire source chain, use `any_downcast_ref` instead" },
|
||||
]
|
||||
|
||||
@@ -484,6 +484,7 @@ mod tests {
|
||||
(id, idx, key)
|
||||
}
|
||||
|
||||
#[expect(clippy::disallowed_methods, reason = "This is a test.")]
|
||||
fn assert_disconnected(
|
||||
connections: &mut Connections<u32, u32>,
|
||||
id: u32,
|
||||
|
||||
@@ -518,8 +518,9 @@ fn is_equal_modulo_scope_for_ipv6_link_local(expected: SocketAddr, actual: Socke
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn is_os_error_5(e: &anyhow::Error) -> bool {
|
||||
e.root_cause()
|
||||
.downcast_ref::<io::Error>()
|
||||
use anyhow::ErrorExt;
|
||||
|
||||
e.any_downcast_ref::<io::Error>()
|
||||
.and_then(|e| e.raw_os_error())
|
||||
.is_some_and(|c| c == libc::EIO)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use anyhow::{Context as _, ErrorExt, Result, bail};
|
||||
use ip_packet::{IpPacket, IpPacketBuf};
|
||||
use std::io;
|
||||
use std::os::fd::AsRawFd;
|
||||
@@ -80,7 +80,7 @@ where
|
||||
break;
|
||||
};
|
||||
}
|
||||
Err(e) if e.root_cause().is::<ip_packet::Fragmented>() => {
|
||||
Err(e) if e.any_is::<ip_packet::Fragmented>() => {
|
||||
tracing::debug!("{e:#}"); // Log on debug to be less noisy.
|
||||
continue;
|
||||
}
|
||||
@@ -109,6 +109,6 @@ mod tests {
|
||||
|
||||
let final_error = anyhow::Error::new(io_error).context("Failed to read from TUN fd");
|
||||
|
||||
assert!(final_error.root_cause().is::<ip_packet::Fragmented>())
|
||||
assert!(final_error.any_is::<ip_packet::Fragmented>())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ use crate::messages::{IceCredentials, SecretKey};
|
||||
use crate::peer_store::PeerStore;
|
||||
use crate::unique_packet_buffer::UniquePacketBuffer;
|
||||
use crate::{IPV4_TUNNEL, IPV6_TUNNEL, IpConfig, TunConfig, dns, is_peer, p2p_control};
|
||||
use anyhow::Context;
|
||||
use anyhow::{Context, ErrorExt};
|
||||
use connlib_model::{
|
||||
GatewayId, IceCandidate, PublicKey, RelayId, ResourceId, ResourceStatus, ResourceView,
|
||||
};
|
||||
@@ -520,7 +520,7 @@ impl ClientState {
|
||||
}
|
||||
Err(e)
|
||||
if response.transport == dns::Transport::Udp
|
||||
&& e.downcast_ref::<io::Error>()
|
||||
&& e.any_downcast_ref::<io::Error>()
|
||||
.is_some_and(|e| e.kind() == io::ErrorKind::TimedOut) =>
|
||||
{
|
||||
tracing::debug!("Recursive UDP DNS query timed out");
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::messages::gateway::{Client, ResourceDescription, Subject};
|
||||
use crate::messages::{Answer, IceCredentials, ResolveRequest};
|
||||
use crate::peer_store::PeerStore;
|
||||
use crate::{FailedToDecapsulate, GatewayEvent, IpConfig, p2p_control, packet_kind};
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{Context, ErrorExt, Result};
|
||||
use boringtun::x25519::{self, PublicKey};
|
||||
use chrono::{DateTime, Utc};
|
||||
use connlib_model::{ClientId, IceCandidate, RelayId, ResourceId};
|
||||
@@ -133,7 +133,7 @@ impl GatewayState {
|
||||
let encrypted_packet = match self.node.encapsulate(cid, &packet, now) {
|
||||
Ok(Some(encrypted_packet)) => encrypted_packet,
|
||||
Ok(None) => return Ok(None),
|
||||
Err(e) if e.is::<snownet::UnknownConnection>() => {
|
||||
Err(e) if e.any_is::<snownet::UnknownConnection>() => {
|
||||
return Err(e.context(UnroutablePacket::not_connected(&packet)));
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
|
||||
@@ -1024,6 +1024,7 @@ mod tests {
|
||||
now += nat_table::UDP_TTL;
|
||||
peer.handle_timeout(now);
|
||||
|
||||
#[expect(clippy::disallowed_methods, reason = "This is a test.")]
|
||||
let err = peer
|
||||
.translate_inbound(response, now)
|
||||
.unwrap_err()
|
||||
|
||||
@@ -5,7 +5,7 @@ mod tcp_dns;
|
||||
mod udp_dns;
|
||||
|
||||
use crate::{TunnelError, device_channel::Device, dns, otel, sockets::Sockets};
|
||||
use anyhow::{Context as _, Result};
|
||||
use anyhow::{Context as _, ErrorExt, Result};
|
||||
use chrono::{DateTime, Utc};
|
||||
use dns_types::DoHUrl;
|
||||
use futures::FutureExt as _;
|
||||
@@ -361,7 +361,7 @@ impl Io {
|
||||
if let Poll::Ready(response) = &dns_response
|
||||
&& let dns::Upstream::DoH { server } = &response.server
|
||||
&& let Err(e) = &response.message
|
||||
&& e.is::<http_client::Closed>()
|
||||
&& e.any_is::<http_client::Closed>()
|
||||
{
|
||||
tracing::debug!(%server, "Connection of DoH client failed");
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#![cfg_attr(test, allow(clippy::unwrap_used))]
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use anyhow::{Context as _, ErrorExt as _, Result};
|
||||
use chrono::Utc;
|
||||
use connlib_model::{ClientId, GatewayId, IceCandidate, PublicKey, ResourceId, ResourceView};
|
||||
use dns_types::DomainName;
|
||||
@@ -448,7 +448,7 @@ impl GatewayTunnel {
|
||||
}
|
||||
Err(e) => {
|
||||
let routing_error = e
|
||||
.downcast_ref::<gateway::UnroutablePacket>()
|
||||
.any_downcast_ref::<gateway::UnroutablePacket>()
|
||||
.map(|e| e.reason())
|
||||
.unwrap_or(gateway::RoutingError::Other);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::otel;
|
||||
use anyhow::{Context as _, Result};
|
||||
use anyhow::{Context as _, ErrorExt as _, Result};
|
||||
use futures::{SinkExt, ready};
|
||||
use gat_lending_iterator::LendingIterator;
|
||||
use socket_factory::DatagramOut;
|
||||
@@ -271,7 +271,7 @@ impl ThreadedUdpSocket {
|
||||
tokio::task::yield_now().await;
|
||||
|
||||
if let Err(e) = socket.send(datagram).await {
|
||||
if let Some(io) = e.downcast_ref::<io::Error>() {
|
||||
if let Some(io) = e.any_downcast_ref::<io::Error>() {
|
||||
io_error_counter.add(
|
||||
1,
|
||||
&[
|
||||
@@ -307,7 +307,7 @@ impl ThreadedUdpSocket {
|
||||
if let Some(io) = result
|
||||
.as_ref()
|
||||
.err()
|
||||
.and_then(|e| e.downcast_ref::<io::Error>())
|
||||
.and_then(|e| e.any_downcast_ref::<io::Error>())
|
||||
{
|
||||
io_error_counter.add(
|
||||
1,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::{Context as _, Result};
|
||||
use anyhow::{Context as _, ErrorExt, Result};
|
||||
use boringtun::x25519::PublicKey;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use dns_lookup::{AddrInfoHints, AddrInfoIter, LookupError};
|
||||
@@ -288,8 +288,7 @@ impl Eventloop {
|
||||
|
||||
fn handle_tunnel_error(&mut self, mut e: TunnelError) -> Result<()> {
|
||||
for e in e.drain() {
|
||||
if e.root_cause()
|
||||
.downcast_ref::<io::Error>()
|
||||
if e.any_downcast_ref::<io::Error>()
|
||||
.is_some_and(is_unreachable)
|
||||
{
|
||||
tracing::debug!("{e:#}"); // Log these on DEBUG so they don't go completely unnoticed.
|
||||
@@ -297,16 +296,14 @@ impl Eventloop {
|
||||
}
|
||||
|
||||
// Invalid Input can be all sorts of things but we mostly see it with unreachable addresses.
|
||||
if e.root_cause()
|
||||
.downcast_ref::<io::Error>()
|
||||
if e.any_downcast_ref::<io::Error>()
|
||||
.is_some_and(|e| e.kind() == io::ErrorKind::InvalidInput)
|
||||
{
|
||||
tracing::debug!("{e:#}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if e.root_cause()
|
||||
.downcast_ref::<io::Error>()
|
||||
if e.any_downcast_ref::<io::Error>()
|
||||
.is_some_and(|e| e.kind() == io::ErrorKind::PermissionDenied)
|
||||
{
|
||||
if !mem::replace(&mut self.logged_permission_denied, true) {
|
||||
@@ -318,20 +315,18 @@ impl Eventloop {
|
||||
continue;
|
||||
}
|
||||
|
||||
if e.root_cause().is::<ip_packet::ImpossibleTranslation>() {
|
||||
if e.any_is::<ip_packet::ImpossibleTranslation>() {
|
||||
// Some IP packets cannot be translated and should be dropped "silently".
|
||||
// Do so by ignoring the error here.
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(e) = e.downcast_ref::<firezone_tunnel::UnroutablePacket>() {
|
||||
if let Some(e) = e.any_downcast_ref::<firezone_tunnel::UnroutablePacket>() {
|
||||
tracing::debug!(src = %e.source(), dst = %e.destination(), proto = %e.proto(), "{e:#}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if e.root_cause()
|
||||
.is::<firezone_tunnel::UdpSocketThreadStopped>()
|
||||
{
|
||||
if e.any_is::<firezone_tunnel::UdpSocketThreadStopped>() {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use anyhow::{Context as _, ErrorExt, Result, bail};
|
||||
use clap::{Args, Parser};
|
||||
use controller::Failure;
|
||||
use firezone_gui_client::{controller, deep_link, elevation, gui, logging, settings};
|
||||
@@ -169,28 +169,25 @@ fn try_main(
|
||||
return Err(anyhow);
|
||||
}
|
||||
|
||||
if anyhow.root_cause().is::<gui::AlreadyRunning>() {
|
||||
if anyhow.any_is::<gui::AlreadyRunning>() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if anyhow.root_cause().is::<gui::NewInstanceHandshakeFailed>() {
|
||||
if anyhow.any_is::<gui::NewInstanceHandshakeFailed>() {
|
||||
show_error_dialog(
|
||||
"Firezone is already running but not responding. Please force-stop it first.",
|
||||
)?;
|
||||
return Err(anyhow);
|
||||
}
|
||||
|
||||
if anyhow
|
||||
.root_cause()
|
||||
.is::<firezone_gui_client::ipc::NotFound>()
|
||||
{
|
||||
if anyhow.any_is::<firezone_gui_client::ipc::NotFound>() {
|
||||
show_error_dialog(
|
||||
"Couldn't find Firezone Tunnel service. Is the service running?",
|
||||
)?;
|
||||
return Err(anyhow);
|
||||
}
|
||||
|
||||
if anyhow.root_cause().is::<controller::FailedToReceiveHello>() {
|
||||
if anyhow.any_is::<controller::FailedToReceiveHello>() {
|
||||
show_error_dialog(
|
||||
"The Firezone Tunnel service is not responding. If the issue persists, contact your administrator.",
|
||||
)?;
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
updates, uptime,
|
||||
view::{GeneralSettingsForm, SessionViewModel},
|
||||
};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use anyhow::{Context, ErrorExt as _, Result, anyhow, bail};
|
||||
use connlib_model::ResourceView;
|
||||
use firezone_logging::FilterReloadHandle;
|
||||
use firezone_telemetry::Telemetry;
|
||||
@@ -669,8 +669,7 @@ impl<I: GuiIntegration> Controller<I> {
|
||||
Ok(()) => {}
|
||||
Err(error)
|
||||
if error
|
||||
.root_cause()
|
||||
.downcast_ref::<auth::Error>()
|
||||
.any_downcast_ref::<auth::Error>()
|
||||
.is_some_and(|e| matches!(e, auth::Error::NoInflightRequest)) =>
|
||||
{
|
||||
tracing::debug!("Ignoring deep-link; no local state");
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
ipc::{self, SocketId},
|
||||
logging,
|
||||
};
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use anyhow::{Context as _, ErrorExt as _, Result, bail};
|
||||
use atomicwrites::{AtomicFile, OverwriteBehavior};
|
||||
use backoff::ExponentialBackoffBuilder;
|
||||
use connlib_model::ResourceView;
|
||||
@@ -399,7 +399,7 @@ impl<'a> Handler<'a> {
|
||||
if let Some(e) = result
|
||||
.as_ref()
|
||||
.err()
|
||||
.and_then(|e| e.root_cause().downcast_ref::<io::Error>())
|
||||
.and_then(|e| e.any_downcast_ref::<io::Error>())
|
||||
{
|
||||
tracing::debug!("Still cannot connect to Firezone: {e}");
|
||||
|
||||
@@ -534,7 +534,7 @@ impl<'a> Handler<'a> {
|
||||
if let Some(e) = result
|
||||
.as_ref()
|
||||
.err()
|
||||
.and_then(|e| e.root_cause().downcast_ref::<io::Error>())
|
||||
.and_then(|e| e.any_downcast_ref::<io::Error>())
|
||||
{
|
||||
tracing::debug!(
|
||||
"Encountered IO error when connecting to portal, most likely we don't have Internet: {e}"
|
||||
|
||||
@@ -55,7 +55,7 @@ aya-log = { workspace = true }
|
||||
ebpf-shared = { workspace = true, features = ["std"] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.build-dependencies]
|
||||
anyhow = "1"
|
||||
anyhow = { workspace = true }
|
||||
aya-build = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
Reference in New Issue
Block a user