1
//! Errors to do with pluggable transports.
2

            
3
use fs_mistrust::anon_home::PathExt as _;
4
use futures::task::SpawnError;
5
use std::path::PathBuf;
6
use std::sync::Arc;
7
use tor_chanmgr::factory::AbstractPtError;
8
use tor_config_path::{CfgPath, CfgPathError};
9
use tor_error::{ErrorKind, HasKind, HasRetryTime, RetryTime};
10

            
11
/// An error spawning or managing a pluggable transport.
12
#[derive(Clone, Debug, thiserror::Error)]
13
#[non_exhaustive]
14
pub enum PtError {
15
    /// We failed to launch a set of pluggable transports in the provided deadline.
16
    #[error("PT launch timed out")]
17
    Timeout,
18
    /// A PT binary does not support a set of pluggable transports.
19
    #[error("PT binary does not support transports: {0:?}")]
20
    ClientTransportsUnsupported(Vec<String>),
21
    /// A PT binary failed to launch a pluggable transport, and reported
22
    /// an error message.
23
    #[error("Transport '{}' failed to launch, saying: {:?}", transport, message)]
24
    TransportGaveError {
25
        /// The transport that failed.
26
        transport: String,
27
        /// The failure message.
28
        message: String,
29
    },
30
    /// A pluggable transport binary failed to understand us.
31
    #[error("PT reported protocol error: {0}")]
32
    ChildProtocolViolation(String),
33
    /// A pluggable transport binary violated the protocol.
34
    #[error("PT violated protocol: {0}")]
35
    ProtocolViolation(String),
36
    /// A pluggable transport binary doesn't support version 1 of the IPC protocol.
37
    #[error("PT binary uses unsupported protocol version")]
38
    UnsupportedVersion,
39
    /// A pluggable transport binary couldn't use the provided proxy URI.
40
    #[error("PT binary failed to use proxy URI: {0}")]
41
    ProxyError(String),
42
    /// A pluggable transport binary quit or was stopped.
43
    #[error("PT binary gone")]
44
    ChildGone,
45
    /// An error was encountered communicating with a pluggable transport binary. The PT is no
46
    /// longer usable.
47
    #[error("Failed to read from PT binary: {0}")]
48
    ChildReadFailed(Arc<std::io::Error>),
49
    /// We couldn't spawn a pluggable transport binary as a child process.
50
    #[error("Couldn't execute PT binary at {}: {}", path.anonymize_home(), error)]
51
    ChildSpawnFailed {
52
        /// The binary path we tried to execute.
53
        path: PathBuf,
54
        /// The I/O error returned.
55
        #[source]
56
        error: Arc<std::io::Error>,
57
    },
58
    /// We failed to parse something a pluggable transport sent us.
59
    #[error("Couldn't parse IPC line \"{}\": {}", line, error)]
60
    IpcParseFailed {
61
        /// The offending line.
62
        line: String,
63
        /// The error encountered parsing it.
64
        error: String,
65
    },
66
    /// We couldn't create a state directory.
67
    #[error("Failed to create a state directory at {}: {}", path.anonymize_home(), error)]
68
    StatedirCreateFailed {
69
        /// The offending path.
70
        path: PathBuf,
71
        /// The error encountered.
72
        #[source]
73
        error: Arc<std::io::Error>,
74
    },
75
    /// We couldn't expand a path.
76
    #[error("Failed to expand path {}: {}", path, error)]
77
    PathExpansionFailed {
78
        /// The offending path.
79
        path: CfgPath,
80
        /// The error encountered.
81
        #[source]
82
        error: CfgPathError,
83
    },
84
    /// A binary path does not have the syntax of a *file* name.
85
    ///
86
    /// For example, it ends in a slash, indicating a directory.
87
    //
88
    // TODO: this should be rejected at the configuration parsing level, and treated as a bug here.
89
    #[error("Configured binary path {} doesn't have syntax of a file", path.anonymize_home())]
90
    NotAFile {
91
        /// The offending path.
92
        path: PathBuf,
93
    },
94
    /// Unable to spawn reactor task.
95
    #[error("Unable to spawn reactor task.")]
96
    Spawn {
97
        /// What happened when we tried to spawn it.
98
        #[source]
99
        cause: Arc<SpawnError>,
100
    },
101
    /// The requested transport was found to be missing due to racing with reconfiguration
102
    #[error("Transport not found due to concurrent reconfiguration")]
103
    // TODO: That this can occur at all is a bug.
104
    // See https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/901#note_2858455
105
    UnconfiguredTransportDueToConcurrentReconfiguration,
106
    /// The pluggable transport reactor failed.
107
    #[error("Internal error")]
108
    Internal(#[from] tor_error::Bug),
109
}
110

            
111
impl HasKind for PtError {
112
    fn kind(&self) -> ErrorKind {
113
        use ErrorKind as EK;
114
        use PtError as E;
115
        match self {
116
            E::ClientTransportsUnsupported(_) => EK::InvalidConfig,
117
            E::ChildProtocolViolation(_)
118
            | E::ProtocolViolation(_)
119
            | E::UnsupportedVersion
120
            | E::IpcParseFailed { .. } => EK::LocalProtocolViolation,
121
            E::Timeout
122
            | E::TransportGaveError { .. }
123
            | E::ChildGone
124
            | E::ChildReadFailed(_)
125
            | E::ChildSpawnFailed { .. }
126
            | E::ProxyError(_) => EK::ExternalToolFailed,
127
            E::StatedirCreateFailed { .. } => EK::PersistentStateAccessFailed,
128
            E::UnconfiguredTransportDueToConcurrentReconfiguration => EK::TransientFailure,
129
            E::PathExpansionFailed { .. } => EK::InvalidConfig,
130
            E::NotAFile { .. } => EK::InvalidConfig,
131
            E::Internal(e) => e.kind(),
132
            E::Spawn { cause, .. } => cause.kind(),
133
        }
134
    }
135
}
136

            
137
impl HasRetryTime for PtError {
138
    fn retry_time(&self) -> RetryTime {
139
        use PtError as E;
140
        use RetryTime as RT;
141
        match self {
142
            E::ClientTransportsUnsupported(_)
143
            | E::ChildProtocolViolation(_)
144
            | E::ProtocolViolation(_)
145
            | E::IpcParseFailed { .. }
146
            | E::NotAFile { .. }
147
            | E::UnsupportedVersion
148
            | E::Internal(_)
149
            | E::Spawn { .. }
150
            | E::PathExpansionFailed { .. } => RT::Never,
151
            E::StatedirCreateFailed { .. }
152
            | E::TransportGaveError { .. }
153
            | E::Timeout
154
            | E::UnconfiguredTransportDueToConcurrentReconfiguration
155
            | E::ProxyError(_)
156
            | E::ChildGone
157
            | E::ChildReadFailed(_) => RT::AfterWaiting,
158
            E::ChildSpawnFailed { error, .. } => {
159
                if error.kind() == std::io::ErrorKind::NotFound {
160
                    RT::Never
161
                } else {
162
                    RT::AfterWaiting
163
                }
164
            }
165
        }
166
    }
167
}
168

            
169
impl AbstractPtError for PtError {}
170

            
171
/// Standard-issue `Result` alias, with [`PtError`].
172
pub type Result<T> = std::result::Result<T, PtError>;