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