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
// SPDX-License-Identifier: MIT
// Copyright (c) Microsoft Corporation.
//! Error types for the Siguldry server, bridge, and client.
use zerocopy::TryCastError;
pub use crate::protocol::{Error as ProtocolError, ServerError};
/// Errors that occur during the connection.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ConnectionError {
/// An I/O occurred.
///
/// This is very likely due to temporary networking issues and the operation
/// should be retried.
///
/// Be aware, however, that it could be because the specified hostname or
/// port is incorrect, in which case retrying will never succeed.
#[error("an I/O error occurred: {0}")]
Io(std::io::Error),
/// An OpenSSL error occurred.
///
/// This is possibly a bug in this client or the OpenSSL bindings, or because
/// the system-provided OpenSSL library does not support an operation this client
/// needs. Retrying is not recommended.
#[error("one or more openssl errors occurred: {0}")]
SslErrors(#[from] openssl::error::ErrorStack),
/// The TLS connection to the bridge or server failed.
///
/// This could be due to a protocol level failure, like a handshake failure
/// due to no common supported versions/ciphers/etc, or because the TLS
/// certificate is incorrect, or due to a network-level failure.
///
/// It may be worth retrying, although in the event of a handshake failure
/// or TLS certificate issue, it will not succeed.
#[error("an SSL error occurred: {0}")]
Ssl(#[from] openssl::ssl::Error),
/// A protocol violation occurred.
///
/// This occurs if the handshake is malformed, the framing is invalid, etc.
/// This is almost certainly a bug.
#[error(transparent)]
Protocol(#[from] ProtocolError),
}
impl From<std::io::Error> for ConnectionError {
fn from(error: std::io::Error) -> Self {
// I/O errors may occur due to a TLS error, like if the server rejects the client certificate
// but then the client reads from the socket. Map those type of errors to our more specific
// error variants.
if let Some(ssl_error) = std::error::Error::source(&error)
.and_then(|error| error.downcast_ref::<openssl::error::ErrorStack>())
{
ConnectionError::Ssl(ssl_error.to_owned().into())
} else {
ConnectionError::Io(error)
}
}
}
impl<S, D> From<TryCastError<S, D>> for ConnectionError
where
D: zerocopy::TryFromBytes,
{
fn from(value: TryCastError<S, D>) -> Self {
ProtocolError::Framing(format!("{value:?}")).into()
}
}
/// Errors the [`crate::client::Client`] may return.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ClientError {
/// Returned in the event that an error occurred while communicating with the bridge or
/// server. This may be a result of a transient networking problem, or because of a more
/// permanent issue such as invalid configuration, or event a client bug.
///
/// Retrying the operation that led to this error is safe, although whether subsequent
/// attempts fail or succeed depend on the specific error. Refer to [`ConnectionError`] for
/// details on the possible errors and if retrying is advisable.
#[error("connection error with bridge or server: {0}")]
Connection(#[from] ConnectionError),
/// A general I/O error occurred, unrelated to the underlying network connection. It is likely
/// due to a file not existing, or being unreadable by this process.
///
/// For example, TLS certificates and private keys are read from the filesystem. Some client
/// operations may involve sending or receiving files, as well.
#[error("an I/O error occurred: {0}")]
Io(#[from] std::io::Error),
/// Returned in the event that the OpenSSL configuration derived from
/// [`crate::client::TlsConfig`] is invalid or otherwise disagreeable to OpenSSL.
///
/// This error is not returned for an OpenSSL-related error during the connection, so retrying
/// is not appropriate.
#[error("openssl could not be configured: {0}")]
Ssl(#[from] openssl::error::ErrorStack),
#[error("The bridge or server certificate is not valid")]
CertificateVerifyFailed,
#[error("The client certificate is not signed by a CA the bridge or server trust")]
InvalidClientCertificate,
/// Errors the server returned for a particular request.
///
/// Retrying may be appropriate for some server errors, while others may never succeed.
#[error("The server responded with an error: {0}")]
Server(#[from] ServerError),
#[error("Failed to serialize a request or response to JSON: {0}")]
Serialization(#[from] serde_json::Error),
/// Generic error that indicates a fatal error, likely due to a bug in the client.
///
/// Retrying the operation will not help, and this should be reported as bug.
#[error(transparent)]
Fatal(#[from] anyhow::Error),
}