use std::io;
use thiserror::Error;
use super::frame::FrameError;
#[derive(Debug, Error)]
pub enum TransportError {
#[error("frame error: {0}")]
Frame(#[from] FrameError),
#[error("i/o error: {0}")]
Io(#[from] io::Error),
#[error("authentication failed")]
AuthenticationFailed,
#[error("unknown session")]
UnknownSession,
#[error("nonce replay detected")]
NonceReplay,
#[error("nonce too old")]
NonceTooOld,
#[error("frame too small")]
FrameTooSmall,
#[error("connection timeout")]
ConnectionTimeout,
#[error("max retransmits exceeded")]
MaxRetransmitsExceeded,
#[error("connection closed")]
ConnectionClosed,
#[error("amplification limit reached")]
AmplificationLimit,
#[error("migration rate limited")]
MigrationRateLimited,
#[error("nonce counter exhaustion - session must be terminated")]
CounterExhaustion,
}
impl TransportError {
pub fn is_silent_drop(&self) -> bool {
matches!(
self,
TransportError::AuthenticationFailed
| TransportError::UnknownSession
| TransportError::NonceReplay
| TransportError::NonceTooOld
| TransportError::FrameTooSmall
)
}
pub fn is_fatal(&self) -> bool {
matches!(
self,
TransportError::ConnectionTimeout
| TransportError::MaxRetransmitsExceeded
| TransportError::ConnectionClosed
| TransportError::CounterExhaustion
)
}
pub fn is_security_error(&self) -> bool {
matches!(
self,
TransportError::AuthenticationFailed
| TransportError::NonceReplay
| TransportError::NonceTooOld
| TransportError::CounterExhaustion
)
}
}
pub type TransportResult<T> = Result<T, TransportError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_silent_drop_errors() {
assert!(TransportError::AuthenticationFailed.is_silent_drop());
assert!(TransportError::UnknownSession.is_silent_drop());
assert!(TransportError::NonceReplay.is_silent_drop());
assert!(TransportError::NonceTooOld.is_silent_drop());
assert!(TransportError::FrameTooSmall.is_silent_drop());
assert!(!TransportError::ConnectionTimeout.is_silent_drop());
assert!(!TransportError::Io(io::Error::new(io::ErrorKind::Other, "test")).is_silent_drop());
}
#[test]
fn test_fatal_errors() {
assert!(TransportError::ConnectionTimeout.is_fatal());
assert!(TransportError::MaxRetransmitsExceeded.is_fatal());
assert!(TransportError::ConnectionClosed.is_fatal());
assert!(TransportError::CounterExhaustion.is_fatal());
assert!(!TransportError::AuthenticationFailed.is_fatal());
assert!(!TransportError::NonceReplay.is_fatal());
}
#[test]
fn test_security_errors() {
assert!(TransportError::AuthenticationFailed.is_security_error());
assert!(TransportError::NonceReplay.is_security_error());
assert!(TransportError::NonceTooOld.is_security_error());
assert!(TransportError::CounterExhaustion.is_security_error());
assert!(!TransportError::ConnectionTimeout.is_security_error());
assert!(!TransportError::AmplificationLimit.is_security_error());
}
}