1#![doc = include_str!("../README.md")]
2
3extern crate alloc;
4
5const PKG_VERSION: &str = if let Some(version) = option_env!("CARGO_PKG_VERSION") {
9 version
10} else {
11 ""
12};
13
14#[cfg(feature = "acme")]
16pub mod acme;
17#[cfg(feature = "async_tokio")]
18mod cert;
19mod config;
20mod control_dialer;
21mod derp;
22mod dial_plan;
23mod dns;
24#[cfg_attr(not(feature = "async_tokio"), expect(dead_code))]
25mod map_request_builder;
26mod node;
27#[cfg(feature = "async_tokio")]
28mod serve;
29mod service;
30mod ssh_policy;
31mod tka;
32#[cfg(feature = "async_tokio")]
33mod tokio;
34#[cfg(feature = "identity-federation")]
35pub mod wif;
36
37use std::fmt;
38
39#[cfg(feature = "async_tokio")]
40pub use cert::{
41 CertError, MISSING_CERT_RPC, certified_key_from_pem, get_certificate, is_tailnet_name,
42};
43#[cfg(feature = "acme")]
44pub use cert::{PublishTxt, SetDnsPublisher, issue_certificate_via_setdns};
45#[doc(inline)]
46pub use config::{
47 Config, DEFAULT_CONTROL_SERVER, DEFAULT_PERSISTENT_KEEPALIVE, ExitProxyConfig, ExitProxyScheme,
48 TransportMode, TunConfig, services_hash,
49};
50pub use control_dialer::{ControlDialer, TcpDialer, complete_connection};
51pub use derp::{Map as DerpMap, Region as DerpRegion, convert_derp_map};
52pub use dial_plan::{DialCandidate, DialMode, DialPlan};
53pub use dns::{DnsConfig, ExtraRecord, Resolver as DnsResolver, ResolverTransport};
54pub use node::{
55 ExitNodeSelector, Id as NodeId, Node, NodeCapMap, PeerChange, StableId as StableNodeId,
56 TailnetAddress, UserProfile, is_tailscale_ip, validate_service_name,
57};
58#[cfg(feature = "async_tokio")]
59pub use serve::{
60 FunnelError, FunnelOptions, MISSING_FUNNEL_RELAY, ServeConfig, ServeState, ServeTarget,
61 accept_tls, funnel_access, listen_funnel, listen_tls, tls_acceptor,
62};
63pub use service::{ServiceError, ServiceMode, resolve_service_listen};
64pub use ssh_policy::{
65 SshAccept, SshAction, SshConnIdentity, SshDecision, SshDenyReason, SshPolicy, SshPrincipal,
66 SshRule,
67};
68pub use tka::TkaStatus;
69pub use ts_control_serde::{
70 Endpoint, EndpointType, TkaBootstrapRequest, TkaBootstrapResponse, TkaSyncOfferRequest,
71 TkaSyncOfferResponse, TkaSyncSendRequest, TkaSyncSendResponse, UserId,
72};
73#[cfg(feature = "identity-federation")]
74pub use wif::{WifConfig, WifError, resolve_auth_key};
75
76#[cfg(feature = "async_tokio")]
80pub mod tls {
81 pub use tokio_rustls::{TlsAcceptor, rustls::sign::CertifiedKey, server::TlsStream};
82}
83
84#[cfg(feature = "async_tokio")]
85pub use crate::tokio::{
86 AsyncControlClient, FilterUpdate, IdTokenError, LogoutError, LogoutInternalErrorKind,
87 PeerUpdate, SetDnsError, SetDnsInternalErrorKind, StateUpdate, TkaSyncError,
88 TkaSyncInternalErrorKind, fetch_id_token, logout, set_dns, tka_bootstrap, tka_sync_offer,
89 tka_sync_send,
90};
91
92#[derive(Debug, thiserror::Error, Clone, Eq, PartialEq)]
94pub enum Error {
95 #[error("machine was not authorized by control to join tailnet, authorize at {0}")]
97 MachineNotAuthorized(url::Url),
98
99 #[error("invalid URL: {0}")]
101 InvalidUrl(url::Url),
102
103 #[error("control rejected registration: {0}")]
106 Registration(String),
107
108 #[error("a networking error occurred in {0}")]
114 NetworkError(Operation),
115
116 #[error("{0} error in {1}")]
121 Internal(InternalErrorKind, Operation),
122}
123
124impl Error {
125 fn io_error(err: std::io::Error, op: Operation) -> Self {
126 if crate::is_network_error(&err) {
127 Error::NetworkError(op)
128 } else {
129 Error::Internal(InternalErrorKind::Io, op)
130 }
131 }
132}
133
134#[non_exhaustive]
138#[derive(Debug, Clone, Copy, Eq, PartialEq)]
139pub enum InternalErrorKind {
140 Url,
142 Http,
144 SerDe,
146 Io,
148 MessageFormat,
150 Utf8,
152 NoiseHandshake,
154 Challenge,
156 MachineAuthorization,
159}
160
161impl fmt::Display for InternalErrorKind {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 match self {
164 InternalErrorKind::Url => write!(f, "URL parsing error"),
165 InternalErrorKind::Http => write!(f, "unsuccessful HTTP request or upgrade"),
166 InternalErrorKind::SerDe => write!(f, "serialization/deserialization error"),
167 InternalErrorKind::Io => write!(f, "I/O error"),
168 InternalErrorKind::MessageFormat => write!(f, "message format error"),
169 InternalErrorKind::Utf8 => write!(f, "invalid UTF8"),
170 InternalErrorKind::NoiseHandshake => write!(f, "error in Noise handshake"),
171 InternalErrorKind::Challenge => write!(f, "error with Tailscale challenge packet"),
172 InternalErrorKind::MachineAuthorization => {
173 write!(f, "machine not authorized to register with Tailnet")
174 }
175 }
176 }
177}
178
179#[derive(Debug, Clone, Copy, Eq, PartialEq)]
181pub enum Operation {
182 MapRequest,
184 ConnectToControlServer,
186 Registration,
188}
189
190impl fmt::Display for Operation {
191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192 match self {
193 Operation::MapRequest => write!(f, "net map request"),
194 Operation::ConnectToControlServer => write!(f, "connection to control server"),
195 Operation::Registration => write!(f, "registration"),
196 }
197 }
198}
199
200impl From<ts_http_util::Error> for Error {
201 fn from(error: ts_http_util::Error) -> Self {
202 tracing::error!(%error, "http error");
203
204 if http_error_is_recoverable(error) {
205 Error::NetworkError(Operation::ConnectToControlServer)
206 } else {
207 Error::Internal(InternalErrorKind::Http, Operation::ConnectToControlServer)
208 }
209 }
210}
211
212fn is_network_error(err: &std::io::Error) -> bool {
214 use std::io::ErrorKind::*;
215 matches!(
216 err.kind(),
217 ConnectionRefused
218 | ConnectionReset
219 | HostUnreachable
220 | NetworkUnreachable
221 | ConnectionAborted
222 | NotConnected
223 | TimedOut
224 | AddrNotAvailable
225 | Interrupted
226 | NetworkDown
227 )
228}
229
230fn http_error_is_recoverable(error: ts_http_util::Error) -> bool {
232 match error {
233 ts_http_util::Error::Io => true,
234 ts_http_util::Error::InvalidInput
235 | ts_http_util::Error::Timeout
238 | ts_http_util::Error::InvalidResponse => false,
239 ts_http_util::Error::ConnectionClosed => false,
241 }
242}