diff --git a/crates/tsunagi-cli/src/main.rs b/crates/tsunagi-cli/src/main.rs index e1df07d..d606ea0 100644 --- a/crates/tsunagi-cli/src/main.rs +++ b/crates/tsunagi-cli/src/main.rs @@ -424,7 +424,7 @@ struct UpArgs { long = "protocol", value_name = "LIST", value_delimiter = ',', - default_value = "wg-quic", + default_value = "tcp-tls,wg-quic", help_heading = "Transport" )] protocols: Vec, @@ -1175,6 +1175,12 @@ struct ProtocolSpec { /// Every protocol this build has. const PROTOCOLS: &[ProtocolSpec] = &[ + ProtocolSpec { + name: TCP_TLS_PROTOCOL, + version: TCP_TLS_VERSION, + summary: "Noise E2EE carried over TCP TLS 1.3 (port 443) disguised as HTTPS traffic", + options: TcpTlsCodec::OPTIONS, + }, ProtocolSpec { name: tsunagi_wg_quic::WIREGUARD_PROTOCOL, version: tsunagi_wg_quic::ANNOUNCEMENT_VERSION, @@ -1182,12 +1188,6 @@ const PROTOCOLS: &[ProtocolSpec] = &[ crosses NAT and survives where plain WireGuard is blocked", options: WireguardPlugin::OPTIONS, }, - ProtocolSpec { - name: TCP_TLS_PROTOCOL, - version: TCP_TLS_VERSION, - summary: "Noise E2EE carried over TCP TLS 1.3 (port 443) disguised as HTTPS traffic", - options: TcpTlsCodec::OPTIONS, - }, ]; /// One `-o` setting, and the protocol it was aimed at. diff --git a/crates/tsunagi-tcp-tls/src/transport.rs b/crates/tsunagi-tcp-tls/src/transport.rs index 0fa4d68..c9d6fce 100644 --- a/crates/tsunagi-tcp-tls/src/transport.rs +++ b/crates/tsunagi-tcp-tls/src/transport.rs @@ -354,12 +354,12 @@ impl TcpTlsTransport { tracing::info!(port = target_port, "TCP TLS listening"); Some(Arc::new(l)) } - Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => { + Err(err) if requested_port.is_none() => { let exe = std::env::current_exe() .map(|p| p.display().to_string()) .unwrap_or_else(|_| "tsunagi".into()); - if requested_port.is_none() { + if err.kind() == std::io::ErrorKind::PermissionDenied { tracing::warn!( port = target_port, %err, @@ -368,23 +368,49 @@ impl TcpTlsTransport { sudo setcap cap_net_admin,cap_net_bind_service+p {exe}\n\ Falling back to port {FALLBACK_PORT}." ); - - let fallback_addr = SocketAddr::from(([0, 0, 0, 0], FALLBACK_PORT)); - let l = TcpListener::bind(fallback_addr).await?; - tracing::info!(port = FALLBACK_PORT, "TCP TLS listening on fallback port"); - Some(Arc::new(l)) } else { tracing::warn!( port = target_port, %err, - "Cannot bind to requested port {target_port} without privileges. \ - To allow port {target_port} and TUN on Linux, grant capabilities once with:\n \ - sudo setcap cap_net_admin,cap_net_bind_service+p {exe}" + "Cannot bind to default port {target_port}: {err}. \ + Falling back to port {FALLBACK_PORT}." ); - return Err(Box::new(err)); + } + + let fallback_addr = SocketAddr::from(([0, 0, 0, 0], FALLBACK_PORT)); + match TcpListener::bind(fallback_addr).await { + Ok(l) => { + tracing::info!(port = FALLBACK_PORT, "TCP TLS listening on fallback port"); + Some(Arc::new(l)) + } + Err(fallback_err) => { + tracing::warn!( + port = FALLBACK_PORT, + err = %fallback_err, + "Cannot bind to fallback port {FALLBACK_PORT}: {fallback_err}. \ + Falling back to ephemeral port." + ); + let ephemeral_addr = SocketAddr::from(([0, 0, 0, 0], 0)); + let l = TcpListener::bind(ephemeral_addr).await?; + let bound = l.local_addr()?.port(); + tracing::info!(port = bound, "TCP TLS listening on ephemeral port"); + Some(Arc::new(l)) + } } } - Err(err) => return Err(Box::new(err)), + Err(err) => { + let exe = std::env::current_exe() + .map(|p| p.display().to_string()) + .unwrap_or_else(|_| "tsunagi".into()); + tracing::warn!( + port = target_port, + %err, + "Cannot bind to requested port {target_port}: {err}. \ + To allow port {target_port} and TUN on Linux, grant capabilities once with:\n \ + sudo setcap cap_net_admin,cap_net_bind_service+p {exe}" + ); + return Err(Box::new(err)); + } }; let bound_port = listener