geiserx_tailscale 0.41.0

A work-in-progress pure-Rust Tailscale implementation (fork of tailscale/tailscale-rs)
Documentation
use std::fmt;

use crate::netstack::Error as NetstackError;

/// Errors that may occur while interacting with a device.
#[derive(Debug, thiserror::Error, Clone, Copy, Eq, PartialEq)]
pub enum Error {
    /// An operation timed-out.
    ///
    /// This error can often be handled by retrying.
    #[error("operation timed-out")]
    Timeout,

    /// A connection was reset.
    ///
    /// This error can often be handled by retrying.
    #[error("connection reset")]
    ConnectionReset,

    /// An error reading or parsing the key file.
    #[error("an error reading or parsing the key file")]
    KeyFileRead,

    /// An error writing out the key file.
    #[error("an error writing out the key file")]
    KeyFileWrite,

    /// The environment variable `TS_RS_EXPERIMENT` was not set.
    ///
    /// The end-user must set `TS_RS_EXPERIMENT=this_is_unstable_software` to acknowledge that tailscale-rs
    /// is early-days experimental software containing bugs, unvalidated cryptography, and no stability
    /// or compatibility guarantees.
    #[error("the environment variable `{}` was not set", crate::ENV_MAGIC_VAR)]
    UnstableEnvVar,

    /// No exit-node suggestion could be made because this node has no measured preferred DERP
    /// region yet — the Rust analog of Go's `ErrNoPreferredDERP` ("no preferred DERP, try again
    /// later"). Returned by [`Device::suggest_exit_node`](crate::Device::suggest_exit_node) before
    /// the first netcheck has completed; callers should treat it as transient and retry once
    /// connectivity has been measured. Distinct from a *successful* "no suggestion" (an empty
    /// candidate set), which is `Ok(None)`.
    #[error("no preferred DERP, try again later")]
    NoPreferredDerp,

    /// An error occurred which can not be anticipated or handled by a library user.
    ///
    /// This is likely due to a bug in our code or a rare and unexpected error.
    ///
    /// [`InternalErrorKind`] is intended to be informational (might be used to improve error reporting
    /// in logs or to the end-user), rather then inspected during handling.
    #[error("internal error ({0})")]
    Internal(InternalErrorKind),
}

impl From<ts_runtime::SuggestExitNodeError> for Error {
    fn from(value: ts_runtime::SuggestExitNodeError) -> Self {
        match value {
            ts_runtime::SuggestExitNodeError::NoPreferredDerp => Error::NoPreferredDerp,
        }
    }
}

/// Informational detail on the kind of internal error.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum InternalErrorKind {
    /// Invalid socket state.
    InvalidSocketState,
    /// Response type mismatched to request type.
    InternalResponseMismatch,
    /// Channel closed.
    InternalChannelClosed,
    /// Handle to invalid TCP listener.
    BadListenerHandle,
    /// Handle to invalid socket.
    BadSocketHandle,
    /// Bad request.
    BadRequest,
    /// Buffer is full, cannot read in packet.
    BufferFull,
    /// Actor missing or shutdown.
    Actor,
    /// The operation is not supported while running in TUN transport mode, or
    /// TUN mode was requested but is unavailable (no device, or the `tun`
    /// feature is disabled in this build).
    UnsupportedInTunMode,
    /// The internal OS network monitor was requested (`Config::network_monitor`)
    /// but this build was compiled without the `network-monitor` feature, so the
    /// supervisor could not be started. Rebuild with the feature enabled, or leave
    /// `network_monitor` off and drive `Device::rebind` from your own link monitor.
    NetworkMonitorUnavailable,
    /// The requested resource (e.g. a Taildrop file) does not exist.
    NotFound,
    /// The resource already exists (e.g. a Taildrop transfer for the same file is in progress).
    AlreadyExists,
    /// An underlying I/O error (e.g. a Taildrop filesystem operation failed).
    Io,
}

impl fmt::Display for InternalErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            InternalErrorKind::InvalidSocketState => write!(f, "invalid socket state"),
            InternalErrorKind::InternalResponseMismatch => {
                write!(f, "response type mismatched to request type")
            }
            InternalErrorKind::InternalChannelClosed => write!(f, "channel closed"),
            InternalErrorKind::BadListenerHandle => write!(f, "handle to invalid TCP listener"),
            InternalErrorKind::BadSocketHandle => write!(f, "handle to invalid socket"),
            InternalErrorKind::BadRequest => write!(f, "bad request"),
            InternalErrorKind::BufferFull => write!(f, "buffer full"),
            InternalErrorKind::Actor => write!(f, "actor missing or shutdown"),
            InternalErrorKind::UnsupportedInTunMode => {
                write!(f, "operation unsupported in TUN transport mode")
            }
            InternalErrorKind::NetworkMonitorUnavailable => {
                write!(
                    f,
                    "network monitor requested but the `network-monitor` feature is disabled"
                )
            }
            InternalErrorKind::NotFound => write!(f, "resource not found"),
            InternalErrorKind::AlreadyExists => write!(f, "resource already exists"),
            InternalErrorKind::Io => write!(f, "I/O error"),
        }
    }
}

impl From<crate::netstack::InternalErrorKind> for InternalErrorKind {
    fn from(e: crate::netstack::InternalErrorKind) -> Self {
        match e {
            crate::netstack::InternalErrorKind::InvalidSocketState => {
                InternalErrorKind::InvalidSocketState
            }
            crate::netstack::InternalErrorKind::InternalResponseMismatch => {
                InternalErrorKind::InternalResponseMismatch
            }
            crate::netstack::InternalErrorKind::InternalChannelClosed => {
                InternalErrorKind::InternalChannelClosed
            }
            crate::netstack::InternalErrorKind::BadListenerHandle => {
                InternalErrorKind::BadListenerHandle
            }
            crate::netstack::InternalErrorKind::BadSocketHandle => {
                InternalErrorKind::BadSocketHandle
            }
            crate::netstack::InternalErrorKind::BufferFull => InternalErrorKind::BufferFull,
            _ => unreachable!(),
        }
    }
}

impl From<ts_runtime::Error> for Error {
    fn from(value: ts_runtime::Error) -> Self {
        match value.kind {
            ts_runtime::ErrorKind::Timeout => Error::Timeout,
            ts_runtime::ErrorKind::ActorGone
            | ts_runtime::ErrorKind::MailboxFull
            | ts_runtime::ErrorKind::ReplyErr => Error::Internal(InternalErrorKind::Actor),
            // TUN transport mode: a netstack-only operation, or TUN requested but unavailable
            // (no device / `tun` feature off).
            ts_runtime::ErrorKind::UnsupportedInTunMode | ts_runtime::ErrorKind::TunUnavailable => {
                Error::Internal(InternalErrorKind::UnsupportedInTunMode)
            }
            // The network monitor was requested but this build lacks the `network-monitor` feature.
            ts_runtime::ErrorKind::NetworkMonitorUnavailable => {
                Error::Internal(InternalErrorKind::NetworkMonitorUnavailable)
            }
        }
    }
}

impl From<NetstackError> for Error {
    fn from(value: NetstackError) -> Self {
        match value {
            NetstackError::Internal(k) => Error::Internal(k.into()),
            NetstackError::ConnectionReset => Error::ConnectionReset,
            NetstackError::BadRequest(_) => Error::Internal(InternalErrorKind::BadRequest),
        }
    }
}