Skip to main content

tailscale/
error.rs

1use std::fmt;
2
3use crate::netstack::Error as NetstackError;
4
5/// Errors that may occur while interacting with a device.
6#[derive(Debug, thiserror::Error, Clone, Copy, Eq, PartialEq)]
7pub enum Error {
8    /// An operation timed-out.
9    ///
10    /// This error can often be handled by retrying.
11    #[error("operation timed-out")]
12    Timeout,
13
14    /// A connection was reset.
15    ///
16    /// This error can often be handled by retrying.
17    #[error("connection reset")]
18    ConnectionReset,
19
20    /// An error reading or parsing the key file.
21    #[error("an error reading or parsing the key file")]
22    KeyFileRead,
23
24    /// An error writing out the key file.
25    #[error("an error writing out the key file")]
26    KeyFileWrite,
27
28    /// The environment variable `TS_RS_EXPERIMENT` was not set.
29    ///
30    /// The end-user must set `TS_RS_EXPERIMENT=this_is_unstable_software` to acknowledge that tailscale-rs
31    /// is early-days experimental software containing bugs, unvalidated cryptography, and no stability
32    /// or compatibility guarantees.
33    #[error("the environment variable `{}` was not set", crate::ENV_MAGIC_VAR)]
34    UnstableEnvVar,
35
36    /// An error occurred which can not be anticipated or handled by a library user.
37    ///
38    /// This is likely due to a bug in our code or a rare and unexpected error.
39    ///
40    /// [`InternalErrorKind`] is intended to be informational (might be used to improve error reporting
41    /// in logs or to the end-user), rather then inspected during handling.
42    #[error("internal error ({0})")]
43    Internal(InternalErrorKind),
44}
45
46/// Informational detail on the kind of internal error.
47#[non_exhaustive]
48#[derive(Debug, Clone, Copy, Eq, PartialEq)]
49pub enum InternalErrorKind {
50    /// Invalid socket state.
51    InvalidSocketState,
52    /// Response type mismatched to request type.
53    InternalResponseMismatch,
54    /// Channel closed.
55    InternalChannelClosed,
56    /// Handle to invalid TCP listener.
57    BadListenerHandle,
58    /// Handle to invalid socket.
59    BadSocketHandle,
60    /// Bad request.
61    BadRequest,
62    /// Buffer is full, cannot read in packet.
63    BufferFull,
64    /// Actor missing or shutdown.
65    Actor,
66    /// The operation is not supported while running in TUN transport mode, or
67    /// TUN mode was requested but is unavailable (no device, or the `tun`
68    /// feature is disabled in this build).
69    UnsupportedInTunMode,
70    /// The internal OS network monitor was requested (`Config::network_monitor`)
71    /// but this build was compiled without the `network-monitor` feature, so the
72    /// supervisor could not be started. Rebuild with the feature enabled, or leave
73    /// `network_monitor` off and drive `Device::rebind` from your own link monitor.
74    NetworkMonitorUnavailable,
75    /// The requested resource (e.g. a Taildrop file) does not exist.
76    NotFound,
77    /// The resource already exists (e.g. a Taildrop transfer for the same file is in progress).
78    AlreadyExists,
79    /// An underlying I/O error (e.g. a Taildrop filesystem operation failed).
80    Io,
81}
82
83impl fmt::Display for InternalErrorKind {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match self {
86            InternalErrorKind::InvalidSocketState => write!(f, "invalid socket state"),
87            InternalErrorKind::InternalResponseMismatch => {
88                write!(f, "response type mismatched to request type")
89            }
90            InternalErrorKind::InternalChannelClosed => write!(f, "channel closed"),
91            InternalErrorKind::BadListenerHandle => write!(f, "handle to invalid TCP listener"),
92            InternalErrorKind::BadSocketHandle => write!(f, "handle to invalid socket"),
93            InternalErrorKind::BadRequest => write!(f, "bad request"),
94            InternalErrorKind::BufferFull => write!(f, "buffer full"),
95            InternalErrorKind::Actor => write!(f, "actor missing or shutdown"),
96            InternalErrorKind::UnsupportedInTunMode => {
97                write!(f, "operation unsupported in TUN transport mode")
98            }
99            InternalErrorKind::NetworkMonitorUnavailable => {
100                write!(
101                    f,
102                    "network monitor requested but the `network-monitor` feature is disabled"
103                )
104            }
105            InternalErrorKind::NotFound => write!(f, "resource not found"),
106            InternalErrorKind::AlreadyExists => write!(f, "resource already exists"),
107            InternalErrorKind::Io => write!(f, "I/O error"),
108        }
109    }
110}
111
112impl From<crate::netstack::InternalErrorKind> for InternalErrorKind {
113    fn from(e: crate::netstack::InternalErrorKind) -> Self {
114        match e {
115            crate::netstack::InternalErrorKind::InvalidSocketState => {
116                InternalErrorKind::InvalidSocketState
117            }
118            crate::netstack::InternalErrorKind::InternalResponseMismatch => {
119                InternalErrorKind::InternalResponseMismatch
120            }
121            crate::netstack::InternalErrorKind::InternalChannelClosed => {
122                InternalErrorKind::InternalChannelClosed
123            }
124            crate::netstack::InternalErrorKind::BadListenerHandle => {
125                InternalErrorKind::BadListenerHandle
126            }
127            crate::netstack::InternalErrorKind::BadSocketHandle => {
128                InternalErrorKind::BadSocketHandle
129            }
130            crate::netstack::InternalErrorKind::BufferFull => InternalErrorKind::BufferFull,
131            _ => unreachable!(),
132        }
133    }
134}
135
136impl From<ts_runtime::Error> for Error {
137    fn from(value: ts_runtime::Error) -> Self {
138        match value.kind {
139            ts_runtime::ErrorKind::Timeout => Error::Timeout,
140            ts_runtime::ErrorKind::ActorGone
141            | ts_runtime::ErrorKind::MailboxFull
142            | ts_runtime::ErrorKind::ReplyErr => Error::Internal(InternalErrorKind::Actor),
143            // TUN transport mode: a netstack-only operation, or TUN requested but unavailable
144            // (no device / `tun` feature off).
145            ts_runtime::ErrorKind::UnsupportedInTunMode | ts_runtime::ErrorKind::TunUnavailable => {
146                Error::Internal(InternalErrorKind::UnsupportedInTunMode)
147            }
148            // The network monitor was requested but this build lacks the `network-monitor` feature.
149            ts_runtime::ErrorKind::NetworkMonitorUnavailable => {
150                Error::Internal(InternalErrorKind::NetworkMonitorUnavailable)
151            }
152        }
153    }
154}
155
156impl From<NetstackError> for Error {
157    fn from(value: NetstackError) -> Self {
158        match value {
159            NetstackError::Internal(k) => Error::Internal(k.into()),
160            NetstackError::ConnectionReset => Error::ConnectionReset,
161            NetstackError::BadRequest(_) => Error::Internal(InternalErrorKind::BadRequest),
162        }
163    }
164}