use std::net::{IpAddr, SocketAddr};
use memberlist_core::transport::{TransportError, Wire};
use nodecraft::resolver::AddressResolver;
#[derive(thiserror::Error)]
pub enum NetTransportError<A: AddressResolver, W: Wire> {
#[error(transparent)]
Connection(#[from] ConnectionError),
#[error("no private IP address found, and explicit IP not provided")]
NoPrivateIP,
#[error("failed to get interface addresses {0}")]
NoInterfaceAddresses(#[from] local_ip_address::Error),
#[error("at least one bind address is required")]
EmptyBindAddresses,
#[error("the ip {0} is blocked")]
BlockedIp(IpAddr),
#[error("failed to resize packet buffer {0}")]
ResizePacketBuffer(std::io::Error),
#[error("failed to start packet listener on {0}: {1}")]
ListenPacket(SocketAddr, std::io::Error),
#[error("failed to start promised listener on {0}: {1}")]
ListenPromised(SocketAddr, std::io::Error),
#[error("failed to resolve address {addr}: {err}")]
Resolve {
addr: A::Address,
err: A::Error,
},
#[error(transparent)]
Label(#[from] super::LabelError),
#[error("checksum mismatch")]
PacketChecksumMismatch,
#[error(transparent)]
UnknownChecksumer(#[from] super::checksum::UnknownChecksumer),
#[error("wire error: {0}")]
Wire(W::Error),
#[error("packet too large, the maximum packet can be sent is 65535, got {0}")]
PacketTooLarge(usize),
#[error("custom error: {0}")]
Custom(std::borrow::Cow<'static, str>),
#[cfg(feature = "compression")]
#[cfg_attr(docsrs, doc(cfg(feature = "compression")))]
#[error("{0}")]
Compressor(#[from] super::compressor::CompressorError),
#[error("{0}")]
#[cfg(feature = "encryption")]
#[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
Security(#[from] super::security::SecurityError),
#[error("computation task panic")]
#[cfg(any(feature = "compression", feature = "encryption"))]
ComputationTaskFailed,
}
#[cfg(feature = "compression")]
const _: () = {
use super::compressor::*;
impl<A: AddressResolver, W: Wire> From<CompressError> for NetTransportError<A, W> {
fn from(err: CompressError) -> Self {
Self::Compressor(err.into())
}
}
impl<A: AddressResolver, W: Wire> From<DecompressError> for NetTransportError<A, W> {
fn from(err: DecompressError) -> Self {
Self::Compressor(err.into())
}
}
impl<A: AddressResolver, W: Wire> From<UnknownCompressor> for NetTransportError<A, W> {
fn from(err: UnknownCompressor) -> Self {
Self::Compressor(err.into())
}
}
};
impl<A: AddressResolver, W: Wire> NetTransportError<A, W> {
#[cfg(feature = "compression")]
pub(crate) fn not_enough_bytes_to_decompress() -> Self {
Self::Compressor(super::compressor::CompressorError::NotEnoughBytes)
}
}
impl<A: AddressResolver, W: Wire> core::fmt::Debug for NetTransportError<A, W> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
core::fmt::Display::fmt(&self, f)
}
}
impl<A: AddressResolver, W: Wire> TransportError for NetTransportError<A, W> {
fn is_remote_failure(&self) -> bool {
if let Self::Connection(e) = self {
e.is_remote_failure()
} else {
false
}
}
fn custom(err: std::borrow::Cow<'static, str>) -> Self {
Self::Custom(err)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum ConnectionKind {
Promised,
Packet,
}
impl core::fmt::Display for ConnectionKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl ConnectionKind {
#[inline]
pub const fn as_str(&self) -> &'static str {
match self {
ConnectionKind::Promised => "promised",
ConnectionKind::Packet => "packet",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum ConnectionErrorKind {
Accept,
Close,
Dial,
Flush,
Read,
Write,
Label,
}
impl core::fmt::Display for ConnectionErrorKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl ConnectionErrorKind {
#[inline]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Accept => "accept",
Self::Read => "read",
Self::Write => "write",
Self::Dial => "dial",
Self::Flush => "flush",
Self::Close => "close",
Self::Label => "label",
}
}
}
#[derive(Debug)]
pub struct ConnectionError {
pub(crate) kind: ConnectionKind,
pub(crate) error_kind: ConnectionErrorKind,
pub(crate) error: std::io::Error,
}
impl core::fmt::Display for ConnectionError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"{} connection {} error {}",
self.kind.as_str(),
self.error_kind.as_str(),
self.error
)
}
}
impl std::error::Error for ConnectionError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.error)
}
}
impl From<ConnectionError> for std::io::Error {
fn from(value: ConnectionError) -> Self {
value.error
}
}
impl ConnectionError {
#[inline]
pub fn is_remote_failure(&self) -> bool {
#[allow(clippy::match_like_matches_macro)]
match self.kind {
ConnectionKind::Promised => match self.error_kind {
ConnectionErrorKind::Read | ConnectionErrorKind::Write | ConnectionErrorKind::Dial => true,
_ => false,
},
ConnectionKind::Packet => match self.error_kind {
ConnectionErrorKind::Read | ConnectionErrorKind::Write => true,
_ => false,
},
}
}
pub(super) fn promised_read(err: std::io::Error) -> Self {
Self {
kind: ConnectionKind::Promised,
error_kind: ConnectionErrorKind::Read,
error: err,
}
}
pub(super) fn promised_write(err: std::io::Error) -> Self {
Self {
kind: ConnectionKind::Promised,
error_kind: ConnectionErrorKind::Write,
error: err,
}
}
pub(super) fn packet_write(err: std::io::Error) -> Self {
Self {
kind: ConnectionKind::Packet,
error_kind: ConnectionErrorKind::Write,
error: err,
}
}
}