qp2p/
error.rs

1// Copyright 2019 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under the MIT license <LICENSE-MIT
4// http://opensource.org/licenses/MIT> or the Modified BSD license <LICENSE-BSD
5// https://opensource.org/licenses/BSD-3-Clause>, at your option. This file may not be copied,
6// modified, or distributed except according to those terms. Please review the Licences for the
7// specific language governing permissions and limitations relating to use of the SAFE Network
8// Software.
9
10use bytes::Bytes;
11use std::{fmt, io, net::SocketAddr};
12use thiserror::Error;
13
14/// Errors returned from the endpoint builder ([`crate::EndpointBuilder::server`]).
15#[derive(Error, Debug)]
16pub enum EndpointError {
17    ///
18    #[error("Certificate could not be generated for config")]
19    Certificate(CertificateError),
20    ///
21    #[error(transparent)]
22    IoError(#[from] io::Error),
23}
24
25/// Various error related to certificates. Should mostly occur due to bugs, not runtime constraints.
26#[derive(Error, Debug)]
27pub enum CertificateError {
28    ///
29    #[error("Rcgen internal error generating certificate")]
30    Rcgen(#[from] rcgen::RcgenError),
31    ///
32    #[error("Certificate or name validation error")]
33    WebPki(#[from] webpki::Error),
34    ///
35    #[error("Rustls internal error")]
36    Rustls(#[from] rustls::Error),
37}
38
39/// Errors that can cause connection loss.
40// This is a copy of `quinn::ConnectionError` without the `*Closed` variants, since we want to
41// separate them in our interface.
42#[derive(Clone, Debug, Error, PartialEq, Eq)]
43pub enum ConnectionError {
44    /// The endpoint has been stopped.
45    #[error("The endpoint has been stopped")]
46    Stopped,
47
48    /// The number of active connections on the local endpoint is at the limit.
49    ///
50    /// This limit is imposed by the underlying connection ID generator, which is not currently
51    /// configurable.
52    // NOTE: We could make this configurable by exposing a way to set
53    // quinn_proto::RandomConnectionIdGenerator cid_len
54    #[error("The number of active connections on the local endpoint is at the limit")]
55    TooManyConnections,
56
57    /// Invalid remote address.
58    ///
59    /// Examples include attempting to connect to port `0` or using an inappropriate address family.
60    #[error("Invalid remote address: {0}")]
61    InvalidAddress(SocketAddr),
62
63    /// Internal configuration error.
64    ///
65    /// This should not occur (if it does, there's a bug!), but it covers possible misconfigurations
66    /// of the underlying transport library.
67    #[error("BUG: internal configuration error")]
68    InternalConfigError(#[source] InternalConfigError),
69
70    /// The peer doesn't implement the supported version.
71    #[error("{}", quinn::ConnectionError::VersionMismatch)]
72    VersionMismatch,
73
74    /// The peer violated the QUIC specification as understood by this implementation.
75    #[error("{0}")]
76    TransportError(#[source] quinn_proto::TransportError),
77
78    /// The peer is unable to continue processing this connection, usually due to having restarted.
79    #[error("{}", quinn::ConnectionError::Reset)]
80    Reset,
81
82    /// Communication with the peer has lapsed for longer than the negotiated idle timeout.
83    #[error("{}", quinn::ConnectionError::TimedOut)]
84    TimedOut,
85
86    /// The connection was closed.
87    #[error("The connection was closed, {0}")]
88    Closed(Close),
89}
90
91impl From<quinn::ConnectError> for ConnectionError {
92    fn from(error: quinn::ConnectError) -> Self {
93        match error {
94            quinn::ConnectError::EndpointStopping => Self::Stopped,
95            quinn::ConnectError::TooManyConnections => Self::TooManyConnections,
96            quinn::ConnectError::InvalidRemoteAddress(addr) => Self::InvalidAddress(addr),
97            quinn::ConnectError::InvalidDnsName(_)
98            | quinn::ConnectError::NoDefaultClientConfig
99            | quinn::ConnectError::UnsupportedVersion => {
100                // We currently use a hard-coded domain name, so if it's invalid we have a library
101                // breaking bug. We want to avoid panics though, so we propagate this as an opaque
102                // error.
103                Self::InternalConfigError(InternalConfigError(error))
104            }
105        }
106    }
107}
108
109impl From<quinn::ConnectionError> for ConnectionError {
110    fn from(error: quinn::ConnectionError) -> Self {
111        match error {
112            quinn::ConnectionError::LocallyClosed => Self::Closed(Close::Local),
113            quinn::ConnectionError::ApplicationClosed(close) => Self::Closed(Close::Application {
114                error_code: close.error_code.into_inner(),
115                reason: close.reason,
116            }),
117            quinn::ConnectionError::ConnectionClosed(close) => Self::Closed(Close::Transport {
118                error_code: TransportErrorCode(close.error_code),
119                reason: close.reason,
120            }),
121            quinn::ConnectionError::VersionMismatch => Self::VersionMismatch,
122            quinn::ConnectionError::TransportError(error) => Self::TransportError(error),
123            quinn::ConnectionError::Reset => Self::Reset,
124            quinn::ConnectionError::TimedOut => Self::TimedOut,
125        }
126    }
127}
128
129/// An internal configuration error encountered by [`Endpoint`](crate::Endpoint) connect methods.
130#[derive(Clone, Debug, Error, PartialEq, Eq)]
131#[error(transparent)]
132pub struct InternalConfigError(quinn::ConnectError);
133
134/// The reason a connection was closed.
135#[derive(Clone, Debug, PartialEq, Eq)]
136pub enum Close {
137    /// This application closed the connection.
138    Local,
139
140    /// The remote application closed the connection.
141    Application {
142        /// The error code supplied by the application.
143        error_code: u64,
144
145        /// The reason supplied by the application.
146        reason: Bytes,
147    },
148
149    /// The transport layer closed the connection.
150    ///
151    /// This would indicate an abrupt disconnection or a protocol violation.
152    Transport {
153        /// The error code supplied by the QUIC library.
154        error_code: TransportErrorCode,
155
156        /// The reason supplied by the QUIC library.
157        reason: Bytes,
158    },
159}
160
161impl fmt::Display for Close {
162    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163        let (closed_by, error_code, reason): (_, &dyn fmt::Display, _) = match self {
164            Self::Local => return write!(f, "we closed the connection"),
165            Self::Application { error_code, reason } => ("remote application", error_code, reason),
166            Self::Transport { error_code, reason } => ("transport layer", error_code, reason),
167        };
168        write!(
169            f,
170            "{} closed the connection (error code: {}, reason: {})",
171            closed_by,
172            error_code,
173            String::from_utf8_lossy(reason)
174        )
175    }
176}
177
178impl From<quinn::ApplicationClose> for Close {
179    fn from(close: quinn::ApplicationClose) -> Self {
180        Self::Application {
181            error_code: close.error_code.into_inner(),
182            reason: close.reason,
183        }
184    }
185}
186
187impl From<quinn::ConnectionClose> for Close {
188    fn from(close: quinn::ConnectionClose) -> Self {
189        Self::Transport {
190            error_code: TransportErrorCode(close.error_code),
191            reason: close.reason,
192        }
193    }
194}
195
196/// An opaque error code indicating a transport failure.
197///
198/// This can be turned to a string via its `Debug` and `Display` impls, but is otherwise opaque.
199#[derive(Clone, PartialEq, Eq)]
200pub struct TransportErrorCode(quinn_proto::TransportErrorCode);
201
202impl fmt::Debug for TransportErrorCode {
203    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
204        write!(f, "{:?}", self.0)
205    }
206}
207
208impl fmt::Display for TransportErrorCode {
209    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210        write!(f, "{}", self.0)
211    }
212}
213
214/// Errors that can occur when performing an RPC operation.
215///
216/// QuicP2P uses a number of RPC operations when establishing endpoints, in order to verify public
217/// addresses and check for reachability. This error type covers the ways in which these operations
218/// can fail, which is a combination of [`SendError`], and [`RecvError`].
219#[derive(Debug, Error)]
220pub enum RpcError {
221    /// We did not receive a response within the expected time.
222    #[error("We did not receive a response within the expected time")]
223    TimedOut,
224
225    /// Failed to send the request.
226    #[error("Failed to send the request")]
227    Send(#[from] SendError),
228
229    /// Failed to receive the response.
230    #[error("Failed to receive the response")]
231    Recv(#[from] RecvError),
232}
233
234// Treating `ConnectionError`s as happening on send works because we would only encounter them
235// directly (e.g. not part of `SendError` or `RecvError`) when establishing outgoing connections
236// before sending.
237impl From<ConnectionError> for RpcError {
238    fn from(error: ConnectionError) -> Self {
239        Self::Send(error.into())
240    }
241}
242
243impl From<quinn::ConnectionError> for RpcError {
244    fn from(error: quinn::ConnectionError) -> Self {
245        Self::Send(error.into())
246    }
247}
248
249impl From<tokio::time::error::Elapsed> for RpcError {
250    fn from(_: tokio::time::error::Elapsed) -> Self {
251        Self::TimedOut
252    }
253}
254
255/// Errors that can occur when sending messages.
256#[derive(Debug, Error)]
257pub enum SendError {
258    /// The serialised message is too long
259    #[error("The serialized message is too long ({0} bytes, max: 4 GiB)")]
260    MessageTooLong(usize),
261
262    /// Failed to serialize message.
263    ///
264    /// This likely indicates a bug in the library, since serializing to bytes should be infallible.
265    /// Limitations in the serde API mean we cannot verify this statically, and we don't want to
266    /// introduce potential panics.
267    #[error("Failed to serialize message")]
268    Serialization(#[from] bincode::Error),
269
270    /// Connection was lost when trying to send a message.
271    #[error("Connection was lost when trying to send a message")]
272    ConnectionLost(#[from] ConnectionError),
273
274    /// Stream was lost when trying to send a message.
275    #[error("Stream was lost when trying to send a message")]
276    StreamLost(#[source] StreamError),
277}
278
279impl From<quinn::ConnectionError> for SendError {
280    fn from(error: quinn::ConnectionError) -> Self {
281        Self::ConnectionLost(error.into())
282    }
283}
284
285impl From<quinn::WriteError> for SendError {
286    fn from(error: quinn::WriteError) -> Self {
287        match error {
288            quinn::WriteError::Stopped(code) => Self::StreamLost(StreamError::Stopped(code.into())),
289            quinn::WriteError::ConnectionLost(error) => Self::ConnectionLost(error.into()),
290            quinn::WriteError::UnknownStream => Self::StreamLost(StreamError::Gone),
291            quinn::WriteError::ZeroRttRejected => Self::StreamLost(StreamError::Unsupported(
292                UnsupportedStreamOperation(error.into()),
293            )),
294        }
295    }
296}
297
298/// Errors that can occur when receiving messages.
299#[derive(Debug, Error)]
300pub enum RecvError {
301    /// Message received has an empty payload
302    #[error("Message received from peer has an empty payload")]
303    EmptyMsgPayload,
304
305    /// Not enough bytes were received to be a valid message
306    #[error("Received too few bytes for message")]
307    NotEnoughBytes,
308
309    /// Failed to deserialize message.
310    #[error("Failed to deserialize message")]
311    Serialization(#[from] bincode::Error),
312
313    /// Message type flag found in message header is invalid
314    #[error("Invalid message type flag found in message header: {0}")]
315    InvalidMsgTypeFlag(u8),
316
317    /// Type of message received is unexpected
318    #[error("Type of message received is unexpected: {0}")]
319    UnexpectedMsgReceived(String),
320
321    /// Connection was lost when trying to receive a message.
322    #[error("Connection was lost when trying to receive a message")]
323    ConnectionLost(#[from] ConnectionError),
324
325    /// Connection was lost when trying to receive a message.
326    #[error("Error reading to end of stream")]
327    ReadToEndError(#[from] quinn::ReadToEndError),
328
329    /// Stream was lost when trying to receive a message.
330    #[error("Stream was lost when trying to receive a message")]
331    StreamLost(#[source] StreamError),
332}
333
334impl From<quinn::ConnectionError> for RecvError {
335    fn from(error: quinn::ConnectionError) -> Self {
336        Self::ConnectionLost(error.into())
337    }
338}
339
340impl From<quinn::ReadError> for RecvError {
341    fn from(error: quinn::ReadError) -> Self {
342        use quinn::ReadError;
343
344        match error {
345            ReadError::Reset(code) => Self::StreamLost(StreamError::Stopped(code.into())),
346            ReadError::ConnectionLost(error) => Self::ConnectionLost(error.into()),
347            ReadError::UnknownStream => Self::StreamLost(StreamError::Gone),
348            ReadError::IllegalOrderedRead | ReadError::ZeroRttRejected => Self::StreamLost(
349                StreamError::Unsupported(UnsupportedStreamOperation(error.into())),
350            ),
351        }
352    }
353}
354
355impl From<quinn::ReadExactError> for RecvError {
356    fn from(error: quinn::ReadExactError) -> Self {
357        match error {
358            quinn::ReadExactError::FinishedEarly => Self::NotEnoughBytes,
359            quinn::ReadExactError::ReadError(error) => error.into(),
360        }
361    }
362}
363
364/// Errors that can occur when interacting with streams.
365#[derive(Debug, Error)]
366pub enum StreamError {
367    /// The peer abandoned the stream.
368    #[error("The peer abandoned the stream (error code: {0})")]
369    Stopped(u64),
370
371    /// The stream was already stopped, finished, or reset.
372    #[error("The stream was already stopped, finished, or reset")]
373    Gone,
374
375    /// An error was caused by an unsupported operation.
376    ///
377    /// Additional stream errors can arise from the use of 0-RTT connections or unordered reads,
378    /// neither of which are supported by the library.
379    #[error("An error was caused by an unsupported operation")]
380    Unsupported(#[source] UnsupportedStreamOperation),
381}
382
383/// An error caused by an unsupported operation.
384#[derive(Debug, Error)]
385#[error(transparent)]
386pub struct UnsupportedStreamOperation(Box<dyn std::error::Error + Send + Sync>);