Files
firezone/rust/connlib/shared/src/error.rs
Gabi 6e77978da7 Fix/lost connection request (#2976)
Should fix #2880

The way I do it is after ~10 seconds dropping the
`gateway_awaiting_connection` and let the client try the connection
again, depending on upper layer, I think this is fine since the cases
where this happens is unlikely.

It's hard to test thoroughly but I'll test with bad-condition
simulators, [pumba](https://github.com/alexei-led/pumba) seems
promising. In the meantime I'm still creating the PR so that I can have
it reviewed.

Edit: Using Pumba with different % of packet loss things seems to go
well, and connections are actually established even if the packets are
loss. (Making a note that we should integrate pumba with our CI)
2023-12-21 20:01:52 +00:00

218 lines
7.5 KiB
Rust

//! Error module.
use base64::{DecodeError, DecodeSliceError};
use boringtun::noise::errors::WireGuardError;
use thiserror::Error;
/// Unified Result type to use across connlib.
pub type Result<T> = std::result::Result<T, ConnlibError>;
/// Unified error type to use across connlib.
#[derive(Error, Debug)]
pub enum ConnlibError {
/// Standard IO error.
#[error(transparent)]
Io(#[from] std::io::Error),
/// Standard IO error.
#[error("Failed to roll over log file: {0}")]
LogFileRollError(std::io::Error),
/// Error while decoding a base64 value.
#[error("There was an error while decoding a base64 value: {0}")]
Base64DecodeError(#[from] DecodeError),
/// Error while decoding a base64 value from a slice.
#[error("There was an error while decoding a base64 value: {0}")]
Base64DecodeSliceError(#[from] DecodeSliceError),
/// Request error for websocket connection.
#[error("Error forming request: {0}")]
RequestError(#[from] tokio_tungstenite::tungstenite::http::Error),
/// Websocket heartbeat timedout
#[error("Websocket heartbeat timedout")]
WebsocketTimeout(#[from] tokio_stream::Elapsed),
/// Error during websocket connection.
#[error("Portal connection error: {0}")]
PortalConnectionError(#[from] tokio_tungstenite::tungstenite::error::Error),
/// Provided string was not formatted as a URL.
#[error("Badly formatted URI")]
UriError,
/// Provided an unsupported uri string.
#[error("Unsupported URI scheme: Must be http://, https://, ws:// or wss://")]
UriScheme,
/// Serde's serialize error.
#[error(transparent)]
SerializeError(#[from] serde_json::Error),
/// Webrtc error
#[error("ICE-related error: {0}")]
IceError(#[from] webrtc::Error),
/// Webrtc error regarding data channel.
#[error("ICE-data error: {0}")]
IceDataError(#[from] webrtc::data::Error),
/// Error while sending through an async channelchannel.
#[error("Error sending message through an async channel")]
SendChannelError,
/// Error when trying to establish connection between peers.
#[error("Error while establishing connection between peers")]
ConnectionEstablishError,
/// Error related to wireguard protocol.
#[error("Wireguard error")]
WireguardError(WireGuardError),
/// Expected an initialized runtime but there was none.
#[error("Expected runtime to be initialized")]
NoRuntime,
/// Tried to access a resource which didn't exists.
#[error("Tried to access an undefined resource")]
UnknownResource,
/// One of the stored resources isn't a valid CIDR/DNS.
#[error("Invalid resource")]
InvalidResource,
/// Error regarding our own control protocol.
#[error("Control plane protocol error. Unexpected messages or message order.")]
ControlProtocolError,
/// Error when reading system's interface
#[error("Error while reading system's interface")]
IfaceRead(std::io::Error),
#[error("`on_set_interface_config` failed: {0}")]
OnSetInterfaceConfigFailed(String),
#[error("`on_tunnel_ready` failed: {0}")]
OnTunnelReadyFailed(String),
#[error("`on_add_route` failed: {0}")]
OnAddRouteFailed(String),
#[error("`on_remove_route` failed: {0}")]
OnRemoveRouteFailed(String),
#[error("`on_update_resources` failed: {0}")]
OnUpdateResourcesFailed(String),
#[error("`get_system_default_resolvers` failed: {0}")]
GetSystemDefaultResolverFailed(String),
/// Glob for errors without a type.
#[error("Other error: {0}")]
Other(&'static str),
/// Invalid tunnel name
#[error("Invalid tunnel name")]
InvalidTunnelName,
#[cfg(target_os = "linux")]
#[error(transparent)]
NetlinkError(rtnetlink::Error),
/// Io translation of netlink error
/// The IO version is easier to interpret
/// We maintain a different variant from the standard IO for this to keep more context
#[error("IO netlink error: {0}")]
NetlinkErrorIo(std::io::Error),
/// No iface found
#[error("No iface found")]
NoIface,
/// Expected file descriptor and none was found
#[error("No filedescriptor")]
NoFd,
/// No MTU found
#[error("No MTU found")]
NoMtu,
/// A panic occurred.
#[error("Panicked: {0}")]
Panic(String),
/// A panic occurred with a non-string payload.
#[error("Panicked with a non-string payload")]
PanicNonStringPayload,
/// Received connection details that might be stale
#[error("Unexpected connection details")]
UnexpectedConnectionDetails,
/// Invalid phoenix channel reference
#[error("Invalid phoenix channel reply reference")]
InvalidReference,
/// Invalid packet format
#[error("Received badly formatted packet")]
BadPacket,
/// Tunnel is under load
#[error("Under load")]
UnderLoad,
/// Invalid source address for peer
#[error("Invalid source address")]
InvalidSource,
/// Invalid destination for packet
#[error("Invalid dest address")]
InvalidDst,
/// Any parse error
#[error("parse error")]
ParseError,
/// DNS lookup error
#[error("Error with the DNS fallback lookup")]
DNSFallback(#[from] hickory_resolver::error::ResolveError),
#[error("Error with the DNS fallback lookup")]
DNSFallbackKind(#[from] hickory_resolver::error::ResolveErrorKind),
#[error("DNS proto error")]
DnsProtoError(#[from] hickory_resolver::proto::error::ProtoError),
/// Connection is still being stablished, retry later
#[error("Pending connection")]
PendingConnection,
#[error(transparent)]
Uuid(#[from] uuid::Error),
#[cfg(target_os = "windows")]
#[error(transparent)]
Wintun(#[from] wintun::Error),
#[error("Token has expired")]
TokenExpired,
#[error("Too many concurrent gateway connection requests")]
TooManyConnectionRequests,
}
impl ConnlibError {
pub fn is_http_client_error(&self) -> bool {
matches!(
self,
Self::PortalConnectionError(tokio_tungstenite::tungstenite::error::Error::Http(e))
if e.status().is_client_error()
)
}
/// Whether this error is fatal to the underlying connection.
pub fn is_fatal_connection_error(&self) -> bool {
if let Self::WireguardError(e) = self {
return matches!(
e,
WireGuardError::ConnectionExpired | WireGuardError::NoCurrentSession
);
}
if let Self::IceDataError(e) = self {
return matches!(
e,
webrtc::data::Error::ErrStreamClosed
| webrtc::data::Error::Sctp(webrtc::sctp::Error::ErrStreamClosed)
);
}
false
}
}
#[cfg(target_os = "linux")]
impl From<rtnetlink::Error> for ConnlibError {
fn from(err: rtnetlink::Error) -> Self {
match err {
rtnetlink::Error::NetlinkError(err) => Self::NetlinkErrorIo(err.to_io()),
err => Self::NetlinkError(err),
}
}
}
impl From<WireGuardError> for ConnlibError {
fn from(e: WireGuardError) -> Self {
ConnlibError::WireguardError(e)
}
}
impl From<&'static str> for ConnlibError {
fn from(e: &'static str) -> Self {
ConnlibError::Other(e)
}
}
impl<T> From<tokio::sync::mpsc::error::SendError<T>> for ConnlibError {
fn from(_: tokio::sync::mpsc::error::SendError<T>) -> Self {
ConnlibError::SendChannelError
}
}
impl From<futures::channel::mpsc::SendError> for ConnlibError {
fn from(_: futures::channel::mpsc::SendError) -> Self {
ConnlibError::SendChannelError
}
}