Files
tsunagi/src/error.rs
T

192 lines
6.2 KiB
Rust
Raw Normal View History

//! Error types for the whole library.
//!
//! The library never panics on untrusted network input: every decoding and
//! validation failure is represented as a [`ProtocolError`] and surfaced as a
//! rejected message or session, never as a process abort.
use std::path::PathBuf;
use crate::identity::NetworkId;
/// Convenient result alias used across the crate.
pub type Result<T, E = Error> = std::result::Result<T, E>;
/// Top level error type of the library.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
/// The supplied network name does not satisfy the documented rules.
#[error("invalid network name: {0}")]
InvalidNetworkName(&'static str),
/// The supplied network secret does not satisfy the documented rules.
///
/// The secret itself is never included in the message.
#[error("invalid network secret: {0}")]
InvalidNetworkSecret(&'static str),
/// A textual identifier could not be parsed.
#[error("invalid {kind}: {reason}")]
InvalidEncoding {
/// What was being parsed, e.g. `network id`.
kind: &'static str,
/// Why parsing failed.
reason: &'static str,
},
/// The mandatory state directory is already owned by another live agent.
///
/// This is an ownership lock, not a "file exists" check.
#[error("state directory {path} is owned by another running agent instance")]
StateLocked {
/// Directory that could not be locked.
path: PathBuf,
},
/// The mandatory state store is unusable. It is never silently recreated.
#[error("mandatory state store at {path} is unusable and was NOT reset: {reason}")]
StateCorrupted {
/// Path of the unusable store.
path: PathBuf,
/// Human readable reason, free of secrets.
reason: String,
},
/// The mandatory state store has a schema this build cannot handle.
#[error(
"state store schema version {found} is not supported by this build (supported: {supported})"
)]
UnsupportedSchema {
/// Version found on disk.
found: i64,
/// Version this build writes.
supported: i64,
},
/// A storage operation failed.
#[error("storage error: {0}")]
Storage(String),
/// A filesystem operation failed.
#[error("io error at {path}: {source}")]
Io {
/// Path involved in the failure.
path: PathBuf,
/// Underlying error.
#[source]
source: std::io::Error,
},
/// Binding or driving the iroh endpoint failed.
#[error("iroh endpoint error: {0}")]
Endpoint(String),
/// A control protocol violation.
#[error(transparent)]
Protocol(#[from] ProtocolError),
/// The requested network is not currently active on this agent.
#[error("network {0} is not active")]
NetworkNotActive(NetworkId),
/// The requested network is already active on this agent.
#[error("network {0} is already active")]
NetworkAlreadyActive(NetworkId),
/// The requested network is not configured in the state store.
#[error("network {0} is not configured")]
NetworkUnknown(NetworkId),
/// No session with that peer exists in the given network.
#[error("no authenticated session with peer {peer} in network {network}")]
NoSuchPeer {
/// Network the lookup was scoped to.
network: NetworkId,
/// Peer that was looked up, short form.
peer: String,
},
/// The agent is shutting down or already stopped.
#[error("agent is stopped")]
Stopped,
/// Discovery backend failure. Never fatal for the agent.
#[error("discovery error: {0}")]
Discovery(String),
}
/// Errors produced while speaking the control protocol.
///
/// These always result in rejecting a single message or a single session. They
/// never stop other networks, other peers, or the agent itself.
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum ProtocolError {
/// The peer announced an unsupported control protocol version.
#[error("unsupported control protocol version {found} (this build speaks {supported})")]
UnsupportedVersion {
/// Version announced by the peer.
found: u16,
/// Version this build speaks.
supported: u16,
},
/// A frame header announced more bytes than the configured limit allows.
///
/// Checked *before* any buffer of that size is allocated.
#[error("frame of {announced} bytes exceeds the {limit} byte limit")]
FrameTooLarge {
/// Length announced in the frame header.
announced: u64,
/// Configured limit.
limit: usize,
},
/// A frame could not be decoded.
#[error("malformed frame: {0}")]
Malformed(&'static str),
/// The stream ended before a complete frame was read.
#[error("stream closed while reading a frame")]
StreamClosed,
/// An underlying stream read or write failed.
#[error("stream error: {0}")]
Stream(String),
/// The peer failed to prove knowledge of the derived network secret.
#[error("network authentication failed")]
AuthenticationFailed,
/// The peer asked for a network this agent does not have active.
#[error("peer requested an unknown or inactive network")]
UnknownNetwork,
/// A message carried a network id different from the session's network.
#[error("message network id does not match the authenticated session network")]
NetworkMismatch,
/// A regular control message arrived before the handshake completed.
#[error("control message received before authentication completed")]
NotAuthenticated,
/// A handshake step did not complete within the configured timeout.
#[error("handshake timed out")]
HandshakeTimeout,
/// A field exceeded its configured bound.
#[error("field `{field}` exceeds its limit ({len} > {limit})")]
FieldTooLarge {
/// Name of the offending field.
field: &'static str,
/// Observed length.
len: usize,
/// Configured limit.
limit: usize,
},
/// The connection is not usable for deriving channel binding material.
#[error("connection does not provide TLS exporter material: {0}")]
NoChannelBinding(String),
}