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>;