Skip to main content

sipx_transport/
error.rs

1//! Transport errors.
2
3use thiserror::Error;
4
5/// What can go wrong in the transport layer.
6#[derive(Debug, Error)]
7#[non_exhaustive]
8pub enum Error {
9    /// Endpoint configuration cannot create a bounded, live runtime.
10    #[error("invalid endpoint configuration `{field}`: {reason}")]
11    InvalidConfig {
12        /// The public configuration field that is invalid.
13        field: &'static str,
14        /// Its required range.
15        reason: &'static str,
16    },
17    /// An outgoing transaction cannot be cancelled as requested.
18    #[error("invalid INVITE cancellation: {reason}")]
19    InvalidCancellation {
20        /// Which invariant the request did not satisfy.
21        reason: &'static str,
22    },
23    /// An outbound request selected a listener kind this endpoint did not configure.
24    #[error("the {transport} transport is not configured")]
25    TransportNotConfigured {
26        /// Stable upper-case transport spelling.
27        transport: &'static str,
28    },
29    /// A socket operation failed.
30    #[error("io: {0}")]
31    Io(#[from] std::io::Error),
32    /// The endpoint loop has stopped.
33    #[error("the endpoint has shut down")]
34    EndpointClosed,
35    /// Graceful drain has closed admission for requests which establish a dialog.
36    #[error("the endpoint is draining and no longer accepts new dialogs")]
37    EndpointDraining,
38    /// An in-process endpoint was constructed without an entered Tokio runtime.
39    #[error("an entered Tokio runtime is required for the in-process endpoint")]
40    RuntimeUnavailable,
41    /// The next hop asked this endpoint to reduce traffic and this request was not admitted.
42    #[error("request rejected by overload control for {peer}")]
43    Overloaded {
44        /// The downstream server whose active report caused the rejection.
45        peer: std::net::SocketAddr,
46    },
47    /// A configured pre-transaction policy refused the request.
48    #[error("request rejected by endpoint policy: {reason}")]
49    PolicyRejected {
50        /// Host-supplied, non-secret reason.
51        reason: String,
52    },
53    /// A policy attempted to add a stack-owned identity, routing, authentication or framing field.
54    #[error("endpoint policy may not add protected header {name}")]
55    ProtectedPolicyHeader {
56        /// Canonical field name.
57        name: String,
58    },
59    /// A live source-admission replacement exceeded the configured scan bound.
60    #[error(
61        "source-admission replacement carries {attempted} prefixes; configured maximum is {max}"
62    )]
63    SourceAdmissionCapacity {
64        /// Configured maximum number of prefixes in one generation.
65        max: usize,
66        /// Number of prefixes in the refused replacement.
67        attempted: usize,
68    },
69    /// A request could not be sent because it has no usable `Via`, so no transaction could be
70    /// keyed on it and no response could ever be matched.
71    #[error("the request has no usable Via")]
72    NoVia,
73    /// A message could not be built.
74    #[error("build: {0}")]
75    Build(#[from] sipx_sip::error::BuildError),
76    /// TLS could not be established or verified.
77    ///
78    /// A `sips:` request that reaches this has failed. There is deliberately no path from here
79    /// to a cleartext retry: a downgrade would defeat exactly what the scheme asked for.
80    #[cfg(feature = "tls")]
81    #[error("tls: {0}")]
82    Tls(#[from] crate::tls::TlsError),
83    /// QUIC authentication, negotiation, or connection failure.
84    #[cfg(feature = "quic")]
85    #[error("quic: {0}")]
86    Quic(#[from] crate::quic::QuicError),
87    /// A response was given for a transaction that no longer exists.
88    ///
89    /// Almost always means the application took longer to answer than
90    /// [`crate::Config::unanswered_limit`] allows. Reported rather than swallowed: an
91    /// application told its 200 OK went out, when it did not, believes a call is up while the
92    /// caller has already timed out.
93    #[error("no such transaction; it was abandoned or has already ended")]
94    NoTransaction,
95    /// A keep-alive was answered with a STUN Binding Error Response (RFC 5626 §4.4.2).
96    ///
97    /// §4.4.2 says the flow "is considered failed" — a *refused* keep-alive is a stronger signal
98    /// than an unanswered one, since something is there and it does not want this flow.
99    #[error("the keep-alive was refused; the flow has failed")]
100    KeepaliveRefused,
101    /// A keep-alive went unanswered (RFC 5626 §4.4.1, §4.4.2).
102    ///
103    /// §4.4.1: "If a pong is not received within 10 seconds after sending a ping ... then the
104    /// client MUST treat the flow as failed."
105    #[error("the keep-alive went unanswered; the flow has failed")]
106    KeepaliveUnanswered,
107    /// The connection a keep-alive was sent on closed before it was answered.
108    #[error("the connection closed")]
109    ConnectionClosed,
110    /// A URI that resolved to no usable candidate (RFC 3263).
111    #[error("no usable candidate for {}", String::from_utf8_lossy(.0))]
112    Unresolvable(Vec<u8>),
113    /// A transport that is declared but not yet implemented.
114    #[error("the {0} transport is not implemented yet")]
115    UnsupportedTransport(&'static str),
116    /// Every configured live connection slot is still occupied.
117    #[error("the connection pool's {max} live slots are occupied")]
118    ConnectionCapacity {
119        /// The configured live-task limit.
120        max: usize,
121    },
122    /// A capture file could not be opened (`docs/specs/sip-transport.md` §13).
123    ///
124    /// Reported from `bind` rather than swallowed, because the alternative is an endpoint that
125    /// starts, appears to be recording, and writes nothing — the same failure as a silent discard,
126    /// one level up. The path is named because a permission or directory mistake is the usual cause.
127    #[error("the capture at {path} could not be opened: {source}")]
128    Capture {
129        /// Where the capture was to be written.
130        path: String,
131        /// Why it could not be.
132        source: std::io::Error,
133    },
134    /// An oversized UDP request selected the mandatory TCP fallback, but TCP could not be used.
135    ///
136    /// The concrete cause is retained so connection refusal, capacity and endpoint shutdown stay
137    /// distinguishable. There is deliberately no retry over UDP after this error.
138    #[error(
139        "the {size} byte request exceeds the {limit} byte datagram limit and TCP fallback failed: {source}"
140    )]
141    TcpFallbackUnavailable {
142        /// Serialized request size that required the switch.
143        size: usize,
144        /// RFC 3261 §18.1.1's derived limit for this path.
145        limit: usize,
146        /// Why the selected TCP send failed.
147        #[source]
148        source: Box<Error>,
149    },
150    /// A datagram larger than the path MTU on an unreliable transport (RFC 3261 §18.1.1).
151    ///
152    /// Named rather than truncated: a truncated SIP message is a security problem, not a
153    /// degraded one.
154    #[error("message of {size} bytes exceeds the {limit} byte datagram limit")]
155    TooLarge {
156        /// How big the message is.
157        size: usize,
158        /// The configured limit.
159        limit: usize,
160    },
161}
162
163/// A transport result.
164pub type Result<T> = std::result::Result<T, Error>;