192 lines
6.2 KiB
Rust
192 lines
6.2 KiB
Rust
//! 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),
|
||
|
|
}
|