From 4ee171bdc81762eee19665444e989c77eb2b146b Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 30 Aug 2026 23:36:02 +0800 Subject: [PATCH 01/62] Define Nowhere protocol versions --- src/protocol/mod.rs | 2 ++ src/protocol/version.rs | 52 +++++++++++++++++++++++++++++++++++ src/tests/protocol/version.rs | 16 +++++++++++ 3 files changed, 70 insertions(+) create mode 100644 src/protocol/version.rs create mode 100644 src/tests/protocol/version.rs diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 215d512..5db4c7c 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -10,6 +10,7 @@ mod request; mod result; mod uot; mod util; +mod version; pub use auth::{ AUTH_FRAME_LEN, AUTH_TAG_LEN, AuthFrame, AuthKey, AuthTransport, Credentials, TLS_EXPORTER_LEN, @@ -42,3 +43,4 @@ pub use uot::{ UOT_HEADER_LEN, UOT_PACKET_MAX, encode_udp_packet, encode_udp_packet_header, read_udp_packet, read_udp_packet_into, write_udp_packet, }; +pub use version::{ProtocolVersion, SUPPORTED_ALPNS, V1_ALPN, V2_ALPN}; diff --git a/src/protocol/version.rs b/src/protocol/version.rs new file mode 100644 index 0000000..d1f6f20 --- /dev/null +++ b/src/protocol/version.rs @@ -0,0 +1,52 @@ +// Copyright (C) 2026 NodePassProject +// SPDX-License-Identifier: GPL-3.0-only + +//! Data-plane protocol version negotiated through TLS ALPN. + +use anyhow::{Result, bail}; +use serde::{Deserialize, Serialize}; + +/// ALPN used by Nowhere 1.x peers with their default configuration. +pub const V1_ALPN: &[u8] = b"now/1"; +/// Fixed ALPN for Nowhere 2.x peers. +pub const V2_ALPN: &[u8] = b"nw2"; +/// Supported ALPNs in server-preference order. +pub const SUPPORTED_ALPNS: [&[u8]; 2] = [V2_ALPN, V1_ALPN]; + +/// Data-plane protocol version selected for one authenticated carrier. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum ProtocolVersion { + V1, + V2, +} + +impl ProtocolVersion { + /// Classifies an ALPN selected by TLS or QUIC. + pub fn from_alpn(alpn: Option<&[u8]>) -> Result { + match alpn { + Some(V2_ALPN) => Ok(Self::V2), + Some(V1_ALPN) => Ok(Self::V1), + Some(_) => bail!("protocol::ProtocolVersion: unsupported negotiated ALPN"), + None => bail!("protocol::ProtocolVersion: peer did not negotiate ALPN"), + } + } + + /// Stable short label used by local telemetry and diagnostics. + pub const fn as_str(self) -> &'static str { + match self { + Self::V1 => "v1", + Self::V2 => "v2", + } + } +} + +impl std::fmt::Display for ProtocolVersion { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str(self.as_str()) + } +} + +#[cfg(test)] +#[path = "../tests/protocol/version.rs"] +mod tests; diff --git a/src/tests/protocol/version.rs b/src/tests/protocol/version.rs new file mode 100644 index 0000000..c179049 --- /dev/null +++ b/src/tests/protocol/version.rs @@ -0,0 +1,16 @@ +use super::*; + +#[test] +fn alpn_mapping_is_fixed_and_ordered_for_v2_preference() { + assert_eq!(SUPPORTED_ALPNS, [b"nw2".as_slice(), b"now/1".as_slice()]); + assert_eq!( + ProtocolVersion::from_alpn(Some(V2_ALPN)).unwrap(), + ProtocolVersion::V2 + ); + assert_eq!( + ProtocolVersion::from_alpn(Some(V1_ALPN)).unwrap(), + ProtocolVersion::V1 + ); + assert!(ProtocolVersion::from_alpn(Some(b"private/2")).is_err()); + assert!(ProtocolVersion::from_alpn(None).is_err()); +} From feb87126d730b5e14a21dbc4a77779a0dcb96c4f Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 30 Aug 2026 23:36:15 +0800 Subject: [PATCH 02/62] Negotiate versions across client transports --- src/common/alpn.rs | 13 +----- src/common/mod.rs | 2 +- src/common/tls.rs | 10 ++--- src/main.rs | 2 - src/tests/common/alpn.rs | 12 ------ src/tests/common/tls.rs | 6 +-- src/tests/main.rs | 3 +- src/tests/vector/config.rs | 21 +++++----- src/tests/vector/session/tls.rs | 17 ++++++-- src/tests/vector/tls.rs | 71 ++++++++++++++++++++++++++++++--- src/transport/quic.rs | 2 +- src/vector/config.rs | 14 ++----- src/vector/flow.rs | 11 +++-- src/vector/flow/tcp.rs | 15 +++++++ src/vector/mod.rs | 3 +- src/vector/session.rs | 10 +++-- src/vector/session/quic.rs | 3 ++ src/vector/session/tls.rs | 65 +++++++++++++++++------------- src/vector/tls.rs | 29 +++++++++----- src/vector/udp_flow.rs | 15 ++++++- 20 files changed, 206 insertions(+), 118 deletions(-) diff --git a/src/common/alpn.rs b/src/common/alpn.rs index 7e4516e..e396f7d 100644 --- a/src/common/alpn.rs +++ b/src/common/alpn.rs @@ -1,21 +1,10 @@ // Copyright (C) 2026 NodePassProject // SPDX-License-Identifier: GPL-3.0-only -//! Configurable ALPN and the TLS Mux wire marker. +//! TLS Mux wire marker. -use anyhow::{Result, bail}; - -pub(crate) const DEFAULT_ALPN: &str = "now/1"; pub(crate) const MUX_MARKER: u8 = 0xff; -pub(crate) fn parse_alpn(value: Option<&str>) -> Result { - let value = value.unwrap_or(DEFAULT_ALPN); - if value.is_empty() || value.len() > u8::MAX as usize { - bail!("alpn length must be 1..255 bytes"); - } - Ok(value.to_owned()) -} - #[cfg(test)] #[path = "../tests/common/alpn.rs"] mod tests; diff --git a/src/common/mod.rs b/src/common/mod.rs index cd13f35..cfb558e 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -13,7 +13,7 @@ mod network; pub(crate) mod socks; mod tls; -pub(crate) use alpn::{MUX_MARKER, parse_alpn}; +pub(crate) use alpn::MUX_MARKER; pub(crate) use config::first_raw_query_value; pub use config::{ DEFAULT_DIALER_IP, DEFAULT_MAX_TCP_FLOWS, DEFAULT_MAX_UDP_FLOWS, DEFAULT_RATE_LIMIT, diff --git a/src/common/tls.rs b/src/common/tls.rs index 90ac2c6..b39889e 100644 --- a/src/common/tls.rs +++ b/src/common/tls.rs @@ -15,6 +15,8 @@ use quinn::crypto::rustls::QuicServerConfig; use rustls::crypto::ring; use url::Url; +use crate::protocol::SUPPORTED_ALPNS; + pub(crate) use self::tls_cert::certificate_sha256; use self::tls_cert::{ReloadingCertResolver, new_self_signed_cert}; use super::{Logger, query_first, reload_interval}; @@ -44,18 +46,16 @@ impl fmt::Display for TLSMode { } } -/// Builds rustls and QUIC TLS server configuration for the configured ALPN. +/// Builds rustls and QUIC TLS server configuration for supported protocol versions. pub fn new_server_configs( parsed_url: &Url, - alpn: &str, logger: Logger, ) -> Result<(TLSMode, Arc, quinn::ServerConfig)> { - new_server_configs_with_reload_interval(parsed_url, alpn, reload_interval(), logger) + new_server_configs_with_reload_interval(parsed_url, reload_interval(), logger) } pub(crate) fn new_server_configs_with_reload_interval( parsed_url: &Url, - alpn: &str, reload_interval: Duration, logger: Logger, ) -> Result<(TLSMode, Arc, quinn::ServerConfig)> { @@ -109,7 +109,7 @@ pub(crate) fn new_server_configs_with_reload_interval( server_crypto.max_early_data_size = 0; server_crypto.send_half_rtt_data = false; - server_crypto.alpn_protocols = vec![alpn.as_bytes().to_vec()]; + server_crypto.alpn_protocols = SUPPORTED_ALPNS.iter().map(|alpn| alpn.to_vec()).collect(); let quic_crypto = QuicServerConfig::try_from(server_crypto.clone()) .map_err(|e| anyhow!("common::tls::new_server_configs: QUIC TLS config failed: {e}"))?; logger.event(format_args!("CERT_SHA256|{cert_sha256}")); diff --git a/src/main.rs b/src/main.rs index 8e1d16b..27a2ded 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,7 +62,6 @@ Portal parameters: tls=0 is not supported. crt= PEM certificate chain for tls=2. key= PEM private key for tls=2. - alpn= Exact TLS/QUIC ALPN. Default: now/1. rate= Client-to-target traffic limit. 0 disables it. etar= Target-to-client traffic limit. 0 disables it. dial= Local source IP for outbound target connections. Default: auto. @@ -81,7 +80,6 @@ Portal parameters: Vector parameters: up=tcp|udp Upload carrier. Default: udp. down=tcp|udp Download carrier. Default: udp. - alpn= Exact TLS/QUIC ALPN. Default: now/1. mux=0|1 Use TLS Mux for TLS/TCP carriers. Default: 0. sni= Verify the certificate for a DNS name. Empty, omitted, or none disables certificate validation. Default: none. diff --git a/src/tests/common/alpn.rs b/src/tests/common/alpn.rs index bc0f9ff..675cbe0 100644 --- a/src/tests/common/alpn.rs +++ b/src/tests/common/alpn.rs @@ -3,18 +3,6 @@ use super::*; -#[test] -fn alpn_defaults_to_now_one_and_preserves_custom_values() { - assert_eq!(parse_alpn(None).unwrap(), DEFAULT_ALPN); - assert_eq!(parse_alpn(Some("private/7")).unwrap(), "private/7"); -} - -#[test] -fn alpn_rejects_empty_and_oversized_values() { - assert!(parse_alpn(Some("")).is_err()); - assert!(parse_alpn(Some(&"a".repeat(256))).is_err()); -} - #[test] fn mux_marker_cannot_start_a_dedicated_flow_header() { assert!(crate::protocol::decode_flow_header(&[MUX_MARKER, 0, 0, 0, 1]).is_err()); diff --git a/src/tests/common/tls.rs b/src/tests/common/tls.rs index 04d9291..96f09f7 100644 --- a/src/tests/common/tls.rs +++ b/src/tests/common/tls.rs @@ -19,7 +19,6 @@ fn certificate_sha256_uses_lowercase_hex() { fn server_tls_config_explicitly_disables_early_data() { let (_, tls, _quic) = new_server_configs( &Url::parse("portal://secret@127.0.0.1:2077?tls=1").unwrap(), - "now/1", Logger::new(LogLevel::None, false), ) .unwrap(); @@ -29,13 +28,12 @@ fn server_tls_config_explicitly_disables_early_data() { } #[test] -fn server_offers_only_the_configured_alpn() { +fn server_prefers_v2_and_accepts_default_v1() { let (_, tls, _) = new_server_configs( &Url::parse("portal://secret@127.0.0.1:2077?tls=1&alpn=private/2&pool=8").unwrap(), - "private/2", Logger::new(LogLevel::None, false), ) .unwrap(); - assert_eq!(tls.alpn_protocols, [b"private/2".to_vec()]); + assert_eq!(tls.alpn_protocols, [b"nw2".to_vec(), b"now/1".to_vec()]); } diff --git a/src/tests/main.rs b/src/tests/main.rs index 1cccf59..6edbad1 100644 --- a/src/tests/main.rs +++ b/src/tests/main.rs @@ -21,7 +21,6 @@ fn help_text_documents_usage_and_configuration_surface() { "next=", "sni=", "pin=", - "alpn=", "mux=0|1", "Use TLS Mux for the native upstream.", "UDP ASSOCIATE", @@ -47,7 +46,7 @@ fn help_text_documents_usage_and_configuration_surface() { "missing help text: {expected}" ); } - for removed in ["pool=", "NOW_QUIC_MAX_UDP_FLOWS"] { + for removed in ["alpn=", "pool=", "NOW_QUIC_MAX_UDP_FLOWS"] { assert!( !HELP_TEXT.contains(removed), "removed help option: {removed}" diff --git a/src/tests/vector/config.rs b/src/tests/vector/config.rs index ec85914..4edf2de 100644 --- a/src/tests/vector/config.rs +++ b/src/tests/vector/config.rs @@ -9,7 +9,6 @@ fn defaults_to_quic_both_directions() { let config = parse("vector://secret@example.com:2077?socks=:1080").unwrap(); assert_eq!(config.up, CarrierMode::Udp); assert_eq!(config.down, CarrierMode::Udp); - assert_eq!(config.alpn, "now/1"); assert_eq!(config.mux, MuxMode::Disabled); assert_eq!(config.sni, None); assert_eq!(config.pin, None); @@ -50,9 +49,8 @@ fn ignores_unknown_values_and_keeps_the_first_duplicate() { .unwrap(); assert_eq!(config.up, CarrierMode::Tcp); assert_eq!(config.down, CarrierMode::Tcp); - assert_eq!(config.alpn, "private/2"); assert_eq!(config.socks.port, 1080); - assert!(config.effective_url().contains("alpn=private/2")); + assert!(!config.effective_url().contains("alpn=")); assert!(!config.effective_url().contains("pool=")); } @@ -89,14 +87,13 @@ fn effective_url_uses_canonical_order_and_prints_identity_options() { .unwrap(); assert_eq!( config.effective_url(), - "vector://example.com:2077?up=tcp&down=tcp&alpn=private&mux=1&sni=relay.example&pin=abc&rate=1&etar=2&socks=:1080" + "vector://example.com:2077?up=tcp&down=tcp&mux=1&sni=relay.example&pin=abc&rate=1&etar=2&socks=:1080" ); } #[test] -fn validates_alpn_and_mux_inputs() { +fn ignores_removed_alpn_and_validates_mux_inputs() { for raw in [ - "vector://secret@example.com:2077?socks=:1080&alpn=", "vector://secret@example.com:2077?socks=:1080&mux=", "vector://secret@example.com:2077?socks=:1080&mux=2", "vector://secret@example.com:2077?socks=:1080&mux=true", @@ -104,13 +101,13 @@ fn validates_alpn_and_mux_inputs() { ] { assert!(parse(raw).is_err(), "URL unexpectedly accepted: {raw}"); } - let oversized = "a".repeat(256); - assert!( - parse(&format!( - "vector://secret@example.com:2077?socks=:1080&alpn={oversized}" + for alpn in [String::new(), "a".repeat(256)] { + let config = parse(&format!( + "vector://secret@example.com:2077?socks=:1080&alpn={alpn}" )) - .is_err() - ); + .unwrap(); + assert!(!config.effective_url().contains("alpn=")); + } } #[test] diff --git a/src/tests/vector/session/tls.rs b/src/tests/vector/session/tls.rs index 62abd32..7368900 100644 --- a/src/tests/vector/session/tls.rs +++ b/src/tests/vector/session/tls.rs @@ -4,6 +4,13 @@ use super::*; use tokio::io::AsyncReadExt; +fn shard(handle: MuxHandle) -> TlsMux { + TlsMux { + handle, + version: ProtocolVersion::V2, + } +} + #[tokio::test] async fn shard_selection_stops_at_four_active_flows() { let (left, right) = tokio::io::duplex(1 << 20); @@ -16,9 +23,11 @@ async fn shard_selection_stops_at_four_active_flows() { streams.push(handle.open_stream(flow_id).await.unwrap()); peers.push(incoming.accept().await.unwrap().unwrap()); } + let shard = shard(handle.clone()); assert!( - select_available_mux(std::slice::from_ref(&handle)) + select_available_mux(std::slice::from_ref(&shard)) .unwrap() + .handle .same_carrier(&handle) ); @@ -29,7 +38,7 @@ async fn shard_selection_stops_at_four_active_flows() { .unwrap(), ); peers.push(incoming.accept().await.unwrap().unwrap()); - assert!(select_available_mux(std::slice::from_ref(&handle)).is_none()); + assert!(select_available_mux(std::slice::from_ref(&shard)).is_none()); } #[tokio::test] @@ -48,8 +57,8 @@ async fn shard_selection_uses_the_least_loaded_carrier() { let _stream_b = handle_b.open_stream(3).await.unwrap(); let _peer_b = incoming_b.accept().await.unwrap().unwrap(); - let selected = select_available_mux(&[handle_a, handle_b.clone()]).unwrap(); - assert!(selected.same_carrier(&handle_b)); + let selected = select_available_mux(&[shard(handle_a), shard(handle_b.clone())]).unwrap(); + assert!(selected.handle.same_carrier(&handle_b)); } #[tokio::test] diff --git a/src/tests/vector/tls.rs b/src/tests/vector/tls.rs index 97e8039..a42b91c 100644 --- a/src/tests/vector/tls.rs +++ b/src/tests/vector/tls.rs @@ -16,7 +16,7 @@ fn config(raw: &str) -> PortalClientConfig { } #[tokio::test] -async fn client_negotiates_the_configured_alpn() { +async fn client_prefers_fixed_v2_alpn() { let generated = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap(); let certificate: CertificateDer<'static> = generated.cert.into(); let key = PrivateKeyDer::from(PrivatePkcs8KeyDer::from( @@ -29,7 +29,7 @@ async fn client_negotiates_the_configured_alpn() { .with_no_client_auth() .with_single_cert(vec![certificate], key) .unwrap(); - server.alpn_protocols = vec![b"private/2".to_vec()]; + server.alpn_protocols = vec![b"nw2".to_vec(), b"now/1".to_vec()]; let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let endpoint = listener.local_addr().unwrap(); let server_task = tokio::spawn(async move { @@ -38,11 +38,12 @@ async fn client_negotiates_the_configured_alpn() { }); let raw = format!("vector://secret@{endpoint}?alpn=private/2&socks=127.0.0.1:1080"); - let (_, _) = ClientTls::new(&config(&raw)) + let (_, _, version) = ClientTls::new(&config(&raw)) .unwrap() .connect_tcp(&endpoint.to_string(), "auto") .await .unwrap(); + assert_eq!(version, ProtocolVersion::V2); server_task.await.unwrap().unwrap(); } @@ -83,7 +84,7 @@ enum TestPin { Invalid, } -async fn test_pinned_handshake(pin: TestPin, sni: Option<&str>) -> Result<()> { +async fn test_pinned_handshake(pin: TestPin, sni: Option<&str>) -> Result { let generated = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap(); let certificate: CertificateDer<'static> = generated.cert.into(); let fingerprint = certificate_sha256(&certificate); @@ -124,11 +125,71 @@ async fn test_pinned_handshake(pin: TestPin, sni: Option<&str>) -> Result<()> { let result = ClientTls::new(&config(&raw))? .connect_tcp(&endpoint.to_string(), "auto") .await - .map(|_| ()); + .map(|(_, _, version)| version); let _ = tokio::time::timeout(Duration::from_secs(1), server_task).await; result } +#[tokio::test] +async fn default_v1_tcp_server_is_detected() { + assert_eq!( + test_pinned_handshake(TestPin::Omitted, None).await.unwrap(), + ProtocolVersion::V1 + ); +} + +async fn negotiate_quic(server_alpns: Vec>) -> Result { + let generated = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap(); + let certificate: CertificateDer<'static> = generated.cert.into(); + let key = PrivateKeyDer::from(PrivatePkcs8KeyDer::from( + generated.signing_key.serialize_der(), + )); + let mut rustls_server = + rustls::ServerConfig::builder_with_provider(Arc::new(ring::default_provider())) + .with_protocol_versions(&[&rustls::version::TLS13]) + .unwrap() + .with_no_client_auth() + .with_single_cert(vec![certificate], key) + .unwrap(); + rustls_server.alpn_protocols = server_alpns; + let quic_server = quinn::crypto::rustls::QuicServerConfig::try_from(rustls_server).unwrap(); + let server = quinn::Endpoint::server( + quinn::ServerConfig::with_crypto(Arc::new(quic_server)), + "127.0.0.1:0".parse().unwrap(), + ) + .unwrap(); + let address = server.local_addr().unwrap(); + let server_task = tokio::spawn(async move { + let incoming = server.accept().await.unwrap(); + incoming.await + }); + + let raw = format!("vector://secret@{address}?socks=127.0.0.1:1080"); + let tls = ClientTls::new(&config(&raw))?; + let mut client = quinn::Endpoint::client("127.0.0.1:0".parse().unwrap())?; + client.set_default_client_config(tls.quic_client_config()?); + let connection = client.connect(address, &tls.quic_server_name())?.await?; + let version = quic_protocol_version(&connection)?; + connection.close(quinn::VarInt::from_u32(0), b""); + let _ = server_task.await; + Ok(version) +} + +#[tokio::test] +async fn quic_prefers_v2_and_falls_back_to_default_v1() { + assert_eq!( + negotiate_quic(vec![b"nw2".to_vec(), b"now/1".to_vec()]) + .await + .unwrap(), + ProtocolVersion::V2 + ); + assert_eq!( + negotiate_quic(vec![b"now/1".to_vec()]).await.unwrap(), + ProtocolVersion::V1 + ); + assert!(negotiate_quic(vec![b"private/2".to_vec()]).await.is_err()); +} + #[tokio::test] async fn exact_pin_overrides_sni_certificate_verification() { test_pinned_handshake(TestPin::Exact, Some("wrong.example")) diff --git a/src/transport/quic.rs b/src/transport/quic.rs index 8b2fb95..7c8d8a6 100644 --- a/src/transport/quic.rs +++ b/src/transport/quic.rs @@ -1,7 +1,7 @@ // Copyright (C) 2026 NodePassProject // SPDX-License-Identifier: GPL-3.0-only -//! Shared QUIC flow-control budgets for every ALPN and Mux setting. +//! Shared QUIC flow-control budgets for every protocol version and Mux setting. use anyhow::{Result, bail}; diff --git a/src/vector/config.rs b/src/vector/config.rs index e0ca20b..9577fa0 100644 --- a/src/vector/config.rs +++ b/src/vector/config.rs @@ -12,10 +12,10 @@ use url::Url; use crate::common::socks::{ SocksCredentials, first_raw_socks_value, format_host_port, parse_host_port, parse_socks_value, }; -use crate::common::{DEFAULT_DIALER_IP, parse_alpn, query_first}; +use crate::common::{DEFAULT_DIALER_IP, query_first}; const VECTOR_QUERY_KEYS: &[&str] = &[ - "up", "down", "alpn", "mux", "sni", "pin", "rate", "etar", "socks", "log", + "up", "down", "mux", "sni", "pin", "rate", "etar", "socks", "log", ]; /// Whether a client originates dedicated or Mux TLS carriers. @@ -70,7 +70,6 @@ pub(crate) struct PortalClientConfig { pub(crate) remote_port: u16, pub(crate) up: CarrierMode, pub(crate) down: CarrierMode, - pub(crate) alpn: String, pub(crate) mux: MuxMode, pub(crate) sni: Option, pub(crate) pin: Option, @@ -92,8 +91,6 @@ impl PortalClientConfig { .ok_or_else(|| anyhow!("vector::config: missing Portal port"))?; let up = CarrierMode::parse(query.get("up").map(String::as_str), "up")?; let down = CarrierMode::parse(query.get("down").map(String::as_str), "down")?; - let alpn = parse_alpn(query.get("alpn").map(String::as_str)) - .map_err(|error| anyhow!("vector::config: {error}"))?; let mux = MuxMode::parse(query.get("mux").map(String::as_str)) .map_err(|error| anyhow!("vector::config: {error}"))?; let sni = query @@ -119,7 +116,6 @@ impl PortalClientConfig { remote_port, up, down, - alpn, mux, sni, pin, @@ -219,7 +215,6 @@ pub(crate) struct VectorConfig { pub(super) remote_port: u16, pub(super) up: CarrierMode, pub(super) down: CarrierMode, - pub(super) alpn: String, pub(super) mux: MuxMode, pub(super) sni: Option, pub(super) pin: Option, @@ -257,7 +252,6 @@ impl VectorConfig { remote_port: portal.remote_port, up: portal.up, down: portal.down, - alpn: portal.alpn, mux: portal.mux, sni: portal.sni, pin: portal.pin, @@ -273,7 +267,6 @@ impl VectorConfig { remote_port: self.remote_port, up: self.up, down: self.down, - alpn: self.alpn.clone(), mux: self.mux, sni: self.sni.clone(), pin: self.pin.clone(), @@ -296,11 +289,10 @@ impl VectorConfig { pub(super) fn effective_url(&self) -> String { format!( - "vector://{}?up={}&down={}&alpn={}&mux={}&sni={}&pin={}&rate={}&etar={}&socks={}", + "vector://{}?up={}&down={}&mux={}&sni={}&pin={}&rate={}&etar={}&socks={}", self.portal_endpoint(), self.up, self.down, - self.alpn, self.mux, self.sni.as_deref().unwrap_or("none"), self.pin.as_deref().unwrap_or("none"), diff --git a/src/vector/flow.rs b/src/vector/flow.rs index 6580983..8bab05d 100644 --- a/src/vector/flow.rs +++ b/src/vector/flow.rs @@ -22,8 +22,8 @@ use crate::common::socks::{ use crate::common::{LatencyGuard, flow_setup_timeout, handshake_timeout, tcp_read_timeout}; use crate::protocol::{ AUTH_FRAME_LEN, AuthFrame, Carrier, FLOW_HEADER_LEN, FlowHeader, FlowKind, FlowResult, - FlowRole, SetupResult, TARGET_MAX_ENCODED_LEN, Target, encode_target_into, read_flow_result, - write_flow_header, + FlowRole, ProtocolVersion, SetupResult, TARGET_MAX_ENCODED_LEN, Target, encode_target_into, + read_flow_result, write_flow_header, }; use crate::telemetry::{AccessOutcome, AccessSpan, RuntimeEvent, RuntimeKind, RuntimeLevel}; @@ -47,6 +47,7 @@ pub(super) struct PhysicalLane { _link: Option, _latency: Option, pub(super) _quic: Option>, + pub(super) version: ProtocolVersion, } impl PhysicalLane { @@ -100,7 +101,7 @@ pub(super) async fn open_lane( error })?; match opened { - OpenedTls::Mux(stream) => { + OpenedTls::Mux(stream, version) => { let (reader, writer) = stream.into_split(); Ok(PhysicalLane { reader: Some(Box::pin(reader)), @@ -110,6 +111,7 @@ pub(super) async fn open_lane( _link: None, _latency: None, _quic: None, + version, }) } OpenedTls::Dedicated(lane) => { @@ -122,6 +124,7 @@ pub(super) async fn open_lane( _link: Some(parts.link), _latency: Some(parts.latency), _quic: None, + version: parts.version, }) } } @@ -150,6 +153,7 @@ pub(super) async fn open_lane( } }; let pending_quic_auth = pending_auth.is_some(); + let version = session.version; Ok(PhysicalLane { reader: Some(Box::pin(reader)), writer: Some(Box::pin(writer)), @@ -158,6 +162,7 @@ pub(super) async fn open_lane( _link: None, _latency: None, _quic: Some(session), + version, }) } } diff --git a/src/vector/flow/tcp.rs b/src/vector/flow/tcp.rs index 7343d47..197f520 100644 --- a/src/vector/flow/tcp.rs +++ b/src/vector/flow/tcp.rs @@ -12,6 +12,7 @@ pub(crate) struct TcpTunnel { _lease: Option, uplink: Carrier, downlink: Carrier, + version: ProtocolVersion, _flow_permit: Option, } @@ -30,6 +31,10 @@ impl TcpTunnel { (self.uplink, self.downlink) } + pub(crate) fn protocol_version(&self) -> ProtocolVersion { + self.version + } + pub(crate) fn into_parts(self) -> (BoxReader, BoxWriter, TcpTunnelGuard) { let Self { reader, @@ -38,6 +43,7 @@ impl TcpTunnel { _lease, uplink: _, downlink: _, + version: _, _flow_permit, } = self; ( @@ -113,6 +119,7 @@ pub(crate) async fn open_tcp( downlink, hops, }; + let version = lane.version; let pending_auth = lane.take_pending_auth(); write_open_request( lane.writer.as_mut().expect("lane writer"), @@ -135,6 +142,7 @@ pub(crate) async fn open_tcp( _lease: Some(lease), uplink, downlink, + version, _flow_permit: Some(flow_permit), }); } @@ -150,6 +158,12 @@ pub(crate) async fn open_tcp( ); let mut uplink_lane = uplink_result.map_err(OpenFlowError::Transport)?; let mut downlink_lane = downlink_result.map_err(OpenFlowError::Transport)?; + if uplink_lane.version != downlink_lane.version { + return Err(OpenFlowError::Protocol(anyhow!( + "vector::flow::open_tcp: split carriers negotiated different protocol versions" + ))); + } + let version = uplink_lane.version; let open_header = FlowHeader { role: FlowRole::Open, flow_id, @@ -194,6 +208,7 @@ pub(crate) async fn open_tcp( _lease: Some(lease), uplink, downlink, + version, _flow_permit: Some(flow_permit), }) } diff --git a/src/vector/mod.rs b/src/vector/mod.rs index fd67d5e..7838699 100644 --- a/src/vector/mod.rs +++ b/src/vector/mod.rs @@ -186,11 +186,10 @@ impl Vector { let credentials = Credentials::new(&parsed_url).context("vector::Vector::new: invalid shared key")?; let telemetry_summary = format!( - "portal={} up={} down={} alpn={} mux={} socks={}", + "portal={} up={} down={} mux={} socks={}", config.portal_endpoint(), config.up, config.down, - config.alpn, config.mux, config.socks.endpoint(), ); diff --git a/src/vector/session.rs b/src/vector/session.rs index 132207f..8cd665c 100644 --- a/src/vector/session.rs +++ b/src/vector/session.rs @@ -28,14 +28,14 @@ use crate::common::{ use crate::mux::{MUX_IDLE_TIMEOUT, MuxConfig, MuxHandle, MuxStream}; use crate::protocol::{ AuthFrame, AuthKey, AuthTransport, Credentials, DatagramReassembler, FlowId, OwnedUdpFragment, - OwnedUdpFrame, ReassemblyConfig, ReassemblyOutcome, SessionId, decode_udp_frame_owned, - encode_auth_frame, encode_udp_close, + OwnedUdpFrame, ProtocolVersion, ReassemblyConfig, ReassemblyOutcome, SessionId, + decode_udp_frame_owned, encode_auth_frame, encode_udp_close, }; use crate::telemetry::{RuntimeEvent, RuntimeKind, RuntimeLevel, TelemetryHub}; use crate::transport::{Stats, quic_flow_control}; use super::config::PortalClientConfig; -use super::tls::{ClientTls, EXPORTER_LABEL}; +use super::tls::{ClientTls, EXPORTER_LABEL, quic_protocol_version}; const QUIC_DATAGRAM_BUFFER_SIZE: usize = 4 * 1024 * 1024; const TLS_MUX_FLOWS_PER_SHARD: usize = 4; @@ -63,6 +63,7 @@ impl ClientSignals { pub(super) struct TlsLane { pub(super) stream: TlsStream, + pub(super) version: ProtocolVersion, pending_auth: Option, _link: LinkGuard, latency: LatencyGuard, @@ -74,12 +75,14 @@ pub(super) struct TlsLaneParts { pub(super) pending_auth: Option, pub(super) link: LinkGuard, pub(super) latency: LatencyGuard, + pub(super) version: ProtocolVersion, } impl TlsLane { pub(super) fn into_parts(self) -> TlsLaneParts { let Self { stream, + version, pending_auth, _link, latency, @@ -91,6 +94,7 @@ impl TlsLane { pending_auth, link: _link, latency, + version, } } } diff --git a/src/vector/session/quic.rs b/src/vector/session/quic.rs index 4ea8607..885314a 100644 --- a/src/vector/session/quic.rs +++ b/src/vector/session/quic.rs @@ -130,6 +130,7 @@ impl QuicManager { .await .map_err(|_| anyhow!("vector::session::QuicManager: QUIC handshake timeout"))? .context("vector::session::QuicManager: QUIC handshake failed")?; + let version = quic_protocol_version(&connection)?; let mut exporter = [0u8; crate::protocol::TLS_EXPORTER_LEN]; connection .export_keying_material(&mut exporter, EXPORTER_LABEL, b"") @@ -160,6 +161,7 @@ impl QuicManager { queue_budget: Arc::new(Semaphore::new(self.queue_bytes)), _link: LinkGuard::new(self.stats.clone(), self.telemetry.clone(), true), latency, + version, }); spawn_datagram_loop(Arc::downgrade(&session), self.shutdown.clone()); Ok(session) @@ -188,6 +190,7 @@ pub(in crate::vector) struct QuicSession { queue_budget: Arc, _link: LinkGuard, latency: LatencyGuard, + pub(in crate::vector) version: ProtocolVersion, } pub(in crate::vector) type QueuedDatagram = BudgetedDatagram; diff --git a/src/vector/session/tls.rs b/src/vector/session/tls.rs index 9768457..725e887 100644 --- a/src/vector/session/tls.rs +++ b/src/vector/session/tls.rs @@ -12,8 +12,8 @@ pub(in crate::vector) struct TlsManager { stats: Arc, telemetry: Arc, latency: Arc, - up_mux: Mutex>, - down_mux: Mutex>, + up_mux: Mutex>, + down_mux: Mutex>, up_mux_connect: Mutex<()>, down_mux_connect: Mutex<()>, mux_enabled: bool, @@ -27,7 +27,13 @@ pub(in crate::vector) enum MuxDirection { pub(in crate::vector) enum OpenedTls { Dedicated(Box), - Mux(MuxStream), + Mux(MuxStream, ProtocolVersion), +} + +#[derive(Clone)] +struct TlsMux { + handle: MuxHandle, + version: ProtocolVersion, } impl TlsManager { @@ -71,16 +77,18 @@ impl TlsManager { // exact: the selected shard records its new stream before the next // opener observes load. let _opening = self.mux_connect(direction).lock().await; - if let Some(handle) = self.available_mux(direction).await { - return handle + if let Some(shard) = self.available_mux(direction).await { + return shard + .handle .open_stream(flow_id) .await - .map(OpenedTls::Mux) + .map(|stream| OpenedTls::Mux(stream, shard.version)) .map_err(Into::into); } let lane = self.connect_lane().await?; let TlsLane { mut stream, + version, pending_auth, _link, latency, @@ -96,22 +104,24 @@ impl TlsManager { stream.flush().await?; let (handle, incoming) = MuxHandle::start(stream, MuxConfig::default())?; drop(incoming); - self.mux(direction).lock().await.push(handle.clone()); + let shard = TlsMux { handle, version }; + self.mux(direction).lock().await.push(shard.clone()); let manager = self.clone(); - let lifetime = handle.clone(); + let lifetime = shard.clone(); tokio::spawn(async move { manager .monitor_mux(direction, lifetime, _link, latency) .await; }); - handle + shard + .handle .open_stream(flow_id) .await - .map(OpenedTls::Mux) + .map(|stream| OpenedTls::Mux(stream, version)) .map_err(Into::into) } - fn mux(&self, direction: MuxDirection) -> &Mutex> { + fn mux(&self, direction: MuxDirection) -> &Mutex> { match direction { MuxDirection::Up => &self.up_mux, MuxDirection::Down => &self.down_mux, @@ -125,49 +135,49 @@ impl TlsManager { } } - async fn available_mux(&self, direction: MuxDirection) -> Option { + async fn available_mux(&self, direction: MuxDirection) -> Option { let mut muxes = self.mux(direction).lock().await; - muxes.retain(|handle| !handle.is_closed()); + muxes.retain(|shard| !shard.handle.is_closed()); select_available_mux(&muxes) } async fn monitor_mux( self: Arc, direction: MuxDirection, - handle: MuxHandle, + shard: TlsMux, _link: LinkGuard, _latency: LatencyGuard, ) { loop { tokio::select! { - _ = handle.closed() => break, - idle = handle.idle_for(MUX_IDLE_TIMEOUT) => { + _ = shard.handle.closed() => break, + idle = shard.handle.idle_for(MUX_IDLE_TIMEOUT) => { if !idle { break; } } } let _opening = self.mux_connect(direction).lock().await; - if handle.active_streams() != 0 { + if shard.handle.active_streams() != 0 { continue; } - self.remove_mux(direction, &handle).await; - handle.close(); + self.remove_mux(direction, &shard).await; + shard.handle.close(); return; } let _opening = self.mux_connect(direction).lock().await; - self.remove_mux(direction, &handle).await; + self.remove_mux(direction, &shard).await; } - async fn remove_mux(&self, direction: MuxDirection, handle: &MuxHandle) { + async fn remove_mux(&self, direction: MuxDirection, shard: &TlsMux) { self.mux(direction) .lock() .await - .retain(|candidate| !candidate.same_carrier(handle)); + .retain(|candidate| !candidate.handle.same_carrier(&shard.handle)); } async fn connect_lane(&self) -> Result { - let (stream, exporter) = self + let (stream, exporter, version) = self .tls .connect_tcp(&self.endpoint, &self.dialer_ip) .await?; @@ -181,6 +191,7 @@ impl TlsManager { ); Ok(TlsLane { stream, + version, pending_auth: Some(auth), _link: LinkGuard::new(self.stats.clone(), self.telemetry.clone(), false), latency, @@ -188,12 +199,12 @@ impl TlsManager { } } -fn select_available_mux(muxes: &[MuxHandle]) -> Option { +fn select_available_mux(muxes: &[TlsMux]) -> Option { muxes .iter() - .filter(|handle| !handle.is_closed()) - .min_by_key(|handle| handle.active_streams()) - .filter(|handle| handle.active_streams() < TLS_MUX_FLOWS_PER_SHARD) + .filter(|shard| !shard.handle.is_closed()) + .min_by_key(|shard| shard.handle.active_streams()) + .filter(|shard| shard.handle.active_streams() < TLS_MUX_FLOWS_PER_SHARD) .cloned() } diff --git a/src/vector/tls.rs b/src/vector/tls.rs index b6e7476..e3828d6 100644 --- a/src/vector/tls.rs +++ b/src/vector/tls.rs @@ -7,7 +7,8 @@ use std::fmt; use std::sync::Arc; use anyhow::{Context, Result, anyhow, bail}; -use quinn::crypto::rustls::QuicClientConfig; +use quinn::Connection; +use quinn::crypto::rustls::{HandshakeData, QuicClientConfig}; use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; use rustls::crypto::{ WebPkiSupportedAlgorithms, ring, verify_tls12_signature, verify_tls13_signature, @@ -19,7 +20,7 @@ use tokio::time::timeout; use tokio_rustls::{TlsConnector, client::TlsStream}; use crate::common::{certificate_sha256, dial_tcp_from_local_ip, handshake_timeout}; -use crate::protocol::{TLS_EXPORTER_LEN, TlsExporter}; +use crate::protocol::{ProtocolVersion, SUPPORTED_ALPNS, TLS_EXPORTER_LEN, TlsExporter}; use super::config::PortalClientConfig; @@ -30,7 +31,6 @@ pub(super) const EXPORTER_LABEL: &[u8] = b"EXPORTER-Nowhere-Auth"; pub(super) struct ClientTls { rustls: Arc, server_name: ServerName<'static>, - alpn: Vec, } impl ClientTls { @@ -75,8 +75,7 @@ impl ClientTls { })) .with_no_client_auth() }; - let alpn = config.alpn.as_bytes().to_vec(); - client.alpn_protocols = vec![alpn.clone()]; + client.alpn_protocols = SUPPORTED_ALPNS.iter().map(|alpn| alpn.to_vec()).collect(); client.enable_early_data = false; let server_name = ServerName::try_from( @@ -90,7 +89,6 @@ impl ClientTls { Ok(Self { rustls: Arc::new(client), server_name, - alpn, }) } @@ -112,7 +110,7 @@ impl ClientTls { &self, endpoint: &str, dialer_ip: &str, - ) -> Result<(TlsStream, TlsExporter)> { + ) -> Result<(TlsStream, TlsExporter, ProtocolVersion)> { let stream = dial_tcp_from_local_ip(dialer_ip, endpoint, handshake_timeout()) .await .with_context(|| format!("vector::tls::connect_tcp: failed to dial {endpoint}"))?; @@ -132,13 +130,22 @@ impl ClientTls { .1 .export_keying_material(&mut exporter, EXPORTER_LABEL, Some(&[])) .context("vector::tls::connect_tcp: TLS exporter failed")?; - if tls.get_ref().1.alpn_protocol() != Some(self.alpn.as_slice()) { - bail!("vector::tls::connect_tcp: Portal did not negotiate the configured ALPN"); - } - Ok((tls, exporter)) + let version = ProtocolVersion::from_alpn(tls.get_ref().1.alpn_protocol()) + .context("vector::tls::connect_tcp: invalid negotiated protocol")?; + Ok((tls, exporter, version)) } } +pub(super) fn quic_protocol_version(connection: &Connection) -> Result { + let handshake = connection + .handshake_data() + .ok_or_else(|| anyhow!("vector::tls: QUIC handshake data unavailable"))? + .downcast::() + .map_err(|_| anyhow!("vector::tls: unexpected QUIC handshake data"))?; + ProtocolVersion::from_alpn(handshake.protocol.as_deref()) + .context("vector::tls: invalid QUIC negotiated protocol") +} + #[derive(Clone)] struct PinnedCertificateVerification { pin: String, diff --git a/src/vector/udp_flow.rs b/src/vector/udp_flow.rs index bdd2373..54d2775 100644 --- a/src/vector/udp_flow.rs +++ b/src/vector/udp_flow.rs @@ -14,7 +14,9 @@ use tokio::sync::mpsc; use tokio::time::timeout; use crate::common::{UdpDatagramSend, handshake_timeout}; -use crate::protocol::{Carrier, FlowHeader, FlowKind, FlowRole, Target, write_udp_packet}; +use crate::protocol::{ + Carrier, FlowHeader, FlowKind, FlowRole, ProtocolVersion, Target, write_udp_packet, +}; use super::PortalClient; use super::config::CarrierMode; @@ -29,6 +31,7 @@ pub(crate) struct UdpTunnel { quic: Option>, pub(super) uplink: Carrier, pub(super) downlink: Carrier, + version: ProtocolVersion, sender: UdpTunnelSender, receiver: UdpTunnelReceiver, _lanes: Vec, @@ -41,6 +44,9 @@ impl UdpTunnel { pub(crate) fn carriers(&self) -> (Carrier, Carrier) { (self.uplink, self.downlink) } + pub(crate) fn protocol_version(&self) -> ProtocolVersion { + self.version + } pub(crate) async fn send(&mut self, payload: &[u8]) -> Result { self.sender.send(payload).await } @@ -223,6 +229,12 @@ pub(crate) async fn open_udp( }; let quic = lanes.iter().find_map(|lane| lane._quic.clone()); + let version = lanes[0].version; + if lanes.iter().any(|lane| lane.version != version) { + return Err(OpenFlowError::Protocol(anyhow::anyhow!( + "vector::udp_flow::open_udp: split carriers negotiated different protocol versions" + ))); + } let mut down_datagrams = if client.config.down == CarrierMode::Udp { Some( quic.as_ref() @@ -281,6 +293,7 @@ pub(crate) async fn open_udp( flow_id, uplink, downlink, + version, sender: UdpTunnelSender { flow_id, writer, From e93acb5a801cda73539ae691b2b0c41ed664b089 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 30 Aug 2026 23:36:24 +0800 Subject: [PATCH 03/62] Isolate Portal sessions by protocol version --- src/portal/conn.rs | 73 +++++++++----- src/portal/conn/auth.rs | 5 +- src/portal/conn/session.rs | 22 +++-- src/portal/conn/session_datagram.rs | 2 +- src/portal/conn/tcp/flow.rs | 11 ++- src/portal/conn/tcp/mod.rs | 31 +++--- src/portal/mod.rs | 1 - src/portal/pairing.rs | 28 +++--- src/portal/pairing/lifecycle.rs | 8 +- src/portal/pairing/link.rs | 14 +-- src/portal/pairing/state.rs | 23 ++++- src/portal/pairing/tcp.rs | 5 +- src/portal/pairing/udp.rs | 12 ++- src/portal/runtime.rs | 3 +- src/portal/setup.rs | 12 +-- src/tests/portal.rs | 10 +- src/tests/portal/pairing.rs | 7 +- src/tests/portal/pairing/rejection.rs | 4 +- src/tests/portal/pairing/replacement.rs | 2 +- src/tests/portal/pairing/version.rs | 124 ++++++++++++++++++++++++ 20 files changed, 291 insertions(+), 106 deletions(-) create mode 100644 src/tests/portal/pairing/version.rs diff --git a/src/portal/conn.rs b/src/portal/conn.rs index 4ea08d7..667644c 100644 --- a/src/portal/conn.rs +++ b/src/portal/conn.rs @@ -13,6 +13,7 @@ pub(in crate::portal) use self::session::QueuedDatagram; use std::sync::Arc; +use quinn::crypto::rustls::HandshakeData; use quinn::{Connection, Incoming, VarInt}; use tokio::time::timeout; use tokio_util::sync::CancellationToken; @@ -25,6 +26,7 @@ pub(super) use self::tcp::handle_tcp_incoming; use super::PortalInner; use super::admission::UnauthenticatedGuard; use crate::common::rate_limit_bytes_per_second; +use crate::protocol::ProtocolVersion; use crate::telemetry::{RuntimeEvent, RuntimeKind, RuntimeLevel}; pub(super) async fn handle_incoming( @@ -55,40 +57,61 @@ pub(super) async fn handle_incoming( // Keep them silent to avoid log amplification. Err(_) => return, }; - handle_connection(portal, conn, admission, shutdown).await; + let version = match conn + .handshake_data() + .and_then(|data| data.downcast::().ok()) + .and_then(|data| ProtocolVersion::from_alpn(data.protocol.as_deref()).ok()) + { + Some(version) => version, + None => { + conn.close(VarInt::from_u32(1), b"unsupported protocol"); + portal.logger.debug(format_args!( + "portal::conn::handle_incoming: invalid negotiated QUIC protocol" + )); + return; + } + }; + handle_connection(portal, conn, version, admission, shutdown).await; } /// Runs authentication and then dispatches accepted streams/datagrams. async fn handle_connection( portal: Arc, conn: Connection, + version: ProtocolVersion, admission: UnauthenticatedGuard, shutdown: CancellationToken, ) { let auth_deadline = authentication_deadline(portal.runtime.handshake_timeout); - let authenticated = - match authenticate_connection(portal.clone(), conn.clone(), auth_deadline, &shutdown).await - { - AuthenticationOutcome::Success(authenticated) => authenticated, - AuthenticationOutcome::Failure(err) => { - let (code, reason) = authentication_failure_close(); - conn.close(code, reason); - drop(admission); - portal.telemetry.emit_runtime( - RuntimeEvent::new( - RuntimeLevel::Warn, - RuntimeKind::Authentication, - format!("QUIC authentication failed: {err}"), - ) - .with_client(conn.remote_address().to_string()), - ); - portal.logger.error(format_args!( - "portal::conn::handle_connection: authentication failed: {err}" - )); - return; - } - AuthenticationOutcome::Shutdown => return, - }; + let authenticated = match authenticate_connection( + portal.clone(), + conn.clone(), + version, + auth_deadline, + &shutdown, + ) + .await + { + AuthenticationOutcome::Success(authenticated) => authenticated, + AuthenticationOutcome::Failure(err) => { + let (code, reason) = authentication_failure_close(); + conn.close(code, reason); + drop(admission); + portal.telemetry.emit_runtime( + RuntimeEvent::new( + RuntimeLevel::Warn, + RuntimeKind::Authentication, + format!("QUIC authentication failed: {err}"), + ) + .with_client(conn.remote_address().to_string()), + ); + portal.logger.error(format_args!( + "portal::conn::handle_connection: authentication failed: {err}" + )); + return; + } + AuthenticationOutcome::Shutdown => return, + }; if portal.drain.is_cancelled() { conn.close(VarInt::from_u32(0), b""); drop(admission); @@ -117,7 +140,7 @@ async fn handle_connection( let link_guard = portal .pairing .register_quic_link( - session.session_id, + session.session_key, portal.stats.clone(), link_replaced.clone(), ) diff --git a/src/portal/conn/auth.rs b/src/portal/conn/auth.rs index a33a575..6b358c4 100644 --- a/src/portal/conn/auth.rs +++ b/src/portal/conn/auth.rs @@ -14,6 +14,8 @@ use crate::protocol::{AuthTransport, read_auth_frame}; use super::session::PortalSession; use crate::portal::PortalInner; +use crate::portal::pairing::SessionKey; +use crate::protocol::ProtocolVersion; const AUTH_EXPORTER_LABEL: &[u8] = b"EXPORTER-Nowhere-Auth"; @@ -42,6 +44,7 @@ pub(super) enum AuthenticationOutcome { pub(super) async fn authenticate_connection( portal: Arc, conn: Connection, + version: ProtocolVersion, deadline: Instant, shutdown: &CancellationToken, ) -> AuthenticationOutcome { @@ -109,7 +112,7 @@ pub(super) async fn authenticate_connection( session: Arc::new(PortalSession::new( portal.clone(), conn.clone(), - session_id, + SessionKey::new(version, session_id), )), first_send, first_recv, diff --git a/src/portal/conn/session.rs b/src/portal/conn/session.rs index 104c51d..c41d880 100644 --- a/src/portal/conn/session.rs +++ b/src/portal/conn/session.rs @@ -19,11 +19,12 @@ use tokio::time::timeout; use crate::protocol::{ Carrier, DatagramReassembler, FlowErrorCode, FlowKind, FlowResult, FlowRole, ReassemblyConfig, - SessionId, read_flow_header, read_request, write_flow_result, + read_flow_header, read_request, write_flow_result, }; pub(in crate::portal) use self::flow::QueuedDatagram; use crate::portal::PortalInner; +use crate::portal::pairing::SessionKey; const FLOW_REJECT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(1); const UDP_REASSEMBLY_SLOTS: usize = 64; @@ -33,7 +34,7 @@ const UDP_REASSEMBLY_TTL: std::time::Duration = std::time::Duration::from_secs(1 pub(super) struct PortalSession { portal: Arc, conn: Connection, - pub(super) session_id: SessionId, + pub(super) session_key: SessionKey, quic_generation: AtomicU64, udp_flows: StdMutex>, udp_reassembler: StdMutex>, @@ -56,6 +57,7 @@ pub(in crate::portal) struct DatagramReadyRequest { impl PortalSession { fn link_path(&self) -> crate::portal::pairing::LinkPath { crate::portal::pairing::LinkPath { + version: self.session_key.version, peer: self.conn.remote_address().to_string(), local: self.conn.local_ip().map_or_else( || self.portal.endpoint_addr.clone(), @@ -65,7 +67,7 @@ impl PortalSession { } /// Creates session state for one authenticated QUIC connection. - pub(super) fn new(portal: Arc, conn: Connection, session_id: SessionId) -> Self { + pub(super) fn new(portal: Arc, conn: Connection, session_key: SessionKey) -> Self { let (udp_ready_tx, udp_ready_rx) = mpsc::channel(64); let udp_reassembly_config = ReassemblyConfig { max_slots: UDP_REASSEMBLY_SLOTS, @@ -76,7 +78,7 @@ impl PortalSession { udp_queue_budget: Arc::new(Semaphore::new(portal.udp_flow_limits.queue_bytes)), portal, conn, - session_id, + session_key, quic_generation: AtomicU64::new(0), udp_flows: StdMutex::new(HashMap::new()), udp_reassembler: StdMutex::new(DatagramReassembler::new(udp_reassembly_config)), @@ -155,7 +157,7 @@ impl PortalSession { self.portal .pairing .reject_flow_setup( - self.session_id, + self.session_key, header.flow_id, FlowErrorCode::InvalidRequest, ) @@ -176,7 +178,7 @@ impl PortalSession { self.portal .pairing .reject_flow_setup( - self.session_id, + self.session_key, header.flow_id, FlowErrorCode::FlowLimit, ) @@ -193,7 +195,7 @@ impl PortalSession { self.portal .pairing .reject_flow_setup( - self.session_id, + self.session_key, header.flow_id, FlowErrorCode::InvalidRequest, ) @@ -219,7 +221,7 @@ impl PortalSession { match self .portal .pairing - .submit_tcp(self.session_id, header, target, link, reader, writer, None) + .submit_tcp(self.session_key, header, target, link, reader, writer, None) .await { Ok(Some(paired)) => { @@ -241,7 +243,7 @@ impl PortalSession { self.portal .pairing .reject_flow_setup( - self.session_id, + self.session_key, header.flow_id, FlowErrorCode::MetadataConflict, ) @@ -275,7 +277,7 @@ impl PortalSession { match self .portal .pairing - .submit_udp(self.session_id, header, target, link, half) + .submit_udp(self.session_key, header, target, link, half) .await { Ok(Some(paired)) => { diff --git a/src/portal/conn/session_datagram.rs b/src/portal/conn/session_datagram.rs index bea9e71..383f588 100644 --- a/src/portal/conn/session_datagram.rs +++ b/src/portal/conn/session_datagram.rs @@ -182,7 +182,7 @@ impl PortalSession { self.remove_udp_uplink(flow_id); self.portal .pairing - .cancel_udp(self.session_id, flow_id) + .cancel_udp(self.session_key, flow_id) .await; } diff --git a/src/portal/conn/tcp/flow.rs b/src/portal/conn/tcp/flow.rs index 9a319fd..1a40fa0 100644 --- a/src/portal/conn/tcp/flow.rs +++ b/src/portal/conn/tcp/flow.rs @@ -12,10 +12,10 @@ use tokio::time::timeout; use tokio_util::sync::CancellationToken; use crate::portal::PortalInner; -use crate::portal::pairing::{BoxReader, BoxWriter, LinkGuard}; +use crate::portal::pairing::{BoxReader, BoxWriter, LinkGuard, SessionKey}; use crate::protocol::{ - Carrier, FlowErrorCode, FlowKind, FlowResult, FlowRole, SessionId, read_flow_header, - read_request, write_flow_result, + Carrier, FlowErrorCode, FlowKind, FlowResult, FlowRole, read_flow_header, read_request, + write_flow_result, }; const FLOW_REJECT_TIMEOUT: Duration = Duration::from_secs(1); @@ -25,7 +25,7 @@ pub(super) async fn process_flow( portal: Arc, recv: R, mut send: W, - session_id: SessionId, + session_id: SessionKey, peer: SocketAddr, local: Option, shutdown: CancellationToken, @@ -79,6 +79,7 @@ pub(super) async fn process_flow( None }; let path = crate::portal::pairing::LinkPath { + version: session_id.version, peer: peer.to_string(), local: local.map_or_else(|| portal.endpoint_addr.clone(), |value| value.to_string()), }; @@ -180,7 +181,7 @@ where async fn reject_invalid( portal: &Arc, - session_id: SessionId, + session_id: SessionKey, role: FlowRole, flow_id: u32, writer: &mut W, diff --git a/src/portal/conn/tcp/mod.rs b/src/portal/conn/tcp/mod.rs index af19f07..3e7777f 100644 --- a/src/portal/conn/tcp/mod.rs +++ b/src/portal/conn/tcp/mod.rs @@ -23,7 +23,8 @@ use crate::common::MUX_MARKER; use crate::mux::{MUX_IDLE_TIMEOUT, MuxConfig, MuxHandle}; use crate::portal::PortalInner; use crate::portal::admission::UnauthenticatedGuard; -use crate::protocol::{AuthTransport, SessionId, read_auth_frame}; +use crate::portal::pairing::SessionKey; +use crate::protocol::{AuthTransport, ProtocolVersion, read_auth_frame}; use crate::telemetry::{RuntimeEvent, RuntimeKind, RuntimeLevel}; use self::flow::process_flow; @@ -115,12 +116,15 @@ pub(super) async fn handle_tcp_incoming_with_timeouts( }; let auth_deadline = authentication_deadline(portal.runtime.handshake_timeout); let mut tls_stream = tls_stream; - if tls_stream.get_ref().1.alpn_protocol() != Some(portal.alpn.as_bytes()) { - portal.logger.debug(format_args!( - "portal::conn::tcp: peer did not negotiate the configured ALPN" - )); - return; - } + let version = match ProtocolVersion::from_alpn(tls_stream.get_ref().1.alpn_protocol()) { + Ok(version) => version, + Err(err) => { + portal.logger.debug(format_args!( + "portal::conn::tcp: invalid negotiated protocol: {err}" + )); + return; + } + }; let mut exporter = [0u8; 32]; if let Err(err) = tls_stream.get_ref().1.export_keying_material( &mut exporter, @@ -165,6 +169,7 @@ pub(super) async fn handle_tcp_incoming_with_timeouts( } Err(_) => return, }; + let session_key = SessionKey::new(version, session_id); if let Err(err) = SockRef::from(tls_stream.get_ref().0).set_keepalive(true) { portal.logger.debug(format_args!( "portal::conn::tcp: TCP keepalive failed: {err}" @@ -185,7 +190,7 @@ pub(super) async fn handle_tcp_incoming_with_timeouts( handle_mux( portal, tls_stream, - session_id, + session_key, peer, local, shutdown, @@ -197,13 +202,13 @@ pub(super) async fn handle_tcp_incoming_with_timeouts( let link_guard = portal .pairing - .register_tcp_link(session_id, portal.stats.clone()); + .register_tcp_link(session_key, portal.stats.clone()); let (recv, send) = tokio::io::split(tls_stream); process_flow( portal, Cursor::new([first]).chain(recv), send, - session_id, + session_key, peer, local, shutdown, @@ -217,7 +222,7 @@ pub(super) async fn handle_tcp_incoming_with_timeouts( async fn handle_mux( portal: Arc, tls_stream: tokio_rustls::server::TlsStream, - session_id: SessionId, + session_key: SessionKey, peer: SocketAddr, local: Option, shutdown: CancellationToken, @@ -235,7 +240,7 @@ async fn handle_mux( }; let _link_guard = portal .pairing - .register_tcp_link(session_id, portal.stats.clone()); + .register_tcp_link(session_key, portal.stats.clone()); portal.telemetry.emit_runtime( RuntimeEvent::new( RuntimeLevel::Info, @@ -268,7 +273,7 @@ async fn handle_mux( portal, recv, send, - session_id, + session_key, peer, local, shutdown, diff --git a/src/portal/mod.rs b/src/portal/mod.rs index a351e1c..3aebfd8 100644 --- a/src/portal/mod.rs +++ b/src/portal/mod.rs @@ -44,7 +44,6 @@ pub struct Portal { struct PortalInner { credentials: Credentials, - alpn: String, tls_mode: TLSMode, network_mode: NetworkMode, endpoint_addr: String, diff --git a/src/portal/pairing.rs b/src/portal/pairing.rs index 01e7519..649721d 100644 --- a/src/portal/pairing.rs +++ b/src/portal/pairing.rs @@ -14,8 +14,7 @@ use tokio::io::AsyncWriteExt; use tokio::sync::{Mutex, OwnedSemaphorePermit}; use crate::protocol::{ - Carrier, FlowErrorCode, FlowHeader, FlowKind, FlowResult, FlowRole, SessionId, Target, - write_flow_result, + Carrier, FlowErrorCode, FlowHeader, FlowKind, FlowResult, FlowRole, Target, write_flow_result, }; mod lifecycle; @@ -28,7 +27,7 @@ pub(in crate::portal) use self::link::LinkGuard; pub(super) use self::link::{guarded_reader, guarded_writer}; pub(super) use self::state::{ BoxReader, BoxWriter, FlowLease, LinkHalf, LinkPath, PairedTcp, PairedUdp, QuicUdpReceiver, - UdpDown, UdpHalf, UdpUp, + SessionKey, UdpDown, UdpHalf, UdpUp, }; use self::state::{FlowClaim, FlowKey, LinkCounts, Metadata, PendingTcp, PendingUdp}; use self::tcp::reject_tcp_writer; @@ -69,7 +68,7 @@ impl std::error::Error for PairingError {} pub(super) struct PairingRegistry { pub(super) tcp: Mutex>, pub(super) udp: Mutex>, - pub(super) links: StdMutex>, + pub(super) links: StdMutex>, claims: StdMutex>, rejections: StdMutex>, accepting: AtomicBool, @@ -104,7 +103,7 @@ impl PairingRegistry { } } - fn active_quic_generation(&self, session_id: SessionId) -> Option { + fn active_quic_generation(&self, session_id: SessionKey) -> Option { self.links .lock() .expect("link registry poisoned") @@ -114,9 +113,9 @@ impl PairingRegistry { fn validate_current_link_locked( &self, - session_id: SessionId, + session_id: SessionKey, link: &LinkHalf, - links: &HashMap, + links: &HashMap, ) -> Result<(), PairingError> { let current = links.get(&session_id); let valid = match link.quic_generation { @@ -137,12 +136,18 @@ impl PairingRegistry { fn validate_header_and_link( &self, - session_id: SessionId, + session_id: SessionKey, header: FlowHeader, expected_kind: FlowKind, target: Option<&Target>, link: &LinkHalf, ) -> Result<(), PairingError> { + if link.path.version != session_id.version { + return Err(PairingError::new( + FlowErrorCode::MetadataConflict, + "portal::pairing: carrier protocol version mismatch", + )); + } if header.kind != expected_kind { return Err(PairingError::new( FlowErrorCode::InvalidRequest, @@ -346,7 +351,7 @@ impl PairingRegistry { fn acquire_udp_permit( &self, - session_id: SessionId, + session_id: SessionKey, ) -> Result, PairingError> { let budget = self .links @@ -385,12 +390,13 @@ impl PairingRegistry { /// Terminates a setup attempt and delivers the exact failure to an already /// selected downlink. If OPEN failed before ATTACH arrived, retain a short /// tombstone so the later selected downlink receives the same result. - pub(super) async fn reject_flow_setup( + pub(super) async fn reject_flow_setup>( self: &Arc, - session_id: SessionId, + session_id: S, flow_id: u32, code: FlowErrorCode, ) { + let session_id = session_id.into(); let key = FlowKey { session_id, flow_id, diff --git a/src/portal/pairing/lifecycle.rs b/src/portal/pairing/lifecycle.rs index 682ffb4..33f8d75 100644 --- a/src/portal/pairing/lifecycle.rs +++ b/src/portal/pairing/lifecycle.rs @@ -18,7 +18,7 @@ impl PairingRegistry { pub(in crate::portal) fn cancel_quic_generation( self: &Arc, - session_id: SessionId, + session_id: SessionKey, generation: u64, ) { self.cancel_active_quic_generation(session_id, generation); @@ -32,14 +32,14 @@ impl PairingRegistry { pub(in crate::portal) async fn replace_quic_generation( self: &Arc, - session_id: SessionId, + session_id: SessionKey, generation: u64, ) { self.cancel_active_quic_generation(session_id, generation); self.purge_quic_generation(session_id, generation).await; } - fn cancel_active_quic_generation(&self, session_id: SessionId, generation: u64) { + fn cancel_active_quic_generation(&self, session_id: SessionKey, generation: u64) { let claims = self.claims.lock().expect("flow claim registry poisoned"); for (key, claim) in claims.iter() { if key.session_id == session_id @@ -87,7 +87,7 @@ impl PairingRegistry { self.accepting.store(false, Ordering::Release); } - async fn purge_quic_generation(self: &Arc, session_id: SessionId, generation: u64) { + async fn purge_quic_generation(self: &Arc, session_id: SessionKey, generation: u64) { let mut stale = self .tcp .lock() diff --git a/src/portal/pairing/link.rs b/src/portal/pairing/link.rs index 22a7fd6..e547851 100644 --- a/src/portal/pairing/link.rs +++ b/src/portal/pairing/link.rs @@ -11,8 +11,8 @@ use std::task::{Context, Poll}; use tokio::io::{AsyncRead, AsyncWrite}; use super::PairingRegistry; +use super::SessionKey; use super::state::{ActiveQuic, BoxReader, BoxWriter}; -use crate::protocol::SessionId; use crate::transport::Stats; struct GuardedReader { @@ -76,7 +76,7 @@ pub(in crate::portal) fn guarded_writer( pub(in crate::portal) struct LinkGuard { registry: Arc, stats: Arc, - session_id: SessionId, + session_id: SessionKey, carrier: crate::protocol::Carrier, quic_generation: Option, } @@ -130,11 +130,12 @@ impl Drop for LinkGuard { } impl PairingRegistry { - pub(in crate::portal) fn register_tcp_link( + pub(in crate::portal) fn register_tcp_link>( self: &Arc, - session_id: SessionId, + session_id: S, stats: Arc, ) -> LinkGuard { + let session_id = session_id.into(); let mut links = self.links.lock().expect("link registry poisoned"); let counts = links .entry(session_id) @@ -152,12 +153,13 @@ impl PairingRegistry { } /// Registers the latest authenticated QUIC carrier for a transport bundle. - pub(in crate::portal) async fn register_quic_link( + pub(in crate::portal) async fn register_quic_link>( self: &Arc, - session_id: SessionId, + session_id: S, stats: Arc, replacement: tokio_util::sync::CancellationToken, ) -> LinkGuard { + let session_id = session_id.into(); let generation = self.next_quic_generation.fetch_add(1, Ordering::Relaxed); let previous = { let mut links = self.links.lock().expect("link registry poisoned"); diff --git a/src/portal/pairing/state.rs b/src/portal/pairing/state.rs index 4c8a386..1d36612 100644 --- a/src/portal/pairing/state.rs +++ b/src/portal/pairing/state.rs @@ -12,14 +12,32 @@ use tokio::io::{AsyncRead, AsyncWrite}; use tokio::sync::{OwnedSemaphorePermit, Semaphore, mpsc, oneshot}; use tokio_util::sync::CancellationToken; -use crate::protocol::{FlowKind, SessionId, Target}; +use crate::protocol::{FlowKind, ProtocolVersion, SessionId, Target}; pub(in crate::portal) type BoxReader = Pin>; pub(in crate::portal) type BoxWriter = Pin>; +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub(in crate::portal) struct SessionKey { + pub(in crate::portal) version: ProtocolVersion, + pub(in crate::portal) id: SessionId, +} + +impl SessionKey { + pub(in crate::portal) const fn new(version: ProtocolVersion, id: SessionId) -> Self { + Self { version, id } + } +} + +impl From for SessionKey { + fn from(id: SessionId) -> Self { + Self::new(ProtocolVersion::V2, id) + } +} + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub(in crate::portal) struct FlowKey { - pub(in crate::portal) session_id: SessionId, + pub(in crate::portal) session_id: SessionKey, pub(in crate::portal) flow_id: u32, } @@ -48,6 +66,7 @@ pub(in crate::portal) struct PendingTcp { #[derive(Clone, Debug, Eq, PartialEq)] pub(in crate::portal) struct LinkPath { + pub(in crate::portal) version: ProtocolVersion, pub(in crate::portal) peer: String, pub(in crate::portal) local: String, } diff --git a/src/portal/pairing/tcp.rs b/src/portal/pairing/tcp.rs index 7871609..c466ed3 100644 --- a/src/portal/pairing/tcp.rs +++ b/src/portal/pairing/tcp.rs @@ -20,9 +20,9 @@ impl PairingRegistry { clippy::too_many_arguments, reason = "the registry boundary keeps each owned stream half explicit" )] - pub(in crate::portal) async fn submit_tcp( + pub(in crate::portal) async fn submit_tcp>( self: &Arc, - session_id: SessionId, + session_id: S, header: FlowHeader, target: Option, link: LinkHalf, @@ -30,6 +30,7 @@ impl PairingRegistry { mut writer: Option, downlink_liveness: Option, ) -> Result, PairingError> { + let session_id = session_id.into(); if let Err(err) = self.validate_header_and_link(session_id, header, FlowKind::Tcp, target.as_ref(), &link) { diff --git a/src/portal/pairing/udp.rs b/src/portal/pairing/udp.rs index 11e1b21..c45945b 100644 --- a/src/portal/pairing/udp.rs +++ b/src/portal/pairing/udp.rs @@ -16,14 +16,15 @@ enum UdpInstallOutcome { } impl PairingRegistry { - pub(in crate::portal) async fn submit_udp( + pub(in crate::portal) async fn submit_udp>( self: &Arc, - session_id: SessionId, + session_id: S, header: FlowHeader, target: Option, link: LinkHalf, mut half: UdpHalf, ) -> Result, PairingError> { + let session_id = session_id.into(); if let Err(err) = self.validate_header_and_link(session_id, header, FlowKind::Udp, target.as_ref(), &link) { @@ -394,7 +395,12 @@ impl PairingRegistry { }); } - pub(in crate::portal) async fn cancel_udp(&self, session_id: SessionId, flow_id: u32) { + pub(in crate::portal) async fn cancel_udp>( + &self, + session_id: S, + flow_id: u32, + ) { + let session_id = session_id.into(); let key = FlowKey { session_id, flow_id, diff --git a/src/portal/runtime.rs b/src/portal/runtime.rs index d08c58f..05d388a 100644 --- a/src/portal/runtime.rs +++ b/src/portal/runtime.rs @@ -293,11 +293,10 @@ impl Portal { /// Returns the effective startup URL that is logged for operators. pub(super) fn effective_url(&self) -> String { let base = format!( - "portal://{}?net={}&tls={}&alpn={}&rate={}&etar={}&dial={}&socks={}&next={}", + "portal://{}?net={}&tls={}&rate={}&etar={}&dial={}&socks={}&next={}", self.inner.endpoint_addr, self.inner.network_mode, self.inner.tls_mode, - self.inner.alpn, self.inner.rate_limit, self.inner.etar_limit, self.inner.outbound.dialer_ip(), diff --git a/src/portal/setup.rs b/src/portal/setup.rs index 8bc41c5..d05909d 100644 --- a/src/portal/setup.rs +++ b/src/portal/setup.rs @@ -12,7 +12,7 @@ use url::Url; use crate::common::{ DEFAULT_RATE_LIMIT, LifeMode, LifeReason, LifeState, Lifecycle, Logger, OutboundDialer, SocksConfig, bind_udp_addrs, first_raw_query_value, init_dialer_ip, - new_server_configs_with_reload_interval, parse_alpn, query_first, rate_limit_bytes_per_second, + new_server_configs_with_reload_interval, query_first, rate_limit_bytes_per_second, }; use crate::protocol::Credentials; use crate::telemetry::{InstanceRole, TelemetryHub}; @@ -23,7 +23,7 @@ use super::listener::{configure_transport, format_endpoint_addr}; use super::{NetworkMode, Portal, PortalInner, UdpFlowLimits, admission, outbound::PortalOutbound}; const PORTAL_QUERY_PARAMETERS: &[&str] = &[ - "net", "tls", "crt", "key", "alpn", "rate", "etar", "dial", "socks", "next", "log", + "net", "tls", "crt", "key", "rate", "etar", "dial", "socks", "next", "log", ]; const PORTAL_UPSTREAM_PARAMETERS: &[&str] = &["up", "down", "mux", "sni", "pin"]; @@ -83,14 +83,11 @@ impl Portal { Credentials::new(&parsed_url).map_err(|e| anyhow::anyhow!("portal::new: {e}"))?; let runtime = super::config::PortalRuntimeConfig::from_env() .map_err(|e| anyhow::anyhow!("portal::new: invalid runtime configuration: {e}"))?; - let alpn = parse_alpn(query.get("alpn").map(String::as_str)) - .map_err(|error| anyhow::anyhow!("portal::new: {error}"))?; let network_mode = NetworkMode::from_url(&parsed_url).map_err(|e| anyhow::anyhow!("portal::new: {e}"))?; let (tls_mode, tls_server_config, mut quic_server_config) = new_server_configs_with_reload_interval( &parsed_url, - &alpn, runtime.reload_interval, logger.clone(), ) @@ -145,7 +142,7 @@ impl Portal { |(config, _)| format!("next={} {}", config.endpoint(), config.effective_route()), ); let telemetry_summary = format!( - "net={network_mode} tls={tls_mode} alpn={alpn} rate={rate_limit} etar={etar_limit} dial={dialer_ip} socks={socks_endpoint} {next_summary}", + "net={network_mode} tls={tls_mode} rate={rate_limit} etar={etar_limit} dial={dialer_ip} socks={socks_endpoint} {next_summary}", ); let telemetry = TelemetryHub::for_current_process( InstanceRole::Portal, @@ -168,7 +165,6 @@ impl Portal { Ok(Self { inner: Arc::new(PortalInner { credentials, - alpn, tls_mode, network_mode, endpoint_addr, @@ -205,7 +201,7 @@ impl Portal { fn validate_query(query: &std::collections::HashMap) -> Result<()> { for name in [ - "log", "tls", "crt", "key", "alpn", "net", "rate", "etar", "dial", "socks", + "log", "tls", "crt", "key", "net", "rate", "etar", "dial", "socks", ] { if query.get(name).is_some_and(String::is_empty) { anyhow::bail!("empty {name} parameter"); diff --git a/src/tests/portal.rs b/src/tests/portal.rs index dd1283c..c88d0d8 100644 --- a/src/tests/portal.rs +++ b/src/tests/portal.rs @@ -33,7 +33,7 @@ fn empty_host_listens_on_both_wildcard_families() { assert_eq!(portal.inner.network_mode, NetworkMode::Mix); assert_eq!( portal.effective_url(), - "portal://:2077?net=mix&tls=1&alpn=now/1&rate=0&etar=0&dial=127.0.0.1&socks=none&next=none" + "portal://:2077?net=mix&tls=1&rate=0&etar=0&dial=127.0.0.1&socks=none&next=none" ); } @@ -153,8 +153,6 @@ fn native_next_reuses_transport_identity_and_source_binding() { test_logger(), ) .unwrap(); - assert_eq!(portal.inner.alpn, "private/2"); - assert_eq!(portal.inner.outbound.dialer_ip(), "127.0.0.2"); assert_eq!(portal.inner.outbound.next_endpoint(), "[::1]:2080"); assert_eq!( @@ -300,7 +298,6 @@ fn portal_url_contract_rejects_invalid_structure_and_selected_values() { "portal://secret@127.0.0.1:2077/path", "portal://secret@127.0.0.1:2077#fragment", "portal://secret@127.0.0.1:2077?net=", - "portal://secret@127.0.0.1:2077?alpn=", "portal://secret@127.0.0.1:2077?socks=", "portal://secret@127.0.0.1:2077?rate=-1", "portal://secret@127.0.0.1:2077?dial=not-an-ip", @@ -326,9 +323,8 @@ fn portal_ignores_unknown_parameters_and_keeps_first_duplicate() { .unwrap(); assert_eq!(portal.inner.network_mode, NetworkMode::Tcp); assert_eq!(portal.inner.rate_limit, 1); - assert_eq!(portal.inner.alpn, "private/2"); assert!(portal.effective_url().contains("?net=tcp&tls=1&")); - assert!(portal.effective_url().contains("alpn=private/2")); + assert!(!portal.effective_url().contains("alpn=")); assert!(!portal.effective_url().contains("mux=")); assert!(!portal.effective_url().contains("pool=")); } @@ -344,7 +340,7 @@ fn portal_mux_is_ignored_without_next() { test_logger(), ) .unwrap(); - assert_eq!(portal.inner.alpn, "private/2"); + assert!(!portal.effective_url().contains("alpn=")); assert!(!portal.effective_url().contains("mux=")); } } diff --git a/src/tests/portal/pairing.rs b/src/tests/portal/pairing.rs index 1592b64..a25695c 100644 --- a/src/tests/portal/pairing.rs +++ b/src/tests/portal/pairing.rs @@ -5,7 +5,7 @@ use super::*; use crate::protocol::{ - Carrier, FlowKind, FlowResult, FlowRole, SESSION_ID_LEN, Target, encode_flow_result, + Carrier, FlowKind, FlowResult, FlowRole, SESSION_ID_LEN, SessionId, Target, encode_flow_result, read_flow_result, }; use crate::transport::Stats; @@ -65,6 +65,7 @@ fn target(value: &str) -> Target { fn path(label: &str) -> LinkPath { LinkPath { + version: crate::protocol::ProtocolVersion::V2, peer: format!("{label}.client:1234"), local: "portal.test:2077".into(), } @@ -83,7 +84,7 @@ fn available_udp_permits(registry: &PairingRegistry, session_id: SessionId) -> u .links .lock() .expect("link registry poisoned") - .get(&session_id) + .get(&session_id.into()) .expect("registered session") .udp_flow_budget .available_permits() @@ -130,3 +131,5 @@ mod rejection; mod replacement; #[path = "pairing/udp.rs"] mod udp; +#[path = "pairing/version.rs"] +mod version; diff --git a/src/tests/portal/pairing/rejection.rs b/src/tests/portal/pairing/rejection.rs index 13c6506..26edd45 100644 --- a/src/tests/portal/pairing/rejection.rs +++ b/src/tests/portal/pairing/rejection.rs @@ -111,7 +111,7 @@ async fn late_attach_receives_original_open_pair_timeout() { .is_none() ); let tcp_key = FlowKey { - session_id: tcp_session, + session_id: tcp_session.into(), flow_id: 16, }; tokio::time::timeout(Duration::from_secs(1), async { @@ -171,7 +171,7 @@ async fn late_attach_receives_original_open_pair_timeout() { .is_none() ); let udp_key = FlowKey { - session_id: udp_session, + session_id: udp_session.into(), flow_id: 17, }; tokio::time::timeout(Duration::from_secs(1), async { diff --git a/src/tests/portal/pairing/replacement.rs b/src/tests/portal/pairing/replacement.rs index 186bb08..c7bae51 100644 --- a/src/tests/portal/pairing/replacement.rs +++ b/src/tests/portal/pairing/replacement.rs @@ -234,7 +234,7 @@ async fn stale_open_after_map_lock_leaves_exact_rejection_for_tcp_attach() { .await }); tokio::time::timeout(Duration::from_secs(1), async { - while registry.active_quic_generation(session_id) == Some(old_generation) { + while registry.active_quic_generation(session_id.into()) == Some(old_generation) { tokio::task::yield_now().await; } }) diff --git a/src/tests/portal/pairing/version.rs b/src/tests/portal/pairing/version.rs new file mode 100644 index 0000000..2b868b7 --- /dev/null +++ b/src/tests/portal/pairing/version.rs @@ -0,0 +1,124 @@ +use super::*; +use crate::protocol::ProtocolVersion; + +fn versioned_half(version: ProtocolVersion, label: &str) -> LinkHalf { + LinkHalf::tcp(LinkPath { + version, + peer: format!("{label}.client:1234"), + local: "portal.test:2077".into(), + }) +} + +#[tokio::test] +async fn identical_session_and_flow_ids_are_isolated_by_protocol_version() { + let registry = registry(8, Duration::from_secs(30)); + let stats = Arc::new(Stats::default()); + let id = [41; SESSION_ID_LEN]; + let v1 = SessionKey::new(ProtocolVersion::V1, id); + let v2 = SessionKey::new(ProtocolVersion::V2, id); + let _v1_link = registry.register_tcp_link(v1, stats.clone()); + let _v2_link = registry.register_tcp_link(v2, stats); + + let (uplink, _uplink_peer) = tokio::io::duplex(64); + assert!( + registry + .submit_tcp( + v1, + header( + FlowRole::Open, + 9, + FlowKind::Tcp, + Carrier::TlsTcp, + Carrier::TlsTcp, + ), + Some(target("target.test:443")), + versioned_half(ProtocolVersion::V1, "v1"), + Some(Box::pin(uplink)), + None, + None, + ) + .await + .unwrap() + .is_none() + ); + + let (downlink, _downlink_peer) = tokio::io::duplex(64); + assert!( + registry + .submit_tcp( + v2, + header( + FlowRole::Attach, + 9, + FlowKind::Tcp, + Carrier::TlsTcp, + Carrier::TlsTcp, + ), + None, + versioned_half(ProtocolVersion::V2, "v2"), + None, + Some(Box::pin(downlink)), + None, + ) + .await + .unwrap() + .is_none() + ); + + let pending = registry.tcp.lock().await; + assert!(pending.contains_key(&FlowKey { + session_id: v1, + flow_id: 9 + })); + assert!(pending.contains_key(&FlowKey { + session_id: v2, + flow_id: 9 + })); + assert_eq!(pending.len(), 2); + drop(pending); + + registry + .reject_flow_setup(v1, 77, FlowErrorCode::DialFailed) + .await; + assert_eq!( + registry.terminal_rejection( + FlowKey { + session_id: v1, + flow_id: 77, + }, + false, + ), + Some(FlowErrorCode::DialFailed) + ); + assert_eq!( + registry.terminal_rejection( + FlowKey { + session_id: v2, + flow_id: 77, + }, + false, + ), + None + ); +} + +#[tokio::test] +async fn quic_replacement_is_scoped_to_protocol_version() { + let registry = registry(8, Duration::from_secs(30)); + let stats = Arc::new(Stats::default()); + let id = [42; SESSION_ID_LEN]; + let v1 = SessionKey::new(ProtocolVersion::V1, id); + let v2 = SessionKey::new(ProtocolVersion::V2, id); + let v1_replaced = tokio_util::sync::CancellationToken::new(); + + let _v1 = registry + .register_quic_link(v1, stats.clone(), v1_replaced.clone()) + .await; + let _v2 = registry + .register_quic_link(v2, stats, tokio_util::sync::CancellationToken::new()) + .await; + + assert!(!v1_replaced.is_cancelled()); + assert!(registry.active_quic_generation(v1).is_some()); + assert!(registry.active_quic_generation(v2).is_some()); +} From fb3e523de9dc4d2a48212014b29cb07224e49751 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 30 Aug 2026 23:36:33 +0800 Subject: [PATCH 04/62] Report negotiated wire versions --- src/portal/conn/relay_tcp.rs | 2 +- src/portal/conn/relay_uot.rs | 2 +- src/telemetry/hub.rs | 10 ++++++++-- src/telemetry/wire.rs | 10 +++++----- src/tests/portal/conn/relay.rs | 2 ++ src/tests/telemetry/hub.rs | 36 +++++++++++++++++++++++++++++++++- src/tests/telemetry/ipc.rs | 16 +++++++-------- src/tests/tui/client.rs | 4 ++-- src/tests/tui/render.rs | 6 +++--- src/tui/client/adapter.rs | 7 ++++--- src/tui/model/filter.rs | 4 ++++ src/tui/model/types.rs | 2 +- src/tui/render/feed.rs | 11 +++++++++++ src/vector/socks/server.rs | 3 ++- src/vector/socks/server/udp.rs | 5 +++-- 15 files changed, 90 insertions(+), 30 deletions(-) diff --git a/src/portal/conn/relay_tcp.rs b/src/portal/conn/relay_tcp.rs index 5f43deb..969a345 100644 --- a/src/portal/conn/relay_tcp.rs +++ b/src/portal/conn/relay_tcp.rs @@ -41,7 +41,7 @@ pub(in crate::portal) async fn relay_paired_tcp(portal: Arc, paired id: 0, timestamp_ms: now_unix_ms(), protocol: TrafficProtocol::Tcp, - alpn: portal.alpn.clone(), + wire_version: Some(uplink_path.version), flow_id: Some(flow_id.into()), session_tag: None, client: Some(uplink_path.peer.clone()), diff --git a/src/portal/conn/relay_uot.rs b/src/portal/conn/relay_uot.rs index a7f85e4..2635499 100644 --- a/src/portal/conn/relay_uot.rs +++ b/src/portal/conn/relay_uot.rs @@ -48,7 +48,7 @@ pub(in crate::portal) async fn relay_paired_udp(portal: Arc, paired id: 0, timestamp_ms: now_unix_ms(), protocol: TrafficProtocol::Udp, - alpn: portal.alpn.clone(), + wire_version: Some(uplink_path.version), flow_id: Some(flow_id.into()), session_tag: None, client: Some(uplink_path.peer.clone()), diff --git a/src/telemetry/hub.rs b/src/telemetry/hub.rs index 699645e..b1bbc00 100644 --- a/src/telemetry/hub.rs +++ b/src/telemetry/hub.rs @@ -173,7 +173,7 @@ impl TelemetryHub { id: start.id, timestamp_ms: start.timestamp_ms, protocol: start.protocol, - alpn: start.alpn, + wire_version: start.wire_version, flow_id: start.flow_id, session_tag: start.session_tag, client: start.client, @@ -213,7 +213,7 @@ impl TelemetryHub { timestamp_ms: now_unix_ms(), duration_ms: started_at.elapsed().as_millis().min(u64::MAX as u128) as u64, protocol: started.protocol, - alpn: started.alpn.clone(), + wire_version: started.wire_version, flow_id: started.flow_id, session_tag: started.session_tag.clone(), client: started.client.clone(), @@ -276,6 +276,12 @@ impl AccessSpan { } } + pub(crate) fn set_wire_version(&mut self, version: crate::protocol::ProtocolVersion) { + if let Some(started) = &mut self.started { + started.wire_version = Some(version); + } + } + pub(crate) fn finish(self, outcome: AccessOutcome, error: Option) { self.finish_once(outcome, error); } diff --git a/src/telemetry/wire.rs b/src/telemetry/wire.rs index 387919d..da5c17f 100644 --- a/src/telemetry/wire.rs +++ b/src/telemetry/wire.rs @@ -8,12 +8,12 @@ use std::time::Duration; use anyhow::Result; use serde::{Deserialize, Serialize}; -use crate::protocol::Carrier; +use crate::protocol::{Carrier, ProtocolVersion}; use super::process::{now_unix_ms, process_incarnation, process_uid}; /// Version of the local telemetry protocol. It is not a data-plane wire version. -pub(crate) const PROTOCOL_VERSION: u16 = 2; +pub(crate) const PROTOCOL_VERSION: u16 = 3; /// Maximum accepted JSON payload, excluding the four-byte length prefix. pub(crate) const MAX_FRAME_SIZE: usize = 64 * 1024; @@ -140,7 +140,7 @@ pub(crate) struct AccessStart { pub(crate) id: u64, pub(crate) timestamp_ms: u64, pub(crate) protocol: TrafficProtocol, - pub(crate) alpn: String, + pub(crate) wire_version: Option, pub(crate) flow_id: Option, pub(crate) session_tag: Option, pub(crate) client: Option, @@ -156,7 +156,7 @@ pub(crate) struct AccessStarted { pub(crate) id: u64, pub(crate) timestamp_ms: u64, pub(crate) protocol: TrafficProtocol, - pub(crate) alpn: String, + pub(crate) wire_version: Option, pub(crate) flow_id: Option, pub(crate) session_tag: Option, pub(crate) client: Option, @@ -174,7 +174,7 @@ pub(crate) struct AccessFinished { pub(crate) timestamp_ms: u64, pub(crate) duration_ms: u64, pub(crate) protocol: TrafficProtocol, - pub(crate) alpn: String, + pub(crate) wire_version: Option, pub(crate) flow_id: Option, pub(crate) session_tag: Option, pub(crate) client: Option, diff --git a/src/tests/portal/conn/relay.rs b/src/tests/portal/conn/relay.rs index 1291cf3..66089f2 100644 --- a/src/tests/portal/conn/relay.rs +++ b/src/tests/portal/conn/relay.rs @@ -22,10 +22,12 @@ fn symmetric_exchange_path( #[test] fn paired_path_contains_both_carriers_and_both_client_links() { let uplink = LinkPath { + version: crate::protocol::ProtocolVersion::V2, peer: "198.51.100.1:1000".into(), local: "192.0.2.1:2077".into(), }; let downlink = LinkPath { + version: crate::protocol::ProtocolVersion::V2, peer: "[2001:db8::2]:2000".into(), local: "[2001:db8::1]:2077".into(), }; diff --git a/src/tests/telemetry/hub.rs b/src/tests/telemetry/hub.rs index 9318e22..022147c 100644 --- a/src/tests/telemetry/hub.rs +++ b/src/tests/telemetry/hub.rs @@ -31,7 +31,7 @@ fn access_span_finishes_only_once() { id: 0, timestamp_ms: 1, protocol: TrafficProtocol::Tcp, - alpn: "now/1".to_owned(), + wire_version: Some(crate::protocol::ProtocolVersion::V1), flow_id: Some(7), session_tag: Some("abc123".to_owned()), client: Some("127.0.0.1:1".to_owned()), @@ -68,6 +68,40 @@ fn access_fields_are_not_built_without_a_receiver() { span.finish(AccessOutcome::Success, None); } +#[test] +fn negotiated_wire_version_is_added_to_completion() { + let hub = TelemetryHub::new(descriptor()); + let mut events = hub.event_receiver(); + let mut span = hub.start_access(|| AccessStart { + id: 0, + timestamp_ms: 1, + protocol: TrafficProtocol::Tcp, + wire_version: None, + flow_id: None, + session_tag: None, + client: None, + path_peers: Vec::new(), + target: "example:443".to_owned(), + initial_uplink: Some(Carrier::TlsTcp), + initial_downlink: Some(Carrier::TlsTcp), + path: None, + }); + span.set_wire_version(crate::protocol::ProtocolVersion::V2); + span.finish(AccessOutcome::Success, None); + + let Ok(ServerMessage::AccessStart(start)) = events.try_recv() else { + panic!("missing access start"); + }; + assert_eq!(start.wire_version, None); + let Ok(ServerMessage::AccessFinish(finish)) = events.try_recv() else { + panic!("missing access finish"); + }; + assert_eq!( + finish.wire_version, + Some(crate::protocol::ProtocolVersion::V2) + ); +} + #[test] fn snapshot_contains_existing_transport_counters() { let hub = TelemetryHub::new(descriptor()); diff --git a/src/tests/telemetry/ipc.rs b/src/tests/telemetry/ipc.rs index 6533df9..1362211 100644 --- a/src/tests/telemetry/ipc.rs +++ b/src/tests/telemetry/ipc.rs @@ -10,7 +10,7 @@ use crate::transport::Stats; fn parse_registry_name(name: &str) -> Option { let mut components = name.split('.'); - if components.next()? != "nowhere" || components.next()? != "v2" { + if components.next()? != "nowhere" || components.next()? != "v3" { return None; } let uid = components.next()?.parse().ok()?; @@ -28,23 +28,23 @@ fn parse_registry_name(name: &str) -> Option { } #[test] -fn parses_only_exact_v2_registry_names() { +fn parses_only_exact_v3_registry_names() { assert_eq!( - parse_registry_name("nowhere.v2.1000.42.900"), + parse_registry_name("nowhere.v3.1000.42.900"), Some(DiscoveredInstance { - registry_name: "nowhere.v2.1000.42.900".to_owned(), + registry_name: "nowhere.v3.1000.42.900".to_owned(), uid: 1000, pid: 42, incarnation: 900, }) ); - assert!(parse_registry_name("nowhere.v1.1000.42.900").is_none()); - assert!(parse_registry_name("nowhere.v2.1000.42").is_none()); - assert!(parse_registry_name("nowhere.v2.1000.42.900.extra").is_none()); + assert!(parse_registry_name("nowhere.v2.1000.42.900").is_none()); + assert!(parse_registry_name("nowhere.v3.1000.42").is_none()); + assert!(parse_registry_name("nowhere.v3.1000.42.900.extra").is_none()); } #[test] -fn v2_snapshot_round_trips_transport_counters() { +fn v3_snapshot_round_trips_transport_counters() { let snapshot = TelemetrySnapshot { tls_carriers_active: 2, quic_carriers_active: 3, diff --git a/src/tests/tui/client.rs b/src/tests/tui/client.rs index 736838a..e10f165 100644 --- a/src/tests/tui/client.rs +++ b/src/tests/tui/client.rs @@ -40,7 +40,7 @@ fn completion_inherits_access_path() { id: 9, timestamp_ms: 1, protocol: TrafficProtocol::Tcp, - alpn: "now/1".to_owned(), + wire_version: Some(crate::protocol::ProtocolVersion::V1), flow_id: None, session_tag: Some("abc123".to_owned()), client: Some("10.0.0.1:9".to_owned()), @@ -56,7 +56,7 @@ fn completion_inherits_access_path() { timestamp_ms: 2, duration_ms: 1, protocol: TrafficProtocol::Tcp, - alpn: "now/1".to_owned(), + wire_version: Some(crate::protocol::ProtocolVersion::V1), flow_id: None, session_tag: Some("abc123".to_owned()), client: Some("10.0.0.1:9".to_owned()), diff --git a/src/tests/tui/render.rs b/src/tests/tui/render.rs index 1381c6f..56794fd 100644 --- a/src/tests/tui/render.rs +++ b/src/tests/tui/render.rs @@ -25,7 +25,7 @@ fn app_with_instance() -> App { endpoint: "0.0.0.0:2077".to_owned(), config_summary: "net=mix tls=1".to_owned(), telemetry_interval_ms: 1_000, - telemetry_protocol_version: 2, + telemetry_protocol_version: 3, }, lifecycle: Lifecycle::Ready, snapshot: Some(TelemetrySnapshot { @@ -279,7 +279,7 @@ fn full_sidebar_keeps_long_lifecycle_labels_visible() { endpoint: "[::1]:1082".to_owned(), config_summary: "portal=relay.example:2077".to_owned(), telemetry_interval_ms: 1_000, - telemetry_protocol_version: 2, + telemetry_protocol_version: 3, }, lifecycle: Lifecycle::Starting, snapshot: None, @@ -294,7 +294,7 @@ fn full_sidebar_keeps_long_lifecycle_labels_visible() { fn selected_uses_available_height_for_complete_config() { let mut app = app_with_instance(); app.instances[0].meta.config_summary = - "net=mix tls=1 alpn=now/1 rate=0 etar=0 dial=auto socks=none next=origin.example:3077 up=udp down=tcp mux=0 sni=origin.example pin=present" + "net=mix tls=1 rate=0 etar=0 dial=auto socks=none next=origin.example:3077 up=udp down=tcp mux=0 sni=origin.example pin=present" .to_owned(); let output = rendered(160, 40, &app); diff --git a/src/tui/client/adapter.rs b/src/tui/client/adapter.rs index 4d419ce..f0efd62 100644 --- a/src/tui/client/adapter.rs +++ b/src/tui/client/adapter.rs @@ -138,7 +138,7 @@ pub(super) fn access_start_ui_value(value: AccessStarted) -> AccessRecord { TrafficProtocol::Udp => "UDP", } .to_owned(), - alpn: value.alpn, + wire_version: value.wire_version.map(|version| version.to_string()), session_tag: value.session_tag, client: value.client, path_peers: value.path_peers, @@ -161,7 +161,7 @@ pub(super) fn access_finish_ui_value( client, path_peers, target, - alpn, + wire_version, session_tag, initial_uplink, initial_downlink, @@ -176,7 +176,7 @@ pub(super) fn access_finish_ui_value( id, timestamp_ms, protocol, - alpn, + wire_version, flow_id, session_tag, client, @@ -189,6 +189,7 @@ pub(super) fn access_finish_ui_value( }); record.timestamp_ms = timestamp_ms; record.event_id = id; + record.wire_version = wire_version.map(|version| version.to_string()); record.phase = AccessPhase::Finish; let benign_end = error.as_deref().is_some_and(is_benign_access_end); record.status = Some(match outcome { diff --git a/src/tui/model/filter.rs b/src/tui/model/filter.rs index 5ab38ed..09ff684 100644 --- a/src/tui/model/filter.rs +++ b/src/tui/model/filter.rs @@ -12,6 +12,10 @@ pub fn access_matches(record: &AccessRecord, lowercase_filter: &str) -> bool { .to_ascii_lowercase() .contains(lowercase_filter) || record.route.to_ascii_lowercase().contains(lowercase_filter) + || record + .wire_version + .as_deref() + .is_some_and(|value| value.contains(lowercase_filter)) || record .client .as_deref() diff --git a/src/tui/model/types.rs b/src/tui/model/types.rs index e566187..ab573c6 100644 --- a/src/tui/model/types.rs +++ b/src/tui/model/types.rs @@ -133,7 +133,7 @@ pub struct AccessRecord { pub event_id: u64, pub phase: AccessPhase, pub protocol: String, - pub alpn: String, + pub wire_version: Option, pub session_tag: Option, pub client: Option, pub path_peers: Vec, diff --git a/src/tui/render/feed.rs b/src/tui/render/feed.rs index 5018301..81161d5 100644 --- a/src/tui/render/feed.rs +++ b/src/tui/render/feed.rs @@ -127,6 +127,17 @@ fn access_line(record: &AccessRecord, app: &App) -> Line<'static> { format!("{:<4}", record.protocol.to_ascii_uppercase()), accent(app, palette::protocol(&record.protocol)), ), + Span::styled( + format!( + "{:<3}", + record + .wire_version + .as_deref() + .unwrap_or("—") + .to_ascii_uppercase() + ), + dim(app), + ), Span::raw(" "), Span::styled(format!("{status:<4}"), accent(app, status_color)), Span::raw(format!( diff --git a/src/vector/socks/server.rs b/src/vector/socks/server.rs index cfa38d0..a0e8485 100644 --- a/src/vector/socks/server.rs +++ b/src/vector/socks/server.rs @@ -170,7 +170,7 @@ async fn handle_client( .await?; return Ok(()); } - let access = start_access( + let mut access = start_access( &vector, TrafficProtocol::Tcp, Some(peer.to_string()), @@ -179,6 +179,7 @@ async fn handle_client( let target = to_target(&request.address)?; match open_tcp(vector.client.clone(), &target, 0).await { Ok(tunnel) => { + access.set_wire_version(tunnel.protocol_version()); let reply = tunnel.socks_reply(); write_reply(&mut stream, reply, &SocksAddress::unspecified()).await?; tokio::select! { diff --git a/src/vector/socks/server/udp.rs b/src/vector/socks/server/udp.rs index 33a71c7..fd13aa0 100644 --- a/src/vector/socks/server/udp.rs +++ b/src/vector/socks/server/udp.rs @@ -146,7 +146,7 @@ async fn open_and_relay_udp_target( .lock() .unwrap_or_else(|lock| lock.into_inner()) .map(|endpoint| endpoint.to_string()); - let access = start_access(&vector, TrafficProtocol::Udp, source, &target); + let mut access = start_access(&vector, TrafficProtocol::Udp, source, &target); let protocol_target = match to_target(&target) { Ok(target) => target, Err(error) => { @@ -167,6 +167,7 @@ async fn open_and_relay_udp_target( } }, }; + access.set_wire_version(tunnel.protocol_version()); relay_udp_target( vector, UdpClientSide { @@ -353,7 +354,7 @@ pub(super) fn start_access( id: 0, timestamp_ms: now_unix_ms(), protocol, - alpn: vector.config.alpn.clone(), + wire_version: None, flow_id: None, session_tag: None, client, From 95ed210b73cce8eec5bf58849e4a4728f0d15ac7 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 30 Aug 2026 23:36:37 +0800 Subject: [PATCH 05/62] Document Nowhere 2.0 compatibility --- docs/README.md | 13 +++++++------ docs/compatibility.md | 28 +++++++++++++++++++--------- docs/configuration.md | 26 +++++++++++++------------- docs/integrations.md | 5 +++-- docs/operations.md | 4 ++-- docs/protocol.md | 10 ++++++---- docs/quick-start.md | 5 +++-- 7 files changed, 53 insertions(+), 38 deletions(-) diff --git a/docs/README.md b/docs/README.md index 1ffc704..0b7030e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,7 +10,7 @@ The documentation has one source of truth for each concern: | Implement or inspect the wire format | [Protocol](protocol.md) | | Deploy and observe the processes | [Operations](operations.md) | | Review authentication and memory bounds | [Security](security.md) | -| Understand ALPN and peer interoperability | [Interoperability](compatibility.md) | +| Understand version negotiation and peer interoperability | [Interoperability](compatibility.md) | | Implement another client or integration | [Integrations](integrations.md) | `protocol.md` is normative. Portal and Vector share one internal bounded TLS @@ -61,8 +61,9 @@ on one hop does not constrain the carrier choice on another hop. | `mux=0` | Dedicated lane per flow | Native streams/datagrams | One flow per carrier | | `mux=1` | Shared bounded Mux | Native streams/datagrams | Assigned flows close with the carrier | -ALPN defaults to `now/1` and is configurable independently from Mux. Peers use -the same exact ALPN. All four uplink/downlink carrier combinations use the same -FlowHeader, Target, pairing, and relay semantics. Portal accepts dedicated and -`0xff`-marked Mux connections on the same TLS listener. The client setting is -available on Vector and on Portal when `next` is enabled. +V2 peers negotiate the fixed `nw2` ALPN and fall back to the default V1 value +`now/1` for compatibility. All four uplink/downlink carrier combinations use +the same FlowHeader, Target, pairing, and relay semantics in this release. +Portal accepts dedicated and `0xff`-marked Mux connections on the same TLS +listener. The client Mux setting is available on Vector and on Portal when +`next` is enabled. diff --git a/docs/compatibility.md b/docs/compatibility.md index 47f3914..52efe66 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -1,11 +1,21 @@ # Interoperability -## ALPN contract +## Version negotiation -Portal and Vector advertise one exact TLS 1.3 ALPN. The default is `now/1`; -`alpn=` selects another nonempty value up to 255 bytes. Peers must use -the same value for TLS/TCP and QUIC/UDP. ALPN does not select a protocol version -or enable Mux. +Nowhere 2 uses ALPN as its data-plane version selector. Portal and Vector offer +the fixed protocols `nw2` and `now/1`, in that order. A negotiated `nw2` carrier +is V2; a negotiated `now/1` carrier is compatible V1. The `alpn` URL parameter +has been removed and is ignored as an unknown parameter. + +| Client | Portal | Negotiated version | +|---|---|---| +| V2 | V2 | `nw2` / V2 | +| V2 | default V1 | `now/1` / V1 | +| default V1 | V2 | `now/1` / V1 | + +V1 installations using a custom ALPN cannot interoperate with V2. A V1 client +that had already customized its ALPN to `nw2` is classified as V2; this rare +collision has no compatibility exception. ## TLS lane contract @@ -56,10 +66,10 @@ same authentication, FlowHeader, Target, setup result, pairing and limits. QUIC behavior is independent from the client Mux setting. Peers must also use matching credentials and reachable carrier families. A -Portal with `next=` uses its configured ALPN and `mux=0|1` selection for the -next hop. The upstream Mux selection defaults to `0` and is ignored without an -enabled `next`. +Portal with `next=` negotiates the upstream version independently and uses its +`mux=0|1` selection for that hop. The upstream Mux selection defaults to `0` +and is ignored without an enabled `next`. Interoperability tests exercise both peer roles: one endpoint as Portal and the other as client. A complete matrix includes all four `up`/`down` carrier -combinations, the default and a custom ALPN, dedicated TLS, and marked Mux. +combinations, both negotiated versions, dedicated TLS, and marked Mux. diff --git a/docs/configuration.md b/docs/configuration.md index 62dea29..892c219 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -15,7 +15,6 @@ portal://shared-key@host:port?net=mix&tls=1&log=info | `net` | `mix`, `tcp`, `udp` | `mix` | | `tls` | `1` generated certificate, `2` supplied certificate | `1` | | `crt`, `key` | PEM paths, required with `tls=2` | — | -| `alpn` | exact TLS/QUIC ALPN, 1–255 bytes | `now/1` | | `rate`, `etar` | Mbps, `0` disables limit | `0` | | `dial` | `auto` or local IP | `auto` | | `socks` | outbound SOCKS5 configuration | disabled | @@ -27,8 +26,8 @@ portal://shared-key@host:port?net=mix&tls=1&log=info | `log` | `none`, `debug`, `info`, `warn`, `error`, `event` | `info` | When `next` is enabled, `up`, `down`, `mux`, `sni`, and `pin` configure that -upstream hop. The Portal's `alpn` also applies to its native upstream client. -These upstream options are ignored when `next` is absent or `none`. +upstream hop. Protocol version is negotiated independently with the next +Portal. These upstream options are ignored when `next` is absent or `none`. `socks` and `next` are mutually exclusive outbound paths. ## Vector URL @@ -40,7 +39,6 @@ vector://shared-key@host:port?up=tcp&down=tcp&socks=127.0.0.1:1080 | Query | Values | Default | |---|---|---| | `up`, `down` | `tcp` or `udp` | `udp` | -| `alpn` | exact TLS/QUIC ALPN, 1–255 bytes | `now/1` | | `mux` | `0` dedicated TLS lanes, `1` TLS Mux | `0` | | `sni` | verified DNS name, or `none` | `none` | | `pin` | certificate SHA-256 pin, or `none` | `none` | @@ -53,18 +51,18 @@ vector://shared-key@host:port?up=tcp&down=tcp&socks=127.0.0.1:1080 ```text Portal URL | - +-- listener: net, tls, crt, key, alpn + +-- listener: net, tls, crt, key +-- relay: rate, etar, dial, log | +-- outbound path | +-- direct target access +-- socks --> SOCKS5 proxy --> target - +-- next --> {up, down, mux, sni, pin, alpn} --> Portal + +-- next --> {up, down, mux, sni, pin} --> Portal Vector URL | - +-- Portal client: up, down, alpn, mux, sni, pin + +-- Portal client: up, down, mux, sni, pin +-- SOCKS5 edge: socks +-- relay: rate, etar, log ``` @@ -78,10 +76,12 @@ use the least-loaded shard; a shard carries 4 active flows before another opens and closes after 30 seconds fully idle. With `mux=0`, every TLS-carried Flow owns one on-demand lane that closes with the Flow. -Portal and Vector advertise only their configured ALPN and require an exact -match. ALPN and Mux are independent settings. Portal's `mux` option controls -only its `next` client. Inbound Portal connections accept a `0xff`-marked Mux -carrier or an unmarked dedicated lane on the same listener. +Portal and Vector offer fixed ALPNs in the order `nw2`, `now/1`. V2 peers select +`nw2`; a V2 peer talking to a default V1 peer selects `now/1`. The removed +`alpn` query is ignored under the normal unknown-parameter rule. Protocol +version and Mux are independent settings. Portal's `mux` option controls only +its `next` client. Inbound Portal connections accept a `0xff`-marked Mux carrier +or an unmarked dedicated lane on the same listener. For `tls=2`, `crt` and `key` are native filesystem paths. Quote the complete URL when a Windows path, space, `&`, or another shell-significant character is @@ -136,8 +136,8 @@ corresponding UDP limit shared by UoT and QUIC DATAGRAM. Excess flows fail without waiting for capacity. QUIC internally admits the sum of both limits as bidirectional streams; this derived capacity has no separate setting. -Portal and Vector use the same QUIC profile regardless of ALPN or the client -Mux setting. +Portal and Vector use the same QUIC profile regardless of negotiated protocol +version or the client Mux setting. The stream/connection/send windows are respectively 4/8/8 MiB for `memory`, 8/16/16 MiB for `balanced`, and 16/32/32 MiB for `throughput`. These are flow-control ceilings, not eager allocations. Larger windows are useful only diff --git a/docs/integrations.md b/docs/integrations.md index bee16fe..fa6a67b 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -29,8 +29,9 @@ client engine. ## Alternate clients Implementers should follow [Protocol](protocol.md). QUIC uses native reliable -streams and DATAGRAM frames, never TLS Mux framing. Peers advertise the exact -configured ALPN. A Mux TLS connection places the `0xff` marker after +streams and DATAGRAM frames, never TLS Mux framing. V2 clients offer `nw2` and +the compatible V1 value `now/1`; the negotiated value selects the version for +that carrier. A Mux TLS connection places the `0xff` marker after authentication; a dedicated lane places its FlowHeader there instead. Portal accepts both forms on the same TLS listener and selects the decoder from that first byte. diff --git a/docs/operations.md b/docs/operations.md index 8df150e..42a9804 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -102,8 +102,8 @@ Functional validation belongs on every deployment platform: - Portal reaches `READY` on every configured listener; - Vector accepts SOCKS5 CONNECT and UDP ASSOCIATE; - every configured uplink/downlink carrier combination reaches a target; -- custom ALPN, credentials, certificate verification, and native chains match - at both ends; +- negotiated protocol version, credentials, certificate verification, and + native chains match at both ends; - flow limits fail promptly instead of waiting for capacity; - idle Mux Shards and UDP flows retire at their documented deadlines; - graceful shutdown reaches `STOPPED` within the configured deadline; diff --git a/docs/protocol.md b/docs/protocol.md index 27b1095..c7958b4 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -21,9 +21,10 @@ otherwise. ## 1. Carrier model -TLS/TCP and QUIC negotiate one exact ALPN. The default is `now/1`; a configured -ALPN is 1–255 bytes and has no version or Mux semantics. Both transports use -TLS 1.3. +TLS/TCP and QUIC use TLS 1.3 and negotiate a data-plane version through ALPN. +Nowhere 2 offers `nw2` first and the default V1 value `now/1` second. A selected +`nw2` carrier is V2; every accepted non-`nw2` carrier is V1, which in this +implementation means exactly `now/1`. One client session has one random 16-byte `session_id`. Every physical carrier is authenticated with that ID, so Portal can pair logical lanes belonging to @@ -153,7 +154,8 @@ tag = first 16 bytes of ``` The 32-byte exporter uses label `EXPORTER-Nowhere-Auth` and empty context. The -fixed derivation labels do not change when a custom ALPN is configured. +fixed derivation labels are shared by the compatible V1 and V2 paths in this +release; the negotiated version does not change authentication bytes. Authentication is bound to the current TLS connection; replaying a captured AuthFrame on another connection fails. diff --git a/docs/quick-start.md b/docs/quick-start.md index 6104e2b..27a54d2 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -53,8 +53,9 @@ automatically: nowhere 'vector://secret@127.0.0.1:2077?up=tcp&down=tcp&mux=1&socks=127.0.0.1:1080' ``` -ALPN defaults to `now/1`. Set the same `alpn=` on Portal and Vector when -a custom identifier is required. ALPN does not enable or disable Mux. +Nowhere 2 negotiates `nw2` with another V2 peer and falls back to `now/1` when +connecting to a default V1 peer. There is no configurable ALPN parameter; Mux +selection remains independent from protocol version. ## 3. Use SOCKS5 From b4e5818430522b4304e1a0244345f0488ee90dd3 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 30 Aug 2026 23:36:42 +0800 Subject: [PATCH 06/62] Bump version to 2.0.0-dev --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8acf273..9e9f682 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -865,7 +865,7 @@ dependencies = [ [[package]] name = "nowhere" -version = "1.8.2" +version = "2.0.0-dev" dependencies = [ "anyhow", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 7b800be..6e5466a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nowhere" -version = "1.8.2" +version = "2.0.0-dev" edition = "2024" description = "One-port, two-transport encrypted relay with independently split directions" license = "GPL-3.0-only" From 4c3c54bedb3e129c35773843e14ed050eb960bcb Mon Sep 17 00:00:00 2001 From: yosebyte Date: Tue, 1 Sep 2026 12:11:58 +0800 Subject: [PATCH 07/62] Align telemetry identity with Nowhere 2 --- src/telemetry/ipc.rs | 6 +++--- src/telemetry/mod.rs | 4 ++-- src/telemetry/wire.rs | 14 +++++++------- src/tests/telemetry/hub.rs | 4 ++-- src/tests/telemetry/ipc.rs | 14 +++++++------- src/tests/tui/client.rs | 4 ++-- src/tests/tui/render.rs | 4 ++-- src/tui/client/adapter.rs | 2 +- src/tui/model/types.rs | 2 +- 9 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/telemetry/ipc.rs b/src/telemetry/ipc.rs index 2e33088..9e117de 100644 --- a/src/telemetry/ipc.rs +++ b/src/telemetry/ipc.rs @@ -20,7 +20,7 @@ use tokio_util::sync::CancellationToken; use super::process::{process_is_alive, process_uid, read_process_incarnation}; use super::{ - ClientMessage, Hello, MAX_FRAME_SIZE, PROTOCOL_VERSION, ServerMessage, Subscription, + ClientMessage, Hello, MAX_FRAME_SIZE, ServerMessage, Subscription, TELEMETRY_VERSION, TelemetryHub, }; @@ -291,7 +291,7 @@ impl Drop for TelemetryServer { fn registry_directory() -> PathBuf { std::env::temp_dir().join(format!( - "nowhere-telemetry-v{PROTOCOL_VERSION}-{}", + "nowhere-{TELEMETRY_VERSION}-telemetry-{}", process_uid() )) } @@ -302,7 +302,7 @@ fn registry_path(registry_name: &str) -> PathBuf { fn validate_hello(hello: &Hello, discovered: &DiscoveredInstance) -> Result<()> { let instance = &hello.instance; - if instance.protocol_version != PROTOCOL_VERSION + if instance.telemetry_version != TELEMETRY_VERSION || instance.uid != discovered.uid || instance.pid != discovered.pid || instance.incarnation != discovered.incarnation diff --git a/src/telemetry/mod.rs b/src/telemetry/mod.rs index 27883b7..31b52ce 100644 --- a/src/telemetry/mod.rs +++ b/src/telemetry/mod.rs @@ -19,6 +19,6 @@ pub(crate) use ipc::{DiscoveredInstance, TelemetryClient, TelemetryServer, disco pub(crate) use process::now_unix_ms; pub(crate) use wire::{ AccessFinished, AccessOutcome, AccessStart, AccessStarted, ClientMessage, Hello, InstanceRole, - MAX_FRAME_SIZE, PROTOCOL_VERSION, RuntimeEvent, RuntimeKind, RuntimeLevel, ServerMessage, - Subscription, TelemetrySnapshot, TrafficProtocol, + MAX_FRAME_SIZE, RuntimeEvent, RuntimeKind, RuntimeLevel, ServerMessage, Subscription, + TELEMETRY_VERSION, TelemetrySnapshot, TrafficProtocol, }; diff --git a/src/telemetry/wire.rs b/src/telemetry/wire.rs index da5c17f..3433218 100644 --- a/src/telemetry/wire.rs +++ b/src/telemetry/wire.rs @@ -12,8 +12,8 @@ use crate::protocol::{Carrier, ProtocolVersion}; use super::process::{now_unix_ms, process_incarnation, process_uid}; -/// Version of the local telemetry protocol. It is not a data-plane wire version. -pub(crate) const PROTOCOL_VERSION: u16 = 3; +/// Local telemetry generation, aligned with the Nowhere application major version. +pub(crate) const TELEMETRY_VERSION: u16 = 2; /// Maximum accepted JSON payload, excluding the four-byte length prefix. pub(crate) const MAX_FRAME_SIZE: usize = 64 * 1024; @@ -27,7 +27,7 @@ pub(crate) enum InstanceRole { /// Non-secret metadata identifying a single process incarnation. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub(crate) struct InstanceDescriptor { - pub(crate) protocol_version: u16, + pub(crate) telemetry_version: u16, pub(crate) id: String, pub(crate) role: InstanceRole, pub(crate) pid: u32, @@ -50,7 +50,7 @@ impl InstanceDescriptor { let uid = process_uid(); let incarnation = process_incarnation(pid)?; Ok(Self { - protocol_version: PROTOCOL_VERSION, + telemetry_version: TELEMETRY_VERSION, id: format!("{uid}:{pid}:{incarnation}"), role, pid, @@ -65,8 +65,8 @@ impl InstanceDescriptor { pub(crate) fn registry_name(&self) -> String { format!( - "nowhere.v{}.{}.{}.{}", - PROTOCOL_VERSION, self.uid, self.pid, self.incarnation + "nowhere.{}.{}.{}.{}", + TELEMETRY_VERSION, self.uid, self.pid, self.incarnation ) } @@ -79,7 +79,7 @@ impl InstanceDescriptor { let pid = std::process::id(); let uid = process_uid(); Self { - protocol_version: PROTOCOL_VERSION, + telemetry_version: TELEMETRY_VERSION, id: format!("{uid}:{pid}:unavailable"), role, pid, diff --git a/src/tests/telemetry/hub.rs b/src/tests/telemetry/hub.rs index 022147c..50333d7 100644 --- a/src/tests/telemetry/hub.rs +++ b/src/tests/telemetry/hub.rs @@ -3,14 +3,14 @@ use std::sync::atomic::Ordering; use crate::protocol::Carrier; use crate::telemetry::wire::InstanceDescriptor; use crate::telemetry::{ - AccessOutcome, AccessStart, InstanceRole, PROTOCOL_VERSION, ServerMessage, TelemetryHub, + AccessOutcome, AccessStart, InstanceRole, ServerMessage, TELEMETRY_VERSION, TelemetryHub, TrafficProtocol, }; use crate::transport::Stats; fn descriptor() -> InstanceDescriptor { InstanceDescriptor { - protocol_version: PROTOCOL_VERSION, + telemetry_version: TELEMETRY_VERSION, id: "1:2:3".to_owned(), role: InstanceRole::Portal, pid: 2, diff --git a/src/tests/telemetry/ipc.rs b/src/tests/telemetry/ipc.rs index 1362211..09c5491 100644 --- a/src/tests/telemetry/ipc.rs +++ b/src/tests/telemetry/ipc.rs @@ -10,7 +10,7 @@ use crate::transport::Stats; fn parse_registry_name(name: &str) -> Option { let mut components = name.split('.'); - if components.next()? != "nowhere" || components.next()? != "v3" { + if components.next()? != "nowhere" || components.next()? != "2" { return None; } let uid = components.next()?.parse().ok()?; @@ -28,23 +28,23 @@ fn parse_registry_name(name: &str) -> Option { } #[test] -fn parses_only_exact_v3_registry_names() { +fn parses_only_nowhere_2_registry_names() { assert_eq!( - parse_registry_name("nowhere.v3.1000.42.900"), + parse_registry_name("nowhere.2.1000.42.900"), Some(DiscoveredInstance { - registry_name: "nowhere.v3.1000.42.900".to_owned(), + registry_name: "nowhere.2.1000.42.900".to_owned(), uid: 1000, pid: 42, incarnation: 900, }) ); assert!(parse_registry_name("nowhere.v2.1000.42.900").is_none()); - assert!(parse_registry_name("nowhere.v3.1000.42").is_none()); - assert!(parse_registry_name("nowhere.v3.1000.42.900.extra").is_none()); + assert!(parse_registry_name("nowhere.2.1000.42").is_none()); + assert!(parse_registry_name("nowhere.2.1000.42.900.extra").is_none()); } #[test] -fn v3_snapshot_round_trips_transport_counters() { +fn nowhere_2_snapshot_round_trips_transport_counters() { let snapshot = TelemetrySnapshot { tls_carriers_active: 2, quic_carriers_active: 3, diff --git a/src/tests/tui/client.rs b/src/tests/tui/client.rs index e10f165..96473de 100644 --- a/src/tests/tui/client.rs +++ b/src/tests/tui/client.rs @@ -1,11 +1,11 @@ use super::*; use crate::telemetry::wire::{InstanceDescriptor, LifecycleSnapshot}; -use crate::telemetry::{AccessOutcome, InstanceRole as WireRole, PROTOCOL_VERSION}; +use crate::telemetry::{AccessOutcome, InstanceRole as WireRole, TELEMETRY_VERSION}; fn hello() -> Hello { Hello { instance: InstanceDescriptor { - protocol_version: PROTOCOL_VERSION, + telemetry_version: TELEMETRY_VERSION, id: "0:42:7".to_owned(), role: WireRole::Portal, pid: 42, diff --git a/src/tests/tui/render.rs b/src/tests/tui/render.rs index 56794fd..01a77ea 100644 --- a/src/tests/tui/render.rs +++ b/src/tests/tui/render.rs @@ -25,7 +25,7 @@ fn app_with_instance() -> App { endpoint: "0.0.0.0:2077".to_owned(), config_summary: "net=mix tls=1".to_owned(), telemetry_interval_ms: 1_000, - telemetry_protocol_version: 3, + telemetry_version: 2, }, lifecycle: Lifecycle::Ready, snapshot: Some(TelemetrySnapshot { @@ -279,7 +279,7 @@ fn full_sidebar_keeps_long_lifecycle_labels_visible() { endpoint: "[::1]:1082".to_owned(), config_summary: "portal=relay.example:2077".to_owned(), telemetry_interval_ms: 1_000, - telemetry_protocol_version: 3, + telemetry_version: 2, }, lifecycle: Lifecycle::Starting, snapshot: None, diff --git a/src/tui/client/adapter.rs b/src/tui/client/adapter.rs index f0efd62..d5d1762 100644 --- a/src/tui/client/adapter.rs +++ b/src/tui/client/adapter.rs @@ -20,7 +20,7 @@ pub(super) fn hello_ui_event(hello: &Hello) -> UiEvent { endpoint: descriptor.endpoint.clone(), config_summary: descriptor.config_summary.clone(), telemetry_interval_ms: descriptor.telemetry_interval_ms, - telemetry_protocol_version: descriptor.protocol_version, + telemetry_version: descriptor.telemetry_version, }, lifecycle: Lifecycle::from_label(&hello.lifecycle), snapshot: None, diff --git a/src/tui/model/types.rs b/src/tui/model/types.rs index ab573c6..cd6038a 100644 --- a/src/tui/model/types.rs +++ b/src/tui/model/types.rs @@ -85,7 +85,7 @@ pub struct InstanceMeta { pub endpoint: String, pub config_summary: String, pub telemetry_interval_ms: u64, - pub telemetry_protocol_version: u16, + pub telemetry_version: u16, } /// Runtime event severity. From 04bd3b1bc88c2ace3ff01a58ede2ec6a899392e6 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 06:55:22 +0800 Subject: [PATCH 08/62] Define carrier-specific service endpoints --- src/common/endpoint.rs | 227 +++++++++++++++++++++++++++++++++++ src/common/mod.rs | 8 +- src/common/network.rs | 62 +++++++++- src/tests/common/endpoint.rs | 153 +++++++++++++++++++++++ src/tests/common/network.rs | 46 +++++++ 5 files changed, 491 insertions(+), 5 deletions(-) create mode 100644 src/common/endpoint.rs create mode 100644 src/tests/common/endpoint.rs diff --git a/src/common/endpoint.rs b/src/common/endpoint.rs new file mode 100644 index 0000000..43f783f --- /dev/null +++ b/src/common/endpoint.rs @@ -0,0 +1,227 @@ +// Copyright (C) 2026 NodePassProject +// SPDX-License-Identifier: GPL-3.0-only + +//! Shared Portal service endpoint URL grammar. + +use std::fmt; +use std::net::IpAddr; + +use anyhow::{Result, anyhow, bail}; +use url::Url; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum AddressFamily { + Any, + V4, + V6, +} + +impl AddressFamily { + pub(crate) const fn accepts(self, ip: IpAddr) -> bool { + match self { + Self::Any => true, + Self::V4 => ip.is_ipv4(), + Self::V6 => ip.is_ipv6(), + } + } + + fn suffix(self) -> &'static str { + match self { + Self::Any => "", + Self::V4 => "4", + Self::V6 => "6", + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) struct CarrierEndpoint { + pub(crate) port: u16, + pub(crate) family: AddressFamily, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub(crate) struct ServiceEndpoint { + pub(crate) host: String, + pub(crate) tcp: Option, + pub(crate) udp: Option, +} + +impl ServiceEndpoint { + pub(crate) fn parse(url: &Url, allow_wildcard: bool, context: &str) -> Result { + let host = url + .host_str() + .filter(|host| !host.is_empty()) + .ok_or_else(|| anyhow!("{context}: missing host"))? + .trim_start_matches('[') + .trim_end_matches(']') + .to_owned(); + if host == "*" && !allow_wildcard { + bail!("{context}: wildcard host is only valid for Portal listeners"); + } + + let (tcp, udp) = if url.path().is_empty() { + let port = required_port(url.port(), context)?; + let endpoint = CarrierEndpoint { + port, + family: AddressFamily::Any, + }; + (Some(endpoint), Some(endpoint)) + } else { + if url.port().is_some() { + bail!( + "{context}: choose either HOST:PORT or HOST/CARRIER:PORT; authority port and carrier path cannot be combined" + ); + } + parse_carrier_path(url.path(), context)? + }; + + let endpoint = Self { host, tcp, udp }; + endpoint.validate_literal_families(context)?; + Ok(endpoint) + } + + fn validate_literal_families(&self, context: &str) -> Result<()> { + let Ok(ip) = self.host.parse::() else { + return Ok(()); + }; + for endpoint in [self.tcp, self.udp].into_iter().flatten() { + if !endpoint.family.accepts(ip) { + bail!("{context}: address family does not match host {ip}"); + } + } + Ok(()) + } + + pub(crate) const fn has_tcp(&self) -> bool { + self.tcp.is_some() + } + + pub(crate) const fn has_udp(&self) -> bool { + self.udp.is_some() + } + + pub(crate) fn carrier_addr(&self, endpoint: CarrierEndpoint) -> String { + format_host_port(&self.host, endpoint.port) + } + + pub(crate) fn canonical(&self) -> String { + if let (Some(tcp), Some(udp)) = (self.tcp, self.udp) + && tcp.family == AddressFamily::Any + && udp.family == AddressFamily::Any + && tcp.port == udp.port + { + return format_host_port(&self.host, tcp.port); + } + let mut value = format_host(&self.host); + if let Some(tcp) = self.tcp { + value.push_str(&format!("/tcp{}:{}", tcp.family.suffix(), tcp.port)); + } + if let Some(udp) = self.udp { + value.push_str(&format!("/udp{}:{}", udp.family.suffix(), udp.port)); + } + value + } +} + +/// Validates the endpoint path before `url::Url` can normalize dot segments. +/// +/// Other URL structure remains the responsibility of the standard parser. +pub fn validate_endpoint_url_input(raw: &str, context: &str) -> Result<()> { + let Some((scheme, remainder)) = raw.split_once("://") else { + return Ok(()); + }; + if !scheme.eq_ignore_ascii_case("portal") && !scheme.eq_ignore_ascii_case("vector") { + return Ok(()); + } + let endpoint = remainder + .split_once(['?', '#']) + .map_or(remainder, |(endpoint, _)| endpoint); + let Some(path_start) = endpoint.find('/') else { + return Ok(()); + }; + parse_carrier_path(&endpoint[path_start..], context).map(|_| ()) +} + +fn parse_carrier_path( + path: &str, + context: &str, +) -> Result<(Option, Option)> { + let raw = path + .strip_prefix('/') + .ok_or_else(|| anyhow!("{context}: carrier path must start with '/'"))?; + if raw.is_empty() || raw.ends_with('/') || raw.split('/').any(str::is_empty) { + bail!("{context}: carrier path must not contain empty segments or a trailing slash"); + } + let mut tcp = None; + let mut udp = None; + for segment in raw.split('/') { + if is_dot_segment(segment) { + bail!("{context}: carrier path must not contain '.' or '..' segments"); + } + let (carrier, raw_port) = segment.split_once(':').ok_or_else(|| { + anyhow!("{context}: carrier segment {segment:?} must use CARRIER:PORT") + })?; + if raw_port.is_empty() || !raw_port.bytes().all(|byte| byte.is_ascii_digit()) { + bail!("{context}: carrier port in {segment:?} must contain decimal digits only"); + } + let port = raw_port + .parse::() + .ok() + .filter(|port| *port != 0) + .ok_or_else(|| anyhow!("{context}: carrier port must be in 1..=65535"))?; + let (name, slot, family) = match carrier { + "tcp" => ("TCP", &mut tcp, AddressFamily::Any), + "tcp4" => ("TCP", &mut tcp, AddressFamily::V4), + "tcp6" => ("TCP", &mut tcp, AddressFamily::V6), + "udp" => ("UDP", &mut udp, AddressFamily::Any), + "udp4" => ("UDP", &mut udp, AddressFamily::V4), + "udp6" => ("UDP", &mut udp, AddressFamily::V6), + _ => bail!( + "{context}: unknown carrier {carrier:?}; expected tcp, tcp4, tcp6, udp, udp4, or udp6" + ), + }; + if slot.is_some() { + bail!("{context}: {name} carrier is declared more than once"); + } + *slot = Some(CarrierEndpoint { port, family }); + } + Ok((tcp, udp)) +} + +fn is_dot_segment(segment: &str) -> bool { + matches!( + segment.to_ascii_lowercase().as_str(), + "." | ".." | "%2e" | ".%2e" | "%2e." | "%2e%2e" + ) +} + +impl fmt::Display for ServiceEndpoint { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(&self.canonical()) + } +} + +fn required_port(port: Option, context: &str) -> Result { + port.filter(|port| *port != 0) + .ok_or_else(|| anyhow!("{context}: compact endpoint requires a port in 1..=65535")) +} + +pub(crate) fn format_host_port(host: &str, port: u16) -> String { + match host.parse::() { + Ok(IpAddr::V6(ip)) => format!("[{ip}]:{port}"), + Ok(IpAddr::V4(ip)) => format!("{ip}:{port}"), + Err(_) => format!("{host}:{port}"), + } +} + +fn format_host(host: &str) -> String { + match host.parse::() { + Ok(IpAddr::V6(ip)) => format!("[{ip}]"), + _ => host.to_owned(), + } +} + +#[cfg(test)] +#[path = "../tests/common/endpoint.rs"] +mod tests; diff --git a/src/common/mod.rs b/src/common/mod.rs index 0a900e1..3e0b03d 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -6,6 +6,7 @@ mod alpn; mod config; mod datagram; +mod endpoint; mod latency; mod lifecycle; mod logger; @@ -26,11 +27,16 @@ pub use config::{ pub(crate) use datagram::{ BudgetedDatagram, UdpDatagramSend, reserve_udp_budget, send_quic_udp_packet, }; +pub use endpoint::validate_endpoint_url_input; +pub(crate) use endpoint::{AddressFamily, CarrierEndpoint, ServiceEndpoint}; pub(crate) use latency::{LatencyGuard, LatencyTracker}; pub(crate) use lifecycle::{LifeMode, LifeReason, LifeState, Lifecycle, ShutdownSignals}; pub use logger::{LogLevel, Logger}; pub use network::{bind_udp_addrs, dial_tcp_from_local_ip, dial_udp_from_local_ip}; -pub(crate) use network::{filter_addrs, parse_local_ip}; +pub(crate) use network::{ + dial_tcp_from_local_ip_family, filter_addrs, filter_addrs_for_family, parse_local_ip, + resolve_bind_addrs, +}; pub(crate) use socks::{OutboundDialer, OutboundTcpStream, OutboundUdpSocket, SocksConfig}; pub(crate) use tls::certificate_sha256; pub(crate) use tls::new_server_configs_with_reload_interval; diff --git a/src/common/network.rs b/src/common/network.rs index 5153622..0c187a1 100644 --- a/src/common/network.rs +++ b/src/common/network.rs @@ -9,7 +9,40 @@ use std::time::Duration; use anyhow::{Context, Result, anyhow}; use tokio::net::{TcpSocket, TcpStream, UdpSocket, lookup_host}; -use super::DEFAULT_DIALER_IP; +use super::{AddressFamily, CarrierEndpoint, DEFAULT_DIALER_IP}; + +/// Resolves every matching listen address for one carrier and removes duplicates. +pub(crate) fn resolve_bind_addrs(host: &str, endpoint: CarrierEndpoint) -> Result> { + let mut addrs = if host == "*" || host.is_empty() { + match endpoint.family { + AddressFamily::Any => vec![ + SocketAddr::from(([0, 0, 0, 0], endpoint.port)), + SocketAddr::from(([0u16; 8], endpoint.port)), + ], + AddressFamily::V4 => vec![SocketAddr::from(([0, 0, 0, 0], endpoint.port))], + AddressFamily::V6 => vec![SocketAddr::from(([0u16; 8], endpoint.port))], + } + } else if let Ok(ip) = host.parse::() { + if endpoint.family.accepts(ip) { + vec![SocketAddr::new(ip, endpoint.port)] + } else { + Vec::new() + } + } else { + let joined = format!("{host}:{}", endpoint.port); + joined + .to_socket_addrs() + .with_context(|| format!("failed to resolve listen address: {joined}"))? + .filter(|addr| endpoint.family.accepts(addr.ip())) + .collect() + }; + addrs.sort_unstable(); + addrs.dedup(); + if addrs.is_empty() { + return Err(anyhow!("no matching listen address resolved for {host}")); + } + Ok(addrs) +} /// Resolves the UDP listen addresses for a host/port pair. /// @@ -42,6 +75,15 @@ pub async fn dial_tcp_from_local_ip( dialer_ip: &str, target: &str, timeout: Duration, +) -> Result { + dial_tcp_from_local_ip_family(dialer_ip, target, timeout, AddressFamily::Any).await +} + +pub(crate) async fn dial_tcp_from_local_ip_family( + dialer_ip: &str, + target: &str, + timeout: Duration, + family: AddressFamily, ) -> Result { let connect = async { let local_ip = parse_local_ip(dialer_ip); @@ -50,15 +92,16 @@ pub async fn dial_tcp_from_local_ip( format!("common::util::dial_tcp_from_local_ip: failed to resolve target: {target}") })?; - for addr in filter_addrs(addrs, local_ip) { + for addr in filter_addrs_for_family(addrs, local_ip, family) { match connect_tcp_addr(local_ip, addr).await { Ok(stream) => return Ok(stream), Err(err) => last_err = Some(err), } } - Err(last_err - .unwrap_or_else(|| anyhow!("common::util::dial_tcp_from_local_ip: no target address"))) + Err(last_err.unwrap_or_else(|| { + anyhow!("common::util::dial_tcp_from_local_ip: no target address matches configured address family") + })) }; tokio::time::timeout(timeout, connect) @@ -117,6 +160,17 @@ pub(crate) fn filter_addrs( .collect() } +pub(crate) fn filter_addrs_for_family( + addrs: impl Iterator, + local_ip: Option, + family: AddressFamily, +) -> Vec { + filter_addrs(addrs, local_ip) + .into_iter() + .filter(|addr| family.accepts(addr.ip())) + .collect() +} + pub(super) async fn connect_tcp_addr( local_ip: Option, target: SocketAddr, diff --git a/src/tests/common/endpoint.rs b/src/tests/common/endpoint.rs new file mode 100644 index 0000000..16108a0 --- /dev/null +++ b/src/tests/common/endpoint.rs @@ -0,0 +1,153 @@ +use super::*; + +fn parse(raw: &str, wildcard: bool) -> Result { + ServiceEndpoint::parse(&Url::parse(raw)?, wildcard, "test") +} + +#[test] +fn parses_compact_and_explicit_endpoints() { + let compact = parse("portal://key@*:2077", true).unwrap(); + assert_eq!(compact.canonical(), "*:2077"); + assert_eq!(compact.tcp.unwrap().port, 2077); + assert_eq!(compact.udp.unwrap().port, 2077); + + let explicit = parse("portal://key@*/udp6:3088/tcp4:2077", true).unwrap(); + assert_eq!(explicit.canonical(), "*/tcp4:2077/udp6:3088"); + assert_eq!(explicit.tcp.unwrap().family, AddressFamily::V4); + assert_eq!(explicit.udp.unwrap().family, AddressFamily::V6); +} + +#[test] +fn accepts_every_declared_carrier_token() { + for (carrier, family, is_tcp) in [ + ("tcp", AddressFamily::Any, true), + ("tcp4", AddressFamily::V4, true), + ("tcp6", AddressFamily::V6, true), + ("udp", AddressFamily::Any, false), + ("udp4", AddressFamily::V4, false), + ("udp6", AddressFamily::V6, false), + ] { + let endpoint = parse(&format!("portal://key@*/{carrier}:2077"), true).unwrap(); + let parsed = if is_tcp { endpoint.tcp } else { endpoint.udp }.unwrap(); + assert_eq!(parsed.family, family, "carrier={carrier}"); + assert_eq!(parsed.port, 2077, "carrier={carrier}"); + } +} + +#[test] +fn accepts_port_boundaries_and_canonicalizes_leading_zeroes() { + for (raw_port, expected_port) in [("1", 1), ("00001", 1), ("65535", 65535)] { + let endpoint = parse(&format!("portal://key@*/tcp:{raw_port}"), true).unwrap(); + assert_eq!(endpoint.tcp.unwrap().port, expected_port); + assert_eq!(endpoint.canonical(), format!("*/tcp:{expected_port}")); + } +} + +#[test] +fn rejects_invalid_carrier_names_and_port_representations() { + for carrier in ["", "TCP", "Udp", "quic", "tcp7", "tcp%20"] { + let raw = format!("portal://key@*/{carrier}:2077"); + let error = parse(&raw, true).unwrap_err().to_string(); + assert!( + error.contains("unknown carrier"), + "{raw} returned {error:?}" + ); + } + for port in [ + "", "0", "-1", "+1", "1.0", "1%20", "%201", "0x50", "65536", "999999", "12", + ] { + let raw = format!("portal://key@*/tcp:{port}"); + assert!(parse(&raw, true).is_err(), "accepted {raw}"); + } +} + +#[test] +fn rejects_every_duplicate_transport_pair() { + for carriers in [["tcp", "tcp4", "tcp6"], ["udp", "udp4", "udp6"]] { + for first in carriers { + for second in carriers { + let raw = format!("portal://key@*/{first}:2077/{second}:3088"); + let error = parse(&raw, true).unwrap_err().to_string(); + assert!( + error.contains("carrier is declared more than once"), + "{raw} returned {error:?}" + ); + } + } + } +} + +#[test] +fn formats_ipv6_and_single_carriers() { + assert_eq!( + parse("vector://key@[2001:db8::1]/udp:3088", false) + .unwrap() + .canonical(), + "[2001:db8::1]/udp:3088" + ); + assert_eq!( + parse("vector://key@example.com/tcp:2077", false) + .unwrap() + .canonical(), + "example.com/tcp:2077" + ); +} + +#[test] +fn rejects_invalid_grammar_and_family_mismatches() { + for (raw, expected) in [ + ("portal://key@*:2077/tcp:2077", "choose either HOST:PORT"), + ("portal://key@*/", "must not contain empty segments"), + ("portal://key@*/tcp:2077/", "trailing slash"), + ("portal://key@*/tcp:2077//udp:2077", "empty segments"), + ("portal://key@*/tcp", "must use CARRIER:PORT"), + ("portal://key@*/tcp:1:2", "decimal digits only"), + ("portal://key@*/tcp:abc", "decimal digits only"), + ("portal://key@*/tcp:-1", "decimal digits only"), + ("portal://key@*/tcp:+2077", "decimal digits only"), + ("portal://key@*/tcp:", "decimal digits only"), + ("portal://key@*/tcp:0", "1..=65535"), + ("portal://key@*/tcp:65536", "1..=65535"), + ("portal://key@*/TCP:2077", "unknown carrier"), + ("portal://key@*/sctp:2077", "unknown carrier"), + ( + "portal://key@*/tcp:2077/tcp6:3088", + "TCP carrier is declared more than once", + ), + ( + "portal://key@*/udp4:2077/udp:3088", + "UDP carrier is declared more than once", + ), + ( + "portal://key@192.0.2.1/tcp6:2077", + "address family does not match", + ), + ( + "portal://key@[2001:db8::1]/udp4:2077", + "address family does not match", + ), + ] { + let error = parse(raw, true).unwrap_err().to_string(); + assert!(error.contains(expected), "{raw} returned {error:?}"); + } + let error = parse("vector://key@*:2077", false).unwrap_err().to_string(); + assert!(error.contains("wildcard host is only valid for Portal listeners")); +} + +#[test] +fn raw_input_validation_rejects_dot_segments_before_url_normalization() { + for raw in [ + "portal://key@*/tcp:2077/../udp:3088", + "portal://key@*/tcp:2077/./udp:3088", + "portal://key@*/tcp:2077/%2e%2e/udp:3088", + "vector://key@example.com/%2E./tcp:2077?socks=:1080", + ] { + let error = validate_endpoint_url_input(raw, "test") + .unwrap_err() + .to_string(); + assert!( + error.contains("must not contain '.' or '..' segments"), + "{raw}: {error}" + ); + } +} diff --git a/src/tests/common/network.rs b/src/tests/common/network.rs index 8e69feb..7be46f1 100644 --- a/src/tests/common/network.rs +++ b/src/tests/common/network.rs @@ -57,3 +57,49 @@ fn filter_addrs_matches_local_ip_family() { [addrs[1]] ); } + +#[test] +fn carrier_bind_resolution_expands_wildcards_and_filters_dns() { + let v4 = CarrierEndpoint { + port: 8080, + family: AddressFamily::V4, + }; + let v6 = CarrierEndpoint { + port: 9090, + family: AddressFamily::V6, + }; + assert_eq!( + resolve_bind_addrs("*", v4).unwrap(), + [SocketAddr::from(([0, 0, 0, 0], 8080))] + ); + assert_eq!( + resolve_bind_addrs("*", v6).unwrap(), + [SocketAddr::from(([0u16; 8], 9090))] + ); + assert!(resolve_bind_addrs("127.0.0.1", v6).is_err()); + + let localhost = resolve_bind_addrs("localhost", v4).unwrap(); + assert!(!localhost.is_empty()); + assert!(localhost.iter().all(SocketAddr::is_ipv4)); + assert!(localhost.windows(2).all(|pair| pair[0] != pair[1])); +} + +#[test] +fn carrier_and_local_bind_family_filters_are_both_enforced() { + let addrs = [ + SocketAddr::from(([127, 0, 0, 1], 443)), + SocketAddr::from(([0, 0, 0, 0, 0, 0, 0, 1], 443)), + ]; + assert_eq!( + filter_addrs_for_family(addrs.into_iter(), None, AddressFamily::V6), + [addrs[1]] + ); + assert!( + filter_addrs_for_family( + addrs.into_iter(), + Some("127.0.0.1".parse().unwrap()), + AddressFamily::V6, + ) + .is_empty() + ); +} From a47401d40c2a3f06fa355ac13b532172f6579acc Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 06:55:35 +0800 Subject: [PATCH 09/62] Route Vector through configured carriers --- src/common/mod.rs | 3 +- src/tests/vector/config.rs | 98 ++++++++++++++++++++++++- src/tests/vector/tls.rs | 12 ++- src/vector/config.rs | 145 ++++++++++++++++++++++--------------- src/vector/session.rs | 5 +- src/vector/session/quic.rs | 11 ++- src/vector/session/tls.rs | 12 ++- src/vector/tls.rs | 23 +++--- 8 files changed, 227 insertions(+), 82 deletions(-) diff --git a/src/common/mod.rs b/src/common/mod.rs index 3e0b03d..9253579 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -34,8 +34,7 @@ pub(crate) use lifecycle::{LifeMode, LifeReason, LifeState, Lifecycle, ShutdownS pub use logger::{LogLevel, Logger}; pub use network::{bind_udp_addrs, dial_tcp_from_local_ip, dial_udp_from_local_ip}; pub(crate) use network::{ - dial_tcp_from_local_ip_family, filter_addrs, filter_addrs_for_family, parse_local_ip, - resolve_bind_addrs, + dial_tcp_from_local_ip_family, filter_addrs_for_family, parse_local_ip, resolve_bind_addrs, }; pub(crate) use socks::{OutboundDialer, OutboundTcpStream, OutboundUdpSocket, SocksConfig}; pub(crate) use tls::certificate_sha256; diff --git a/src/tests/vector/config.rs b/src/tests/vector/config.rs index 4ab7ea1..805066d 100644 --- a/src/tests/vector/config.rs +++ b/src/tests/vector/config.rs @@ -16,6 +16,32 @@ fn defaults_to_quic_both_directions() { assert_eq!(config.socks.port, 1080); } +#[test] +fn explicit_endpoints_select_ports_families_and_single_carrier_defaults() { + let tcp = parse("vector://secret@example.com/tcp4:2077?socks=:1080").unwrap(); + assert_eq!(tcp.up, CarrierMode::Tcp); + assert_eq!(tcp.down, CarrierMode::Tcp); + assert_eq!(tcp.portal_endpoint(), "example.com/tcp4:2077"); + + let mixed = + parse("vector://secret@example.com/udp6:3088/tcp4:2077?socks=:1080&up=tcp&down=udp") + .unwrap(); + assert_eq!(mixed.portal_endpoint(), "example.com/tcp4:2077/udp6:3088"); + assert_eq!(mixed.remote.tcp.unwrap().port, 2077); + assert_eq!(mixed.remote.udp.unwrap().port, 3088); +} + +#[test] +fn policy_must_use_declared_carriers() { + for raw in [ + "vector://secret@example.com/tcp:2077?up=udp&socks=:1080", + "vector://secret@example.com/udp:2077?down=tcp&socks=:1080", + "vector://secret@example.com/tcp:2077?up=mix&socks=:1080", + ] { + assert!(parse(raw).is_err(), "accepted {raw}"); + } +} + #[test] fn tcp_pair_defaults_to_dedicated_lanes() { let config = @@ -188,7 +214,7 @@ fn rejects_invalid_authority_shape() { #[test] fn normalizes_ipv6_portal_authority() { let config = parse("vector://secret@[::1]:2077?socks=127.0.0.1:1080").unwrap(); - assert_eq!(config.remote_host, "::1"); + assert_eq!(config.remote.host, "::1"); assert_eq!(config.portal_endpoint(), "[::1]:2077"); } @@ -210,6 +236,35 @@ fn upstream_authority_decodes_reserved_key_bytes_and_ipv6() { ); } +#[test] +fn upstream_authority_decodes_the_shared_key_exactly_once() { + let query = HashMap::new(); + let (_, credentials) = PortalClientConfig::from_upstream_authority( + "part%2540key@origin.example/udp:2080", + &query, + "auto", + ) + .unwrap(); + assert_eq!( + credentials, + crate::protocol::Credentials::from_shared_key(b"part%40key").unwrap() + ); +} + +#[test] +fn upstream_authority_accepts_explicit_carriers() { + let query = HashMap::new(); + let (config, _) = PortalClientConfig::from_upstream_authority( + "secret@origin.example/tcp6:2077", + &query, + "auto", + ) + .unwrap(); + assert_eq!(config.endpoint(), "origin.example/tcp6:2077"); + assert_eq!(config.up, CarrierMode::Tcp); + assert_eq!(config.down, CarrierMode::Tcp); +} + #[test] fn upstream_authority_requires_unambiguous_key_endpoint_separator() { let query = HashMap::new(); @@ -225,3 +280,44 @@ fn upstream_authority_requires_unambiguous_key_endpoint_separator() { ); } } + +#[test] +fn upstream_authority_rejects_every_invalid_endpoint_shape() { + let query = HashMap::new(); + for (authority, expected) in [ + ("secret@*:2077", "wildcard host is only valid"), + ( + "secret@origin.example:2077/tcp:3088", + "choose either HOST:PORT", + ), + ("secret@origin.example/tcp:2077/", "trailing slash"), + ( + "secret@origin.example/tcp:2077/tcp6:3088", + "TCP carrier is declared more than once", + ), + ("secret@origin.example/udp:0", "1..=65535"), + ("secret@origin.example/sctp:2077", "unknown carrier"), + ( + "secret@192.0.2.1/udp6:2077", + "address family does not match", + ), + ( + "secret@origin.example/tcp:2077?inner=1", + "expected shared-key and one endpoint", + ), + ( + "secret@origin.example/tcp:2077#fragment", + "expected shared-key and one endpoint", + ), + ("bad%GG@origin.example/tcp:2077", "malformed percent escape"), + ] { + let error = PortalClientConfig::from_upstream_authority(authority, &query, "auto") + .unwrap_err() + .to_string(); + assert!(error.contains(expected), "{authority} returned {error:?}"); + assert!( + !error.contains("secret@"), + "error leaked the next shared key" + ); + } +} diff --git a/src/tests/vector/tls.rs b/src/tests/vector/tls.rs index a42b91c..c2c1934 100644 --- a/src/tests/vector/tls.rs +++ b/src/tests/vector/tls.rs @@ -40,7 +40,11 @@ async fn client_prefers_fixed_v2_alpn() { let (_, _, version) = ClientTls::new(&config(&raw)) .unwrap() - .connect_tcp(&endpoint.to_string(), "auto") + .connect_tcp( + &endpoint.to_string(), + "auto", + crate::common::AddressFamily::Any, + ) .await .unwrap(); assert_eq!(version, ProtocolVersion::V2); @@ -123,7 +127,11 @@ async fn test_pinned_handshake(pin: TestPin, sni: Option<&str>) -> Result, name: &str) -> Result { + pub(crate) fn parse( + value: Option<&str>, + name: &str, + default: Self, + context: &str, + ) -> Result { match value { - None => Ok(Self::Udp), + None => Ok(default), Some("tcp") => Ok(Self::Tcp), Some("udp") => Ok(Self::Udp), Some("mix") => Ok(Self::Mix), - Some(_) => bail!("vector::config: {name} must be tcp, udp, or mix"), + Some(_) => bail!("{context}: {name} must be tcp, udp, or mix"), } } @@ -72,8 +79,7 @@ impl CarrierMode { /// Transport-only configuration shared by Vector and Portal upstream clients. #[derive(Clone, Debug)] pub(crate) struct PortalClientConfig { - pub(crate) remote_host: String, - pub(crate) remote_port: u16, + pub(crate) remote: ServiceEndpoint, pub(crate) up: CarrierMode, pub(crate) down: CarrierMode, pub(crate) mux: MuxMode, @@ -83,22 +89,29 @@ pub(crate) struct PortalClientConfig { } impl PortalClientConfig { - fn parse(url: &Url, query: &HashMap, dialer_ip: &str) -> Result { - let remote_host = url - .host_str() - .filter(|host| !host.is_empty()) - .ok_or_else(|| anyhow!("vector::config: missing Portal host"))? - .trim_start_matches('[') - .trim_end_matches(']') - .to_owned(); - let remote_port = url - .port() - .filter(|port| *port != 0) - .ok_or_else(|| anyhow!("vector::config: missing Portal port"))?; - let up = CarrierMode::parse(query.get("up").map(String::as_str), "up")?; - let down = CarrierMode::parse(query.get("down").map(String::as_str), "down")?; + fn parse( + url: &Url, + query: &HashMap, + dialer_ip: &str, + context: &str, + ) -> Result { + let remote = ServiceEndpoint::parse(url, false, context)?; + let default = match (remote.has_tcp(), remote.has_udp()) { + (true, false) => CarrierMode::Tcp, + (false, true) | (true, true) => CarrierMode::Udp, + (false, false) => unreachable!(), + }; + let up = CarrierMode::parse(query.get("up").map(String::as_str), "up", default, context)?; + let down = CarrierMode::parse( + query.get("down").map(String::as_str), + "down", + default, + context, + )?; + validate_carrier_policy(&remote, up, "up", context)?; + validate_carrier_policy(&remote, down, "down", context)?; let mux = MuxMode::parse(query.get("mux").map(String::as_str)) - .map_err(|error| anyhow!("vector::config: {error}"))?; + .map_err(|error| anyhow!("{context}: {error}"))?; let mux = if up == CarrierMode::Udp && down == CarrierMode::Udp { MuxMode::Disabled } else { @@ -113,7 +126,7 @@ impl PortalClientConfig { || value.contains([':', '[', ']']) || value.parse::().is_ok() { - bail!("vector::config: sni must be an ASCII DNS name"); + bail!("{context}: sni must be an ASCII DNS name"); } Ok(value.to_owned()) }) @@ -123,8 +136,7 @@ impl PortalClientConfig { .filter(|value| !value.is_empty() && value.as_str() != "none") .cloned(); Ok(Self { - remote_host, - remote_port, + remote, up, down, mux, @@ -139,30 +151,39 @@ impl PortalClientConfig { query: &HashMap, dialer_ip: &str, ) -> Result<(Self, crate::protocol::Credentials)> { + validate_endpoint_url_input(&format!("vector://{raw_authority}"), "Portal next endpoint")?; let separator = raw_authority.rfind('@').ok_or_else(|| { - anyhow!("portal::next: shared key and endpoint must be separated by @") + anyhow!("Portal next endpoint: shared key and endpoint must be separated by '@'") })?; if raw_authority[..separator].contains('@') { - bail!("portal::next: reserved shared-key characters must be percent-encoded"); + bail!("Portal next endpoint: reserved shared-key characters must be percent-encoded"); } let url = Url::parse(&format!("vector://{raw_authority}")) - .map_err(|error| anyhow!("portal::next: invalid upstream Portal authority: {error}"))?; - if url.password().is_some() - || !url.path().is_empty() - || url.query().is_some() - || url.fragment().is_some() - { - bail!("portal::next: expected only shared-key@host:port"); + .map_err(|error| anyhow!("Portal next endpoint: invalid authority: {error}"))?; + if url.password().is_some() || url.query().is_some() || url.fragment().is_some() { + bail!( + "Portal next endpoint: expected shared-key and one endpoint without a query or fragment" + ); } - let credentials = crate::protocol::Credentials::new(&url) - .map_err(|error| anyhow!("portal::next: {error}"))?; - let config = Self::parse(&url, query, dialer_ip) - .map_err(|error| anyhow!("portal::next: {error}"))?; + let credentials = crate::protocol::Credentials::new(&url)?; + let config = Self::parse(&url, query, dialer_ip, "Portal next endpoint")?; Ok((config, credentials)) } pub(crate) fn endpoint(&self) -> String { - format_host_port(&self.remote_host, self.remote_port) + self.remote.canonical() + } + + pub(crate) fn host(&self) -> &str { + &self.remote.host + } + + pub(crate) fn tcp_endpoint(&self) -> Option { + self.remote.tcp + } + + pub(crate) fn udp_endpoint(&self) -> Option { + self.remote.udp } pub(crate) fn effective_route(&self) -> String { @@ -198,9 +219,9 @@ pub(crate) struct SocksListenConfig { impl SocksListenConfig { fn from_url(url: &Url) -> Result { let raw_value = first_raw_socks_value(url) - .ok_or_else(|| anyhow!("vector::config: socks parameter is required"))?; + .ok_or_else(|| anyhow!("Vector configuration: socks parameter is required"))?; if raw_value.is_empty() { - bail!("vector::config: socks must not be empty"); + bail!("Vector configuration: socks must not be empty"); } let (endpoint, credentials) = parse_socks_value(raw_value)?; let (host, port) = parse_host_port(&endpoint, "socks listener", true)?; @@ -223,8 +244,7 @@ impl SocksListenConfig { /// Fully validated Vector runtime configuration. #[derive(Clone, Debug)] pub(crate) struct VectorConfig { - pub(super) remote_host: String, - pub(super) remote_port: u16, + pub(super) remote: ServiceEndpoint, pub(super) up: CarrierMode, pub(super) down: CarrierMode, pub(super) mux: MuxMode, @@ -238,30 +258,25 @@ pub(crate) struct VectorConfig { impl VectorConfig { pub(super) fn from_url(url: &Url) -> Result { if url.scheme() != "vector" { - bail!("vector::config: URL scheme must be vector"); + bail!("Vector configuration: URL scheme must be vector"); } if url.password().is_some() { - bail!("vector::config: URL password component is not supported"); + bail!("Vector configuration: URL password component is not supported"); } if url.username().is_empty() { - bail!("vector::config: missing shared key"); + bail!("Vector configuration: missing shared key before '@'"); } if url.fragment().is_some() { - bail!("vector::config: URL fragment is not supported"); + bail!("Vector configuration: URL fragment is not supported"); } - if !url.path().is_empty() { - bail!("vector::config: URL path is not supported"); - } - let query = query_first(url, VECTOR_QUERY_KEYS)?; - let portal = PortalClientConfig::parse(url, &query, DEFAULT_DIALER_IP)?; + let portal = PortalClientConfig::parse(url, &query, DEFAULT_DIALER_IP, "Vector endpoint")?; let rate_mbps = parse_rate(query.get("rate").map(String::as_str), "rate")?; let etar_mbps = parse_rate(query.get("etar").map(String::as_str), "etar")?; let socks = SocksListenConfig::from_url(url)?; Ok(Self { - remote_host: portal.remote_host, - remote_port: portal.remote_port, + remote: portal.remote, up: portal.up, down: portal.down, mux: portal.mux, @@ -275,8 +290,7 @@ impl VectorConfig { pub(crate) fn portal_client_config(&self) -> PortalClientConfig { PortalClientConfig { - remote_host: self.remote_host.clone(), - remote_port: self.remote_port, + remote: self.remote.clone(), up: self.up, down: self.down, mux: self.mux, @@ -287,7 +301,7 @@ impl VectorConfig { } pub(super) fn portal_endpoint(&self) -> String { - format_host_port(&self.remote_host, self.remote_port) + self.remote.canonical() } pub(super) fn checkpoint_mode(&self) -> u8 { @@ -320,6 +334,23 @@ impl VectorConfig { } } +fn validate_carrier_policy( + endpoint: &ServiceEndpoint, + mode: CarrierMode, + name: &str, + context: &str, +) -> Result<()> { + let available = match mode { + CarrierMode::Tcp => endpoint.has_tcp(), + CarrierMode::Udp => endpoint.has_udp(), + CarrierMode::Mix => endpoint.has_tcp() && endpoint.has_udp(), + }; + if !available { + bail!("{context}: {name} selects a carrier not declared by the endpoint"); + } + Ok(()) +} + fn parse_rate(value: Option<&str>, name: &str) -> Result { match value { None => Ok(0), @@ -327,7 +358,7 @@ fn parse_rate(value: Option<&str>, name: &str) -> Result { .parse::() .ok() .filter(|value| *value >= 0) - .ok_or_else(|| anyhow!("vector::config: {name} must be a non-negative integer")), + .ok_or_else(|| anyhow!("Vector configuration: {name} must be a non-negative integer")), } } diff --git a/src/vector/session.rs b/src/vector/session.rs index 8cd665c..4bda3f8 100644 --- a/src/vector/session.rs +++ b/src/vector/session.rs @@ -21,9 +21,8 @@ use tokio_rustls::client::TlsStream; use tokio_util::sync::CancellationToken; use crate::common::{ - BudgetedDatagram, LatencyGuard, LatencyTracker, UdpDatagramSend, filter_addrs, - handshake_timeout, parse_local_ip, reserve_udp_budget, send_quic_udp_packet, service_cooldown, - udp_idle_timeout, + BudgetedDatagram, LatencyGuard, LatencyTracker, UdpDatagramSend, handshake_timeout, + parse_local_ip, reserve_udp_budget, send_quic_udp_packet, service_cooldown, udp_idle_timeout, }; use crate::mux::{MUX_IDLE_TIMEOUT, MuxConfig, MuxHandle, MuxStream}; use crate::protocol::{ diff --git a/src/vector/session/quic.rs b/src/vector/session/quic.rs index 885314a..0ad83be 100644 --- a/src/vector/session/quic.rs +++ b/src/vector/session/quic.rs @@ -85,14 +85,21 @@ impl QuicManager { } async fn connect(&self) -> Result> { + let endpoint = self.config.udp_endpoint().ok_or_else(|| { + anyhow!("vector::session::QuicManager::connect: UDP carrier is not configured") + })?; let resolved = timeout( handshake_timeout(), - lookup_host((self.config.remote_host.as_str(), self.config.remote_port)), + lookup_host((self.config.host(), endpoint.port)), ) .await .map_err(|_| anyhow!("vector::session::QuicManager::connect: Portal DNS timeout"))? .context("vector::session::QuicManager::connect: Portal DNS failed")?; - let addresses = filter_addrs(resolved, parse_local_ip(&self.config.dialer_ip)); + let addresses = crate::common::filter_addrs_for_family( + resolved, + parse_local_ip(&self.config.dialer_ip), + endpoint.family, + ); if addresses.is_empty() { bail!("vector::session::QuicManager::connect: no Portal address resolved"); } diff --git a/src/vector/session/tls.rs b/src/vector/session/tls.rs index 725e887..16242a3 100644 --- a/src/vector/session/tls.rs +++ b/src/vector/session/tls.rs @@ -4,7 +4,7 @@ use super::*; pub(in crate::vector) struct TlsManager { - endpoint: String, + endpoint: Option<(String, crate::common::AddressFamily)>, dialer_ip: String, tls: ClientTls, auth_key: AuthKey, @@ -45,7 +45,9 @@ impl TlsManager { signals: ClientSignals, ) -> Arc { Arc::new(Self { - endpoint: config.endpoint(), + endpoint: config + .tcp_endpoint() + .map(|endpoint| (config.remote.carrier_addr(endpoint), endpoint.family)), dialer_ip: config.dialer_ip.clone(), tls, auth_key: credentials.auth_key, @@ -177,9 +179,13 @@ impl TlsManager { } async fn connect_lane(&self) -> Result { + let (endpoint, family) = self + .endpoint + .as_ref() + .ok_or_else(|| anyhow!("vector::session::TlsManager: TCP carrier is not configured"))?; let (stream, exporter, version) = self .tls - .connect_tcp(&self.endpoint, &self.dialer_ip) + .connect_tcp(endpoint, &self.dialer_ip, *family) .await?; let latency = self.latency.register(); latency.update_tcp(stream.get_ref().0); diff --git a/src/vector/tls.rs b/src/vector/tls.rs index e3828d6..f067034 100644 --- a/src/vector/tls.rs +++ b/src/vector/tls.rs @@ -19,7 +19,9 @@ use tokio::net::TcpStream; use tokio::time::timeout; use tokio_rustls::{TlsConnector, client::TlsStream}; -use crate::common::{certificate_sha256, dial_tcp_from_local_ip, handshake_timeout}; +use crate::common::{ + AddressFamily, certificate_sha256, dial_tcp_from_local_ip_family, handshake_timeout, +}; use crate::protocol::{ProtocolVersion, SUPPORTED_ALPNS, TLS_EXPORTER_LEN, TlsExporter}; use super::config::PortalClientConfig; @@ -78,14 +80,9 @@ impl ClientTls { client.alpn_protocols = SUPPORTED_ALPNS.iter().map(|alpn| alpn.to_vec()).collect(); client.enable_early_data = false; - let server_name = ServerName::try_from( - config - .sni - .as_deref() - .unwrap_or(config.remote_host.as_str()) - .to_owned(), - ) - .map_err(|_| anyhow!("vector::tls::ClientTls::new: invalid TLS server name"))?; + let server_name = + ServerName::try_from(config.sni.as_deref().unwrap_or(config.host()).to_owned()) + .map_err(|_| anyhow!("vector::tls::ClientTls::new: invalid TLS server name"))?; Ok(Self { rustls: Arc::new(client), server_name, @@ -110,10 +107,12 @@ impl ClientTls { &self, endpoint: &str, dialer_ip: &str, + family: AddressFamily, ) -> Result<(TlsStream, TlsExporter, ProtocolVersion)> { - let stream = dial_tcp_from_local_ip(dialer_ip, endpoint, handshake_timeout()) - .await - .with_context(|| format!("vector::tls::connect_tcp: failed to dial {endpoint}"))?; + let stream = + dial_tcp_from_local_ip_family(dialer_ip, endpoint, handshake_timeout(), family) + .await + .with_context(|| format!("vector::tls::connect_tcp: failed to dial {endpoint}"))?; stream .set_nodelay(true) .context("vector::tls::connect_tcp: failed to set TCP_NODELAY")?; From d7f0974add46f73c171b8bf7a9ccabb947f3929f Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 06:55:46 +0800 Subject: [PATCH 10/62] Bind Portal carriers independently --- src/portal/conn/session.rs | 7 +- src/portal/listener.rs | 15 ++-- src/portal/mod.rs | 7 +- src/portal/mode.rs | 24 ++--- src/portal/runtime.rs | 114 +++++++++++++++++++++--- src/portal/setup.rs | 120 +++++++++++++------------ src/telemetry/hub.rs | 15 +++- src/tests/portal.rs | 106 ++++++++++++++++------- src/tests/portal/conn/vector.rs | 129 +++++++++++++++++++++++++-- src/tests/portal/listener.rs | 15 ++++ src/tests/portal/runtime.rs | 149 ++++++++++++++++++++++++++++++++ src/tests/telemetry/hub.rs | 9 ++ 12 files changed, 571 insertions(+), 139 deletions(-) create mode 100644 src/tests/portal/listener.rs create mode 100644 src/tests/portal/runtime.rs diff --git a/src/portal/conn/session.rs b/src/portal/conn/session.rs index c41d880..a659dc6 100644 --- a/src/portal/conn/session.rs +++ b/src/portal/conn/session.rs @@ -61,7 +61,12 @@ impl PortalSession { peer: self.conn.remote_address().to_string(), local: self.conn.local_ip().map_or_else( || self.portal.endpoint_addr.clone(), - |ip| std::net::SocketAddr::new(ip, self.portal.listen_port).to_string(), + |ip| { + self.portal.udp_listen_port.map_or_else( + || self.portal.endpoint_addr.clone(), + |port| std::net::SocketAddr::new(ip, port).to_string(), + ) + }, ), } } diff --git a/src/portal/listener.rs b/src/portal/listener.rs index 06eb3d6..79a8eb4 100644 --- a/src/portal/listener.rs +++ b/src/portal/listener.rs @@ -3,7 +3,7 @@ //! QUIC endpoint and TCP listener setup plus accept loops. -use std::net::{IpAddr, SocketAddr}; +use std::net::SocketAddr; use std::sync::Arc; use anyhow::{Context, Result}; @@ -178,15 +178,6 @@ pub(super) fn listen_tcp(addr: SocketAddr) -> Result { .with_context(|| format!("portal::listen_tcp: failed to listen for TLS/TCP on {addr}")) } -/// Formats a visible endpoint address without adding brackets to empty hosts. -pub(super) fn format_endpoint_addr(host: &str, port: u16) -> String { - match host.parse::() { - Ok(ip) => SocketAddr::new(ip, port).to_string(), - Err(_) if host.is_empty() => format!(":{port}"), - Err(_) => format!("{host}:{port}"), - } -} - /// Applies transport limits that should be set before the config is shared. pub(super) fn configure_transport( server_config: &mut quinn::ServerConfig, @@ -210,3 +201,7 @@ pub(super) fn configure_transport( Ok(()) } + +#[cfg(test)] +#[path = "../tests/portal/listener.rs"] +mod socket_tests; diff --git a/src/portal/mod.rs b/src/portal/mod.rs index 3aebfd8..84ee744 100644 --- a/src/portal/mod.rs +++ b/src/portal/mod.rs @@ -47,8 +47,11 @@ struct PortalInner { tls_mode: TLSMode, network_mode: NetworkMode, endpoint_addr: String, - bind_addrs: Vec, - listen_port: u16, + tcp_bind_addrs: Vec, + udp_bind_addrs: Vec, + allow_tcp_family_degrade: bool, + allow_udp_family_degrade: bool, + udp_listen_port: Option, outbound: PortalOutbound, rate_limit: i32, etar_limit: i32, diff --git a/src/portal/mode.rs b/src/portal/mode.rs index 31c44b8..edea887 100644 --- a/src/portal/mode.rs +++ b/src/portal/mode.rs @@ -5,10 +5,7 @@ use std::fmt; -use anyhow::Result; -use url::Url; - -/// Portal listener mode selected by the `net` URL query parameter. +/// Portal listener mode derived from declared carrier endpoints. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) enum NetworkMode { Mix, @@ -17,19 +14,12 @@ pub(crate) enum NetworkMode { } impl NetworkMode { - /// Parses the URL `net` query parameter, defaulting to mixed service. - pub(super) fn from_url(parsed_url: &Url) -> Result { - match parsed_url - .query_pairs() - .find(|(key, _)| key == "net") - .map(|(_, value)| value) - .as_deref() - { - None | Some("") => Ok(Self::Mix), - Some("mix") => Ok(Self::Mix), - Some("tcp") => Ok(Self::Tcp), - Some("udp") => Ok(Self::Udp), - Some(_) => Err(anyhow::anyhow!("portal::NetworkMode: invalid net mode")), + pub(super) fn from_carriers(tcp: bool, udp: bool) -> Self { + match (tcp, udp) { + (true, true) => Self::Mix, + (true, false) => Self::Tcp, + (false, true) => Self::Udp, + (false, false) => unreachable!("endpoint parser requires at least one carrier"), } } diff --git a/src/portal/runtime.rs b/src/portal/runtime.rs index 05d388a..d12b22c 100644 --- a/src/portal/runtime.rs +++ b/src/portal/runtime.rs @@ -65,6 +65,45 @@ impl Portal { Ok(listeners) => listeners, Err(error) => return self.start_failed(error), }; + for endpoint in &endpoints { + if let Ok(address) = endpoint.local_addr() { + self.inner + .logger + .info(format_args!("portal::run: listening on QUIC/UDP {address}")); + } + } + for listener in &tcp_listeners { + if let Ok(address) = listener.local_addr() { + self.inner + .logger + .info(format_args!("portal::run: listening on TLS/TCP {address}")); + } + } + let addresses = |addrs: Vec| { + if addrs.is_empty() { + "none".to_owned() + } else { + addrs + .iter() + .map(ToString::to_string) + .collect::>() + .join(",") + } + }; + self.inner.telemetry.set_listening_addresses( + &addresses( + tcp_listeners + .iter() + .map(TcpListener::local_addr) + .collect::>()?, + ), + &addresses( + endpoints + .iter() + .map(Endpoint::local_addr) + .collect::>()?, + ), + ); let telemetry_shutdown = CancellationToken::new(); let mut telemetry_tasks: JoinSet<()> = JoinSet::new(); match TelemetryServer::bind(self.inner.telemetry.clone()) { @@ -293,9 +332,8 @@ impl Portal { /// Returns the effective startup URL that is logged for operators. pub(super) fn effective_url(&self) -> String { let base = format!( - "portal://{}?net={}&tls={}&rate={}&etar={}&dial={}&socks={}&next={}", + "portal://{}?tls={}&rate={}&etar={}&dial={}&socks={}&next={}", self.inner.endpoint_addr, - self.inner.network_mode, self.inner.tls_mode, self.inner.rate_limit, self.inner.etar_limit, @@ -316,12 +354,14 @@ impl Portal { if !self.inner.network_mode.listens_udp() { return Ok(Vec::new()); } - self.inner - .bind_addrs - .iter() - .copied() - .map(|addr| listen_endpoint(self.inner.quic_server_config.clone(), addr)) - .collect() + bind_carrier( + &self.inner.udp_bind_addrs, + self.inner.allow_udp_family_degrade, + |addr| listen_endpoint(self.inner.quic_server_config.clone(), addr), + |addr, error| self.inner.logger.warn(format_args!( + "portal::listen_endpoints: UDP address family unavailable for {addr}; continuing: {error:#}" + )), + ).context("portal::listen_endpoints: failed to open UDP listeners") } /// Opens TLS/TCP listeners for network modes that accept TCP service. @@ -329,13 +369,55 @@ impl Portal { if !self.inner.network_mode.listens_tcp() { return Ok(Vec::new()); } - self.inner - .bind_addrs - .iter() - .copied() - .map(listen_tcp) - .collect() + bind_carrier( + &self.inner.tcp_bind_addrs, + self.inner.allow_tcp_family_degrade, + listen_tcp, + |addr, error| self.inner.logger.warn(format_args!( + "portal::listen_tcp_listeners: TCP address family unavailable for {addr}; continuing: {error:#}" + )), + ).context("portal::listen_tcp_listeners: failed to open TCP listeners") + } +} + +/// Owns every successful bind until the whole carrier has passed validation. +fn bind_carrier( + addresses: &[std::net::SocketAddr], + allow_degrade: bool, + mut bind: impl FnMut(std::net::SocketAddr) -> Result, + mut warn: impl FnMut(std::net::SocketAddr, &anyhow::Error), +) -> Result> { + let mut listeners = Vec::new(); + for &address in addresses { + match bind(address) { + Ok(listener) => listeners.push(listener), + Err(error) if allow_degrade && family_is_unavailable(&error) => warn(address, &error), + Err(error) => return Err(error), + } + } + if listeners.is_empty() { + anyhow::bail!("no declared address could be bound"); } + Ok(listeners) +} + +fn family_is_unavailable(error: &anyhow::Error) -> bool { + error.chain().any(|cause| { + cause + .downcast_ref::() + .is_some_and(io_error_is_family_unavailable) + }) +} + +fn io_error_is_family_unavailable(error: &std::io::Error) -> bool { + #[cfg(unix)] + const FAMILY_UNAVAILABLE: i32 = libc::EAFNOSUPPORT; + #[cfg(windows)] + const FAMILY_UNAVAILABLE: i32 = 10047; // WSAEAFNOSUPPORT + matches!( + error.kind(), + std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::Unsupported + ) || error.raw_os_error() == Some(FAMILY_UNAVAILABLE) } fn listener_exit_error( @@ -350,3 +432,7 @@ fn listener_exit_error( None => anyhow::anyhow!("portal::run: {name} listener set became empty unexpectedly"), } } + +#[cfg(test)] +#[path = "../tests/portal/runtime.rs"] +mod family_error_tests; diff --git a/src/portal/setup.rs b/src/portal/setup.rs index d05909d..87c0cc9 100644 --- a/src/portal/setup.rs +++ b/src/portal/setup.rs @@ -11,19 +11,20 @@ use url::Url; use crate::common::{ DEFAULT_RATE_LIMIT, LifeMode, LifeReason, LifeState, Lifecycle, Logger, OutboundDialer, - SocksConfig, bind_udp_addrs, first_raw_query_value, init_dialer_ip, + ServiceEndpoint, SocksConfig, first_raw_query_value, init_dialer_ip, new_server_configs_with_reload_interval, query_first, rate_limit_bytes_per_second, + resolve_bind_addrs, }; use crate::protocol::Credentials; use crate::telemetry::{InstanceRole, TelemetryHub}; use crate::transport::{Buffers, RateLimiter, Stats}; use crate::vector::{PortalClient, PortalClientConfig}; -use super::listener::{configure_transport, format_endpoint_addr}; +use super::listener::configure_transport; use super::{NetworkMode, Portal, PortalInner, UdpFlowLimits, admission, outbound::PortalOutbound}; const PORTAL_QUERY_PARAMETERS: &[&str] = &[ - "net", "tls", "crt", "key", "rate", "etar", "dial", "socks", "next", "log", + "tls", "crt", "key", "rate", "etar", "dial", "socks", "next", "log", ]; const PORTAL_UPSTREAM_PARAMETERS: &[&str] = &["up", "down", "mux", "sni", "pin"]; @@ -59,71 +60,76 @@ impl Portal { lifecycle: Arc, ) -> Result { if parsed_url.scheme() != "portal" { - anyhow::bail!("portal::new: URL scheme must be portal"); + anyhow::bail!("Portal configuration: URL scheme must be portal"); } if parsed_url.password().is_some() { - anyhow::bail!("portal::new: password userinfo is not supported"); + anyhow::bail!("Portal configuration: password userinfo is not supported"); } if parsed_url.fragment().is_some() { - anyhow::bail!("portal::new: URL fragments are not supported"); - } - if !parsed_url.path().is_empty() { - anyhow::bail!("portal::new: URL paths are not supported"); + anyhow::bail!("Portal configuration: URL fragments are not supported"); } let mut query = query_first(&parsed_url, PORTAL_QUERY_PARAMETERS) - .map_err(|e| anyhow::anyhow!("portal::new: {e}"))?; - validate_query(&query).map_err(|e| anyhow::anyhow!("portal::new: {e}"))?; - let port = parsed_url - .port() - .ok_or_else(|| anyhow::anyhow!("portal::new: missing listen port"))?; - if port == 0 { - anyhow::bail!("portal::new: listen port must be non-zero"); + .map_err(|e| anyhow::anyhow!("Portal configuration: invalid query: {e}"))?; + validate_query(&query).map_err(|e| anyhow::anyhow!("Portal configuration: {e}"))?; + let mut service_endpoint = ServiceEndpoint::parse(&parsed_url, true, "Portal endpoint")?; + let bind_host = listen_host + .unwrap_or(service_endpoint.host.as_str()) + .to_owned(); + if listen_host == Some("") { + service_endpoint.host = "*".to_owned(); } - let credentials = - Credentials::new(&parsed_url).map_err(|e| anyhow::anyhow!("portal::new: {e}"))?; + let credentials = Credentials::new(&parsed_url)?; let runtime = super::config::PortalRuntimeConfig::from_env() - .map_err(|e| anyhow::anyhow!("portal::new: invalid runtime configuration: {e}"))?; + .map_err(|e| anyhow::anyhow!("Portal configuration: invalid runtime setting: {e}"))?; let network_mode = - NetworkMode::from_url(&parsed_url).map_err(|e| anyhow::anyhow!("portal::new: {e}"))?; - let (tls_mode, tls_server_config, mut quic_server_config) = - new_server_configs_with_reload_interval( - &parsed_url, - runtime.reload_interval, - logger.clone(), - ) - .map_err(|e| anyhow::anyhow!("portal::new: {e}"))?; - - let host = listen_host.unwrap_or_else(|| parsed_url.host_str().unwrap_or_default()); - let endpoint_addr = format_endpoint_addr(host, port); - let bind_addrs = bind_udp_addrs(host, port) - .map_err(|e| anyhow::anyhow!("portal::new: failed to bind listen address: {e}"))?; - + NetworkMode::from_carriers(service_endpoint.has_tcp(), service_endpoint.has_udp()); let dialer_ip = init_dialer_ip(query.get("dial").map(String::as_str)); - let socks = SocksConfig::from_url(&parsed_url).map_err(|e| { - anyhow::anyhow!("portal::new: failed to parse socks configuration: {e}") - })?; + let socks = SocksConfig::from_url(&parsed_url) + .map_err(|e| anyhow::anyhow!("Portal configuration: invalid socks parameter: {e}"))?; let next = match query.get("next").map(String::as_str) { None | Some("none") => None, - Some("") => anyhow::bail!("portal::new: empty next parameter"), + Some("") => anyhow::bail!("Portal configuration: next must not be empty"), Some(_) => { query.extend( - query_first(&parsed_url, PORTAL_UPSTREAM_PARAMETERS) - .map_err(|e| anyhow::anyhow!("portal::new: {e}"))?, + query_first(&parsed_url, PORTAL_UPSTREAM_PARAMETERS).map_err(|e| { + anyhow::anyhow!("Portal configuration: invalid upstream query: {e}") + })?, ); let raw = first_raw_query_value(&parsed_url, "next") .expect("decoded next came from the raw query"); - Some( - PortalClientConfig::from_upstream_authority(raw, &query, &dialer_ip) - .map_err(|error| anyhow::anyhow!("portal::new: {error}"))?, - ) + Some(PortalClientConfig::from_upstream_authority( + raw, &query, &dialer_ip, + )?) } }; if socks.is_some() && next.is_some() { - anyhow::bail!("portal::new: socks and next are mutually exclusive"); + anyhow::bail!("Portal configuration: socks and next are mutually exclusive"); } let rate_limit = parse_rate(&query, "rate")?; let etar_limit = parse_rate(&query, "etar")?; + let (tls_mode, tls_server_config, mut quic_server_config) = + new_server_configs_with_reload_interval( + &parsed_url, + runtime.reload_interval, + logger.clone(), + ) + .map_err(|e| anyhow::anyhow!("Portal configuration: invalid TLS settings: {e}"))?; + + let endpoint_addr = service_endpoint.canonical(); + let tcp_bind_addrs = service_endpoint + .tcp + .map(|endpoint| resolve_bind_addrs(&bind_host, endpoint)) + .transpose() + .map_err(|e| anyhow::anyhow!("Portal endpoint: failed to resolve TCP address: {e}"))? + .unwrap_or_default(); + let udp_bind_addrs = service_endpoint + .udp + .map(|endpoint| resolve_bind_addrs(&bind_host, endpoint)) + .transpose() + .map_err(|e| anyhow::anyhow!("Portal endpoint: failed to resolve UDP address: {e}"))? + .unwrap_or_default(); + configure_transport(&mut quic_server_config, runtime.udp_idle_timeout, None)?; let read_bps = rate_limit_bytes_per_second(rate_limit) as i64; @@ -142,7 +148,7 @@ impl Portal { |(config, _)| format!("next={} {}", config.endpoint(), config.effective_route()), ); let telemetry_summary = format!( - "net={network_mode} tls={tls_mode} rate={rate_limit} etar={etar_limit} dial={dialer_ip} socks={socks_endpoint} {next_summary}", + "listen={endpoint_addr} tls={tls_mode} rate={rate_limit} etar={etar_limit} dial={dialer_ip} socks={socks_endpoint} {next_summary}", ); let telemetry = TelemetryHub::for_current_process( InstanceRole::Portal, @@ -168,8 +174,17 @@ impl Portal { tls_mode, network_mode, endpoint_addr, - bind_addrs, - listen_port: port, + tcp_bind_addrs, + udp_bind_addrs, + allow_tcp_family_degrade: service_endpoint.host == "*" + && service_endpoint.tcp.is_some_and(|endpoint| { + endpoint.family == crate::common::AddressFamily::Any + }), + allow_udp_family_degrade: service_endpoint.host == "*" + && service_endpoint.udp.is_some_and(|endpoint| { + endpoint.family == crate::common::AddressFamily::Any + }), + udp_listen_port: service_endpoint.udp.map(|endpoint| endpoint.port), outbound, rate_limit, etar_limit, @@ -200,9 +215,7 @@ impl Portal { } fn validate_query(query: &std::collections::HashMap) -> Result<()> { - for name in [ - "log", "tls", "crt", "key", "net", "rate", "etar", "dial", "socks", - ] { + for name in ["log", "tls", "crt", "key", "rate", "etar", "dial", "socks"] { if query.get(name).is_some_and(String::is_empty) { anyhow::bail!("empty {name} parameter"); } @@ -220,11 +233,6 @@ fn validate_query(query: &std::collections::HashMap) -> Result<( { anyhow::bail!("tls=1 or tls=2 required"); } - if let Some(net) = query.get("net") - && !matches!(net.as_str(), "mix" | "tcp" | "udp") - { - anyhow::bail!("invalid net mode"); - } let tls_is_ca = query.get("tls").is_some_and(|value| value == "2"); let has_crt = query.contains_key("crt"); let has_key = query.contains_key("key"); @@ -246,6 +254,6 @@ fn parse_rate(query: &std::collections::HashMap, name: &str) -> .parse::() .ok() .filter(|value| *value >= 0) - .ok_or_else(|| anyhow::anyhow!("invalid {name} rate limit")) + .ok_or_else(|| anyhow::anyhow!("{name} must be a non-negative integer")) }) } diff --git a/src/telemetry/hub.rs b/src/telemetry/hub.rs index b1bbc00..f225f93 100644 --- a/src/telemetry/hub.rs +++ b/src/telemetry/hub.rs @@ -4,7 +4,7 @@ //! In-process structured telemetry publisher. use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, OnceLock}; use std::time::{Duration, Instant}; use tokio::sync::{broadcast, watch}; @@ -23,6 +23,7 @@ const EVENT_CAPACITY: usize = 1_024; /// The in-process publisher shared by runtime orchestration and every flow. pub(crate) struct TelemetryHub { descriptor: InstanceDescriptor, + listening_descriptor: OnceLock, lifecycle: watch::Sender, snapshots: watch::Sender, events: broadcast::Sender, @@ -69,6 +70,7 @@ impl TelemetryHub { let (events, _) = broadcast::channel(EVENT_CAPACITY); Arc::new(Self { descriptor, + listening_descriptor: OnceLock::new(), lifecycle, snapshots, events, @@ -81,7 +83,16 @@ impl TelemetryHub { } pub(crate) fn descriptor(&self) -> &InstanceDescriptor { - &self.descriptor + self.listening_descriptor.get().unwrap_or(&self.descriptor) + } + + /// Publishes actual bound addresses before the telemetry server starts. + pub(crate) fn set_listening_addresses(&self, tcp: &str, udp: &str) { + let mut descriptor = self.descriptor.clone(); + descriptor + .config_summary + .push_str(&format!(" tcp={tcp} udp={udp}")); + let _ = self.listening_descriptor.set(descriptor); } pub(crate) fn unavailable_reason(&self) -> Option<&str> { diff --git a/src/tests/portal.rs b/src/tests/portal.rs index 9c2ef50..5096f58 100644 --- a/src/tests/portal.rs +++ b/src/tests/portal.rs @@ -21,19 +21,20 @@ fn empty_host_listens_on_both_wildcard_families() { ) .unwrap(); - assert_eq!(portal.inner.endpoint_addr, ":2077"); + assert_eq!(portal.inner.endpoint_addr, "*:2077"); assert_eq!( - portal.inner.bind_addrs, + portal.inner.tcp_bind_addrs, vec![ SocketAddr::from(([0, 0, 0, 0], 2077)), SocketAddr::from(([0u16; 8], 2077)), ] ); + assert_eq!(portal.inner.udp_bind_addrs, portal.inner.tcp_bind_addrs); assert_eq!(portal.inner.outbound.dialer_ip(), "127.0.0.1"); assert_eq!(portal.inner.network_mode, NetworkMode::Mix); assert_eq!( portal.effective_url(), - "portal://:2077?net=mix&tls=1&rate=0&etar=0&dial=127.0.0.1&socks=none&next=none" + "portal://*:2077?tls=1&rate=0&etar=0&dial=127.0.0.1&socks=none&next=none" ); } @@ -52,34 +53,50 @@ fn explicit_wildcard_host_selects_one_address_family() { assert_eq!(ipv4.inner.endpoint_addr, "0.0.0.0:2077"); assert_eq!( - ipv4.inner.bind_addrs, + ipv4.inner.tcp_bind_addrs, vec![SocketAddr::from(([0, 0, 0, 0], 2077))] ); assert_eq!(ipv4.inner.outbound.dialer_ip(), "auto"); assert_eq!(ipv6.inner.endpoint_addr, "[::]:2077"); assert_eq!( - ipv6.inner.bind_addrs, + ipv6.inner.tcp_bind_addrs, vec![SocketAddr::from(([0u16; 8], 2077))] ); assert_eq!(ipv6.inner.outbound.dialer_ip(), "::1"); } #[test] -fn network_mode_accepts_supported_values_and_defaults_to_mix() { +fn explicit_carriers_have_independent_ports_and_families() { + let portal = Portal::new( + Url::parse("portal://secret@*/tcp4:2077/udp6:3088").unwrap(), + test_logger(), + ) + .unwrap(); + + assert_eq!(portal.inner.endpoint_addr, "*/tcp4:2077/udp6:3088"); + assert_eq!( + portal.inner.tcp_bind_addrs, + vec![SocketAddr::from(([0, 0, 0, 0], 2077))] + ); + assert_eq!( + portal.inner.udp_bind_addrs, + vec![SocketAddr::from(([0u16; 8], 3088))] + ); + assert_eq!(portal.inner.network_mode, NetworkMode::Mix); +} + +#[test] +fn carrier_paths_select_network_mode_and_net_is_ignored() { let cases = [ - ("", NetworkMode::Mix), - ("?net=mix", NetworkMode::Mix), - ("?net=tcp", NetworkMode::Tcp), - ("?net=udp", NetworkMode::Udp), + ("portal://secret@127.0.0.1:2077", NetworkMode::Mix), + ("portal://secret@127.0.0.1:2077?net=tcp", NetworkMode::Mix), + ("portal://secret@127.0.0.1/tcp:2077", NetworkMode::Tcp), + ("portal://secret@127.0.0.1/udp:2077", NetworkMode::Udp), ]; - for (query, expected) in cases { - let portal = Portal::new( - Url::parse(&format!("portal://secret@127.0.0.1:2077{query}")).unwrap(), - test_logger(), - ) - .unwrap(); + for (raw, expected) in cases { + let portal = Portal::new(Url::parse(raw).unwrap(), test_logger()).unwrap(); assert_eq!(portal.inner.network_mode, expected); } } @@ -92,13 +109,15 @@ fn network_mode_checkpoint_values_match_listener_modes() { } #[test] -fn network_mode_rejects_unknown_values() { - let error = Portal::new( +fn net_is_an_ignored_unknown_parameter() { + let portal = Portal::new( Url::parse("portal://secret@127.0.0.1:2077?net=auto").unwrap(), test_logger(), - ); + ) + .unwrap(); - assert!(error.is_err()); + assert_eq!(portal.inner.network_mode, NetworkMode::Mix); + assert!(!portal.effective_url().contains("net=")); } #[test] @@ -143,6 +162,26 @@ fn native_next_defaults_to_quic_and_redacts_the_shared_key() { assert_eq!(portal.inner.outbound.ping_ms(), 0); } +#[test] +fn native_next_uses_shared_endpoint_grammar_and_single_carrier_defaults() { + let portal = Portal::new( + Url::parse("portal://relay-key@*/tcp4:2077?next=upstream-key@relay.example/udp6:3088") + .unwrap(), + test_logger(), + ) + .unwrap(); + + assert_eq!(portal.inner.network_mode, NetworkMode::Tcp); + assert_eq!( + portal.inner.outbound.next_endpoint(), + "relay.example/udp6:3088" + ); + assert_eq!( + portal.inner.outbound.next_transport().as_deref(), + Some("up=udp down=udp mux=0 sni=none pin=none") + ); +} + #[test] fn native_next_reuses_transport_identity_and_source_binding() { let portal = Portal::new( @@ -298,17 +337,21 @@ fn all_network_modes_reject_tls_zero() { #[tokio::test] async fn network_mode_binds_only_selected_transports() { - for (query, expected_tcp, expected_udp) in [ - ("", 1, 1), - ("?net=mix", 1, 1), - ("?net=tcp", 1, 0), - ("?net=udp", 0, 1), - ] { + for (path, expected_tcp, expected_udp) in [("", 1, 1), ("/tcp:PORT", 1, 0), ("/udp:PORT", 0, 1)] + { let reservation = TcpListener::bind("127.0.0.1:0").await.unwrap(); let port = reservation.local_addr().unwrap().port(); drop(reservation); let portal = Portal::new( - Url::parse(&format!("portal://secret@127.0.0.1:{port}{query}")).unwrap(), + Url::parse(&if path.is_empty() { + format!("portal://secret@127.0.0.1:{port}") + } else { + format!( + "portal://secret@127.0.0.1{}", + path.replace("PORT", &port.to_string()) + ) + }) + .unwrap(), test_logger(), ) .unwrap(); @@ -325,9 +368,9 @@ fn portal_url_contract_rejects_invalid_structure_and_selected_values() { for raw in [ "vector://secret@127.0.0.1:2077", "portal://secret:password@127.0.0.1:2077", - "portal://secret@127.0.0.1:2077/path", + "portal://secret@127.0.0.1:2077/tcp:2077", + "portal://secret@127.0.0.1/not-a-carrier:2077", "portal://secret@127.0.0.1:2077#fragment", - "portal://secret@127.0.0.1:2077?net=", "portal://secret@127.0.0.1:2077?socks=", "portal://secret@127.0.0.1:2077?rate=-1", "portal://secret@127.0.0.1:2077?dial=not-an-ip", @@ -351,9 +394,10 @@ fn portal_ignores_unknown_parameters_and_keeps_first_duplicate() { test_logger(), ) .unwrap(); - assert_eq!(portal.inner.network_mode, NetworkMode::Tcp); + assert_eq!(portal.inner.network_mode, NetworkMode::Mix); assert_eq!(portal.inner.rate_limit, 1); - assert!(portal.effective_url().contains("?net=tcp&tls=1&")); + assert!(portal.effective_url().contains("?tls=1&")); + assert!(!portal.effective_url().contains("net=")); assert!(!portal.effective_url().contains("alpn=")); assert!(!portal.effective_url().contains("mux=")); assert!(!portal.effective_url().contains("pool=")); diff --git a/src/tests/portal/conn/vector.rs b/src/tests/portal/conn/vector.rs index 132b825..5ba8de0 100644 --- a/src/tests/portal/conn/vector.rs +++ b/src/tests/portal/conn/vector.rs @@ -108,11 +108,22 @@ async fn reserve_tcp_port() -> (u16, TcpListener) { (listener.local_addr().unwrap().port(), listener) } +async fn reserve_udp_port_except(excluded: u16) -> (u16, UdpSocket) { + loop { + let socket = UdpSocket::bind("127.0.0.1:0").await.unwrap(); + let port = socket.local_addr().unwrap().port(); + if port != excluded { + return (port, socket); + } + } +} + async fn start_runtime(up: &str, down: &str, mux: u8) -> TestRuntime { - let (portal_port, tcp_reservation, udp_reservation) = reserve_mixed_port().await; + let (tcp_port, tcp_reservation) = reserve_tcp_port().await; + let (udp_port, udp_reservation) = reserve_udp_port_except(tcp_port).await; let portal = Portal::new( Url::parse(&format!( - "portal://secret@127.0.0.1:{portal_port}?log=none&net=mix" + "portal://secret@127.0.0.1/tcp:{tcp_port}/udp:{udp_port}?log=none" )) .unwrap(), Logger::new(LogLevel::None, false), @@ -139,7 +150,7 @@ async fn start_runtime(up: &str, down: &str, mux: u8) -> TestRuntime { let (socks_port, socks_reservation) = reserve_tcp_port().await; let vector = Vector::new( Url::parse(&format!( - "vector://secret@127.0.0.1:{portal_port}?log=none&up={up}&down={down}&mux={mux}&socks=127.0.0.1:{socks_port}" + "vector://secret@127.0.0.1/tcp:{tcp_port}/udp:{udp_port}?log=none&up={up}&down={down}&mux={mux}&socks=127.0.0.1:{socks_port}" )) .unwrap(), Logger::new(LogLevel::None, false), @@ -316,10 +327,11 @@ async fn mux_fifth_active_tcp_flow_opens_a_second_shard() { async fn start_chain_runtime(up: &str, down: &str) -> ChainRuntime { let logger = || Logger::new(LogLevel::None, false); - let (origin_port, origin_tcp_reservation, origin_udp_reservation) = reserve_mixed_port().await; + let (origin_tcp_port, origin_tcp_reservation) = reserve_tcp_port().await; + let (origin_udp_port, origin_udp_reservation) = reserve_udp_port_except(origin_tcp_port).await; let origin = Portal::new( Url::parse(&format!( - "portal://origin-secret@127.0.0.1:{origin_port}?log=none&net=mix" + "portal://origin-secret@127.0.0.1/tcp:{origin_tcp_port}/udp:{origin_udp_port}?log=none" )) .unwrap(), logger(), @@ -333,7 +345,7 @@ async fn start_chain_runtime(up: &str, down: &str) -> ChainRuntime { let (relay_port, relay_tcp_reservation, relay_udp_reservation) = reserve_mixed_port().await; let relay = Portal::new( Url::parse(&format!( - "portal://relay-secret@127.0.0.1:{relay_port}?log=none&net=mix&next=origin-secret@127.0.0.1:{origin_port}&up={up}&down={down}&mux=1" + "portal://relay-secret@127.0.0.1:{relay_port}?log=none&next=origin-secret@127.0.0.1/tcp:{origin_tcp_port}/udp:{origin_udp_port}&up={up}&down={down}&mux=1" )) .unwrap(), logger(), @@ -751,3 +763,108 @@ async fn native_portal_chain_relays_tcp_and_udp_for_every_upstream_route_policy( #[path = "vector/chain_failure.rs"] mod chain_failure; + +#[tokio::test] +async fn single_carrier_defaults_relay_tcp4_to_udp6() { + let origin_reservation = UdpSocket::bind("[::1]:0").await.unwrap(); + let origin_port = origin_reservation.local_addr().unwrap().port(); + let origin = Portal::new( + Url::parse(&format!( + "portal://origin-secret@[::1]/udp6:{origin_port}?log=none" + )) + .unwrap(), + Logger::new(LogLevel::None, false), + ) + .unwrap(); + assert!(origin.listen_tcp_listeners().unwrap().is_empty()); + drop(origin_reservation); + let endpoint = origin.listen_endpoints().unwrap().pop().unwrap(); + + let (relay_port, reservation) = reserve_tcp_port().await; + let relay = Portal::new( + Url::parse(&format!("portal://relay-secret@127.0.0.1/tcp4:{relay_port}?next=origin-secret@[::1]/udp6:{origin_port}&log=none")).unwrap(), + Logger::new(LogLevel::None, false), + ).unwrap(); + assert!(relay.listen_endpoints().unwrap().is_empty()); + drop(reservation); + let listener = relay.listen_tcp_listeners().unwrap().pop().unwrap(); + let shutdown = CancellationToken::new(); + let portal_tasks = vec![ + tokio::spawn(crate::portal::listener::accept_endpoint_loop( + origin.inner.clone(), + endpoint.clone(), + shutdown.clone(), + shutdown.clone(), + )), + tokio::spawn(crate::portal::listener::accept_tcp_loop( + relay.inner.clone(), + listener, + shutdown.clone(), + shutdown.clone(), + )), + ]; + let (socks_port, reservation) = reserve_tcp_port().await; + let vector = Vector::new( + Url::parse(&format!("vector://relay-secret@127.0.0.1/tcp4:{relay_port}?socks=127.0.0.1:{socks_port}&log=none")).unwrap(), + Logger::new(LogLevel::None, false), + ).unwrap(); + drop(reservation); + let vector_task = tokio::spawn(vector.run()); + let socks = SocketAddr::from(([127, 0, 0, 1], socks_port)); + wait_for_socks(socks).await; + let runtime = ChainRuntime { + shutdown, + endpoints: vec![endpoint], + portal_tasks, + vector_task, + relay, + socks, + }; + + timeout(TEST_TIMEOUT, async { + let target = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let address = target.local_addr().unwrap(); + let echo = tokio::spawn(async move { + let (mut stream, _) = target.accept().await.unwrap(); + let mut packet = [0; 4]; + stream.read_exact(&mut packet).await.unwrap(); + stream.write_all(&packet).await.unwrap(); + }); + let mut stream = TcpStream::connect(socks).await.unwrap(); + negotiate_socks(&mut stream).await; + stream.write_all(&ip_request(1, address)).await.unwrap(); + read_ipv4_reply(&mut stream).await; + stream.write_all(b"ping").await.unwrap(); + let mut packet = [0; 4]; + stream.read_exact(&mut packet).await.unwrap(); + assert_eq!(&packet, b"ping"); + echo.await.unwrap(); + + let target = UdpSocket::bind("127.0.0.1:0").await.unwrap(); + let address = target.local_addr().unwrap(); + let echo = tokio::spawn(async move { + let mut packet = [0; 64]; + let (length, peer) = target.recv_from(&mut packet).await.unwrap(); + target.send_to(&packet[..length], peer).await.unwrap(); + }); + let mut control = TcpStream::connect(socks).await.unwrap(); + negotiate_socks(&mut control).await; + control + .write_all(&ip_request(3, "0.0.0.0:0".parse().unwrap())) + .await + .unwrap(); + let udp_relay = read_ipv4_reply(&mut control).await; + let client = UdpSocket::bind("127.0.0.1:0").await.unwrap(); + let mut packet = vec![0, 0, 0]; + packet.extend_from_slice(&ip_request(0, address)[3..]); + packet.extend_from_slice(b"ping"); + client.send_to(&packet, udp_relay).await.unwrap(); + let mut reply = [0; 64]; + let (length, _) = client.recv_from(&mut reply).await.unwrap(); + assert_eq!(&reply[10..length], b"ping"); + echo.await.unwrap(); + }) + .await + .unwrap(); + runtime.stop().await; +} diff --git a/src/tests/portal/listener.rs b/src/tests/portal/listener.rs new file mode 100644 index 0000000..0b595b9 --- /dev/null +++ b/src/tests/portal/listener.rs @@ -0,0 +1,15 @@ +use super::*; + +#[tokio::test] +async fn ipv6_sockets_are_v6_only_and_allow_ipv4_on_the_same_port() { + let udp = bind_quic_socket("[::]:0".parse().unwrap()).unwrap(); + assert!(socket2::SockRef::from(&udp).only_v6().unwrap()); + let udp4 = std::net::UdpSocket::bind(("0.0.0.0", udp.local_addr().unwrap().port())).unwrap(); + let tcp = listen_tcp("[::]:0".parse().unwrap()) + .unwrap() + .into_std() + .unwrap(); + assert!(socket2::SockRef::from(&tcp).only_v6().unwrap()); + let tcp4 = std::net::TcpListener::bind(("0.0.0.0", tcp.local_addr().unwrap().port())).unwrap(); + drop((udp, udp4, tcp, tcp4)); +} diff --git a/src/tests/portal/runtime.rs b/src/tests/portal/runtime.rs new file mode 100644 index 0000000..a8d855d --- /dev/null +++ b/src/tests/portal/runtime.rs @@ -0,0 +1,149 @@ +use super::{bind_carrier, io_error_is_family_unavailable}; +use std::io::{Error, ErrorKind}; +use std::net::{SocketAddr, TcpListener}; + +#[tokio::test] +async fn dns_listener_binds_every_unique_resolved_address() { + use std::net::ToSocketAddrs; + let reservation = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = reservation.local_addr().unwrap().port(); + let portal = super::Portal::new( + url::Url::parse(&format!("portal://secret@localhost/tcp:{port}")).unwrap(), + crate::common::Logger::new(crate::common::LogLevel::None, false), + ) + .unwrap(); + let mut expected = ("localhost", port) + .to_socket_addrs() + .unwrap() + .collect::>(); + expected.sort_unstable(); + expected.dedup(); + drop(reservation); + let listeners = portal.listen_tcp_listeners().unwrap(); + let mut actual = listeners + .iter() + .map(|listener| listener.local_addr().unwrap()) + .collect::>(); + actual.sort_unstable(); + assert_eq!(actual, expected); +} + +#[tokio::test] +async fn tcp_startup_failure_releases_already_opened_quic_socket() { + let occupied = TcpListener::bind("127.0.0.1:0").unwrap(); + let tcp_port = occupied.local_addr().unwrap().port(); + let reservation = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); + let address = reservation.local_addr().unwrap(); + let portal = super::Portal::new( + url::Url::parse(&format!( + "portal://secret@127.0.0.1/tcp4:{tcp_port}/udp4:{}", + address.port() + )) + .unwrap(), + crate::common::Logger::new(crate::common::LogLevel::None, false), + ) + .unwrap(); + drop(reservation); + assert!(portal.run().await.is_err()); + tokio::time::timeout(std::time::Duration::from_secs(1), async { + loop { + match std::net::UdpSocket::bind(address) { + Ok(socket) => { + drop(socket); + break; + } + Err(error) if error.kind() == ErrorKind::AddrInUse => { + tokio::task::yield_now().await + } + Err(error) => panic!("unexpected rebind error: {error}"), + } + } + }) + .await + .expect("QUIC socket leaked after TCP startup failure"); +} + +#[test] +fn injected_family_failure_warns_only_when_degradation_is_allowed() { + let addresses: [SocketAddr; 2] = [ + "0.0.0.0:2077".parse().unwrap(), + "[::]:2077".parse().unwrap(), + ]; + for allow in [false, true] { + let mut warnings = Vec::new(); + let result = bind_carrier( + &addresses, + allow, + |addr| { + if addr.is_ipv6() { + Err( + Error::new(ErrorKind::AddrNotAvailable, "injected unavailable family") + .into(), + ) + } else { + Ok(addr) + } + }, + |addr, _| warnings.push(addr), + ); + assert_eq!(result.is_ok(), allow); + assert_eq!(warnings.len(), usize::from(allow)); + if let Ok(bound) = result { + assert_eq!(bound, [addresses[0]]); + } + } + assert!( + bind_carrier::<()>( + &addresses, + true, + |_| Err(Error::new(ErrorKind::AddrNotAvailable, "unavailable").into()), + |_, _| {} + ) + .is_err() + ); +} + +#[test] +fn fatal_bind_failure_releases_preceding_socket_even_with_degradation_enabled() { + for kind in [ErrorKind::AddrInUse, ErrorKind::PermissionDenied] { + let mut bound = None; + let addresses = ["127.0.0.1:0".parse().unwrap(); 2]; + let result = bind_carrier( + &addresses, + true, + |addr| { + if bound.is_some() { + return Err(Error::new(kind, "injected fatal failure").into()); + } + let socket = TcpListener::bind(addr)?; + bound = Some(socket.local_addr()?); + Ok(socket) + }, + |_, _| panic!("fatal errors must not degrade"), + ); + assert!(result.is_err()); + let rebound = TcpListener::bind(bound.unwrap()).expect("preceding listener leaked"); + drop(rebound); + } +} + +#[test] +fn classifies_portable_and_platform_family_errors() { + assert!(io_error_is_family_unavailable(&std::io::Error::new( + std::io::ErrorKind::Unsupported, + "family unavailable", + ))); + #[cfg(unix)] + let codes = [libc::EAFNOSUPPORT]; + #[cfg(windows)] + let codes = [10047]; + for code in codes { + assert!(io_error_is_family_unavailable( + &std::io::Error::from_raw_os_error(code) + )); + } + assert!(!io_error_is_family_unavailable(&std::io::Error::new( + std::io::ErrorKind::AddrInUse, + "occupied", + ))); +} diff --git a/src/tests/telemetry/hub.rs b/src/tests/telemetry/hub.rs index 50333d7..2868e42 100644 --- a/src/tests/telemetry/hub.rs +++ b/src/tests/telemetry/hub.rs @@ -23,6 +23,15 @@ fn descriptor() -> InstanceDescriptor { } } +#[test] +fn listener_summary_uses_bound_addresses_without_inventing_a_second_family() { + let hub = TelemetryHub::new(descriptor()); + hub.set_listening_addresses("0.0.0.0:2077", "none"); + let summary = &hub.descriptor().config_summary; + assert!(summary.ends_with("tcp=0.0.0.0:2077 udp=none")); + assert!(!summary.contains("[::]")); +} + #[test] fn access_span_finishes_only_once() { let hub = TelemetryHub::new(descriptor()); From fa0e32bbf2b79786c867a24559437e5446732a5a Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 06:55:56 +0800 Subject: [PATCH 11/62] Reject invalid service URLs early --- src/common/config.rs | 4 +- src/common/socks/config.rs | 44 ++++++------- src/main.rs | 64 +++++++++++-------- src/protocol/auth.rs | 6 +- src/protocol/util.rs | 4 +- src/tests/main.rs | 122 ++++++++++++++++++++++++++++++++++++- src/vector/mod.rs | 6 +- 7 files changed, 187 insertions(+), 63 deletions(-) diff --git a/src/common/config.rs b/src/common/config.rs index 793838d..a22fbb8 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -67,7 +67,7 @@ fn decode_query_component(raw: &str, name: &str) -> Result { || !bytes[index + 1].is_ascii_hexdigit() || !bytes[index + 2].is_ascii_hexdigit() { - bail!("common::config::query_first: invalid percent encoding in {name}"); + bail!("invalid percent encoding in {name}"); } index += 3; } else { @@ -76,7 +76,7 @@ fn decode_query_component(raw: &str, name: &str) -> Result { } percent_decode_str(raw) .decode_utf8() - .with_context(|| format!("common::config::query_first: invalid UTF-8 in {name}")) + .with_context(|| format!("invalid UTF-8 in {name}")) .map(|value| value.into_owned()) } diff --git a/src/common/socks/config.rs b/src/common/socks/config.rs index 4b219a1..07e10a6 100644 --- a/src/common/socks/config.rs +++ b/src/common/socks/config.rs @@ -90,13 +90,13 @@ pub(crate) fn parse_socks_value(raw_value: &str) -> Result<(String, Option Result<(String, u16)> { let (host, raw_port) = if let Some(rest) = value.strip_prefix('[') { - let end = rest.find(']').ok_or_else(|| { - anyhow!("common::socks::parse_host_port: invalid {name}: missing ']'") - })?; + let end = rest + .find(']') + .ok_or_else(|| anyhow!("invalid {name}: missing closing ']'"))?; let host = &rest[..end]; - let port = rest[end + 1..].strip_prefix(':').ok_or_else(|| { - anyhow!("common::socks::parse_host_port: invalid {name}: missing port") - })?; + let port = rest[end + 1..] + .strip_prefix(':') + .ok_or_else(|| anyhow!("invalid {name}: expected ':' followed by a port"))?; if host.parse::().is_err() { - bail!("common::socks::parse_host_port: invalid {name}: bracketed host must be IPv6"); + bail!("invalid {name}: brackets may only contain an IPv6 address"); } (host, port) } else { - let (host, port) = value.rsplit_once(':').ok_or_else(|| { - anyhow!("common::socks::parse_host_port: invalid {name}: missing port") - })?; + let (host, port) = value + .rsplit_once(':') + .ok_or_else(|| anyhow!("invalid {name}: expected HOST:PORT"))?; if host.contains(':') { - bail!("common::socks::parse_host_port: invalid {name}: IPv6 requires brackets"); + bail!("invalid {name}: IPv6 addresses must be enclosed in brackets"); } (host, port) }; if host.is_empty() && !allow_empty_host { - bail!("common::socks::parse_host_port: invalid {name}: empty host"); + bail!("invalid {name}: host must not be empty"); } let port = raw_port .parse::() .ok() .filter(|port| *port != 0) - .ok_or_else(|| anyhow!("common::socks::parse_host_port: invalid {name}: invalid port"))?; + .ok_or_else(|| anyhow!("invalid {name}: port must be in 1..=65535"))?; Ok((host.to_string(), port)) } @@ -157,7 +157,7 @@ pub(crate) fn format_host_port(host: &str, port: u16) -> String { fn validate_credential(name: &str, value: &str) -> Result<()> { if !(1..=u8::MAX as usize).contains(&value.len()) { - bail!("common::socks::validate_credential: {name} length must be 1..255 bytes"); + bail!("socks {name} length must be in 1..=255 bytes"); } Ok(()) } @@ -167,9 +167,7 @@ fn validate_raw_credential(value: &str) -> Result<()> { .bytes() .any(|byte| b":/?#[]@!$&'()*+,;=".contains(&byte)) { - bail!( - "common::socks::validate_raw_credential: reserved credentials must be percent-encoded" - ); + bail!("reserved characters in socks credentials must be percent-encoded"); } Ok(()) } @@ -178,7 +176,7 @@ fn decode_component(raw: &str, name: &str) -> Result { validate_percent_encoding(raw, name)?; percent_decode_str(raw) .decode_utf8() - .with_context(|| format!("common::socks::decode_component: invalid UTF-8 in {name}")) + .with_context(|| format!("invalid UTF-8 in {name}")) .map(|value| value.into_owned()) } @@ -191,9 +189,7 @@ fn validate_percent_encoding(raw: &str, name: &str) -> Result<()> { || !bytes[index + 1].is_ascii_hexdigit() || !bytes[index + 2].is_ascii_hexdigit() { - bail!( - "common::socks::validate_percent_encoding: invalid percent encoding in {name}" - ); + bail!("invalid percent encoding in {name}"); } index += 3; } else { diff --git a/src/main.rs b/src/main.rs index 7d3a911..ac8d055 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use std::env; use std::io::IsTerminal; use anyhow::{Context, Result, bail}; -use nowhere::common::{LogLevel, Logger, query_first}; +use nowhere::common::{LogLevel, Logger, query_first, validate_endpoint_url_input}; use nowhere::portal::Portal; use nowhere::vector::Vector; use url::{ParseError, Url}; @@ -30,13 +30,16 @@ Commands: Portal URL: portal://@:[?] + portal://@/:[/:] Vector URL: vector://@:?socks=[&] + vector://@/:[/:]?socks=... Examples: nowhere 'portal://secret@:2077' - nowhere 'portal://secret@0.0.0.0:2077?log=info&net=tcp' + nowhere 'portal://secret@*/tcp4:2077?log=info' + nowhere 'portal://secret@*/tcp:2077/udp:3088' nowhere 'portal://secret@:2077?tls=2&crt=/etc/nowhere/cert.pem&key=/etc/nowhere/key.pem' nowhere 'portal://secret@:2077?socks=user:pass@127.0.0.1:1080' nowhere 'portal://relay-key@:2077?next=upstream-key@origin.example:2077' @@ -51,13 +54,13 @@ Required URL parts: Password credentials are not supported. Listen host: - empty Bind IPv4 and IPv6 wildcard sockets. + * Bind IPv4 and IPv6 wildcard sockets as allowed by carrier. + empty Compact form only; equivalent to *. 0.0.0.0 Bind IPv4 wildcard only. [::] Bind IPv6 wildcard only. - IP or hostname Bind the resolved listen address. + IP or hostname Bind all matching resolved listen addresses. Portal parameters: - net=mix|tcp|udp Listener mode. Default: mix. tls=1|2 TLS mode. 1 for RAM certificate; 2 for PEM files. Default: 1. tls=0 is not supported. crt= PEM certificate chain for tls=2. @@ -67,12 +70,13 @@ Portal parameters: dial= Local source IP for outbound target connections. Default: auto. socks= SOCKS5 outbound proxy: host:port or user:pass@host:port. Omit or use none to disable. - next= Native upstream Portal: shared-key@host:port. Omit or use + next= Native upstream Portal using the same endpoint grammar. + Example: shared-key@host/tcp:2077/udp6:3088. Omit or use none to disable. Mutually exclusive with socks. up=tcp|udp|mix Native upstream upload carrier. Mix chooses per flow. - Default: udp. + Defaults to the only declared carrier, or UDP. down=tcp|udp|mix Native upstream download carrier. Mix chooses per flow. - Default: udp. + Defaults to the only declared carrier, or UDP. mux=0|1 Use TLS Mux when the native route can select TCP. Default: 0. sni= Native upstream certificate DNS name. Default: none. pin= Native upstream certificate fingerprint. Default: none. @@ -80,8 +84,8 @@ Portal parameters: log= none, debug, info, warn, error, event. Default: info. Vector parameters: - up=tcp|udp|mix Upload carrier. Mix chooses per flow. Default: udp. - down=tcp|udp|mix Download carrier. Mix chooses per flow. Default: udp. + up=tcp|udp|mix Upload carrier. Defaults to the only declared carrier, or UDP. + down=tcp|udp|mix Download carrier. Defaults to the only declared carrier, or UDP. mux=0|1 Use TLS Mux when either direction can select TCP. Default: 0. sni= Verify the certificate for a DNS name. Empty, omitted, or none disables certificate validation. Default: none. @@ -96,6 +100,13 @@ Vector parameters: Query handling: Unknown parameters are ignored. If a parameter appears more than once, only its first value is used. Missing optional parameters use their defaults. + The net parameter is ignored; carrier paths select listeners. + +Carrier endpoint grammar: + tcp, udp Do not restrict the address family. + tcp4, udp4 Use IPv4 only. + tcp6, udp6 Use IPv6 only. + host:port Shorthand for TCP and UDP on the same port. Transport capabilities: TLS/TCP TCP relay and UDP-over-TCP (UoT). @@ -137,22 +148,21 @@ Environment: #[tokio::main] async fn main() { if let Err(err) = start(env::args().collect()).await { - eprintln!( - "nowhere-{VERSION} {}/{} pid={} error={err:#}", - env::consts::OS, - env::consts::ARCH, - std::process::id(), - ); + eprintln!("{}", format_start_error(&err)); std::process::exit(1); } } +fn format_start_error(error: &anyhow::Error) -> String { + format!("error: {error:#}") +} + async fn start(args: Vec) -> Result<()> { if args.len() == 1 { return run_tui().await; } if args.len() > 2 { - bail!("main::start: expected exactly one configuration URL"); + bail!("expected exactly one configuration URL; run 'nowhere --help' for usage"); } match args[1].as_str() { @@ -172,17 +182,16 @@ async fn start(args: Vec) -> Result<()> { _ => {} } - let command_url = - parse_command_url(&args[1]).with_context(|| "main::start: failed to parse command URL")?; + let command_url = parse_command_url(&args[1]).with_context(|| "invalid configuration URL")?; let scheme = command_url.url.scheme().to_string(); if !matches!(scheme.as_str(), "portal" | "vector") { - bail!("main::start: unknown URL scheme: {scheme}"); + bail!("invalid configuration URL: scheme must be portal or vector, found {scheme:?}"); } // Startup only needs `log` here. Each role parses its own configuration, // including Portal's intentionally ignored upstream options when `next` // is disabled. let query = query_first(&command_url.url, &["log"]) - .with_context(|| "main::start: invalid URL query")?; + .with_context(|| "invalid configuration URL query")?; let logger = init_logger(query.get("log").map(String::as_str))?; match scheme.as_str() { @@ -191,16 +200,14 @@ async fn start(args: Vec) -> Result<()> { command_url.url, command_url.listen_host.as_deref(), logger, - ) - .with_context(|| "main::start: failed to create portal")?; + )?; portal.run().await } "vector" => { - let vector = Vector::new(command_url.url, logger) - .with_context(|| "main::start: failed to create vector")?; + let vector = Vector::new(command_url.url, logger)?; vector.run().await } - _ => bail!("main::start: unknown URL scheme: {}", scheme), + _ => unreachable!("scheme was validated above"), } } @@ -225,6 +232,7 @@ struct CommandUrl { } fn parse_command_url(raw: &str) -> Result { + validate_endpoint_url_input(raw, "endpoint")?; match Url::parse(raw) { Ok(url) => Ok(CommandUrl { url, @@ -285,7 +293,9 @@ fn init_logger(level: Option<&str>) -> Result { logger.set_log_level(LogLevel::Event); logger.event(format_args!("main::init_logger: log level set to EVENT")); } - Some(value) => bail!("main::init_logger: invalid log level: {value}"), + Some(value) => { + bail!("log must be none, debug, info, warn, error, or event; found {value:?}") + } } Ok(logger) } diff --git a/src/protocol/auth.rs b/src/protocol/auth.rs index d62bf0d..62dd5ca 100644 --- a/src/protocol/auth.rs +++ b/src/protocol/auth.rs @@ -51,7 +51,7 @@ impl Credentials { /// Parses the shared key from the URL username and derives its auth key. pub fn new(parsed_url: &Url) -> Result { if parsed_url.password().is_some() { - bail!("protocol::auth::Credentials::new: password credentials are not supported"); + bail!("password credentials are not supported; put the shared key before '@'"); } let shared_key = decode_url_username(parsed_url)?; Self::from_shared_key(&shared_key) @@ -60,10 +60,10 @@ impl Credentials { /// Derives credentials directly from non-empty shared-key bytes. pub fn from_shared_key(shared_key: &[u8]) -> Result { if shared_key.is_empty() { - bail!("protocol::auth::Credentials::from_shared_key: missing shared key"); + bail!("missing shared key before '@'"); } if shared_key.len() > u8::MAX as usize { - bail!("protocol::auth::Credentials::from_shared_key: shared key exceeds 255 bytes"); + bail!("shared key exceeds the 255-byte limit"); } Ok(Self { auth_key: derive_auth_key(shared_key), diff --git a/src/protocol/util.rs b/src/protocol/util.rs index 8daa8fd..0f3c38f 100644 --- a/src/protocol/util.rs +++ b/src/protocol/util.rs @@ -20,7 +20,7 @@ pub(super) fn decode_url_username(parsed_url: &Url) -> Result> { || !bytes[index + 1].is_ascii_hexdigit() || !bytes[index + 2].is_ascii_hexdigit() { - bail!("protocol::auth::Credentials::new: malformed percent escape"); + bail!("malformed percent escape in shared key"); } index += 3; } else { @@ -29,7 +29,7 @@ pub(super) fn decode_url_username(parsed_url: &Url) -> Result> { } let decoded = percent_decode_str(username) .decode_utf8() - .context("protocol::auth::Credentials::new: shared key is not valid UTF-8")?; + .context("shared key is not valid UTF-8")?; Ok(decoded.as_bytes().to_vec()) } diff --git a/src/tests/main.rs b/src/tests/main.rs index 9efe048..2b6e840 100644 --- a/src/tests/main.rs +++ b/src/tests/main.rs @@ -14,9 +14,11 @@ fn help_text_documents_usage_and_configuration_surface() { "-h, --help", "-v, --version", "portal://@:", + ":", "vector://@:", "tls=1|2", - "net=mix|tcp|udp", + "tcp4, udp4", + "tcp6, udp6", "socks=", "next=", "sni=", @@ -106,6 +108,11 @@ fn parse_command_url_accepts_empty_listen_host_without_userinfo() { assert_eq!(parsed.listen_host.as_deref(), Some("")); } +#[test] +fn parse_command_url_rejects_empty_host_for_explicit_carriers() { + assert!(parse_command_url("portal://secret@/tcp:2077").is_err()); +} + #[test] fn parse_command_url_keeps_normal_hosts() { let parsed = parse_command_url("portal://secret@[::]:2077?dial=auto").unwrap(); @@ -114,3 +121,116 @@ fn parse_command_url_keeps_normal_hosts() { assert_eq!(parsed.url.port(), Some(2077)); assert_eq!(parsed.listen_host, None); } + +#[tokio::test] +async fn invalid_configuration_urls_fail_before_service_startup_with_safe_errors() { + const SECRET: &str = "do-not-print-this-secret"; + for (raw, expected) in [ + ("not-a-url".to_owned(), "invalid configuration URL"), + ( + format!("ftp://{SECRET}@example.com:2077"), + "scheme must be portal or vector", + ), + ("portal://@*:2077?log=none".to_owned(), "missing shared key"), + ( + format!("portal://{SECRET}@*:abc?log=none"), + "invalid port number", + ), + ( + format!("portal://{SECRET}@*:0?log=none"), + "compact endpoint requires a port in 1..=65535", + ), + ( + format!("portal://{SECRET}@/tcp:2077?log=none"), + "empty host", + ), + ( + format!("portal://{SECRET}@*/tcp?log=none"), + "must use CARRIER:PORT", + ), + ( + format!("portal://{SECRET}@*/tcp:2077/../udp:3088?log=none"), + "must not contain '.' or '..' segments", + ), + ( + format!("portal://{SECRET}@*:2077/tcp:3088?log=none"), + "choose either HOST:PORT", + ), + ( + format!("portal://{SECRET}@*/udp:2077/udp6:3088?log=none"), + "UDP carrier is declared more than once", + ), + ( + format!("portal://{SECRET}@*/tcp:65536?log=none"), + "carrier port must be in 1..=65535", + ), + ( + format!("portal://{SECRET}@192.0.2.1/tcp6:2077?log=none"), + "address family does not match", + ), + ( + format!("portal://{SECRET}@*:2077?log=verbose"), + "log must be none, debug, info, warn, error, or event", + ), + ( + format!("portal://{SECRET}@*:2077?rate=-1&log=none"), + "rate must be a non-negative integer", + ), + ( + format!("portal://{SECRET}@*:2077?socks=bad&log=none"), + "invalid socks endpoint: expected HOST:PORT", + ), + ( + format!("portal://{SECRET}@unresolvable.invalid:2077?rate=-1&log=none"), + "rate must be a non-negative integer", + ), + ( + format!("vector://{SECRET}@*/tcp:2077?socks=:1080&log=none"), + "wildcard host is only valid for Portal listeners", + ), + ( + "vector://@example.com:2077?socks=:1080&log=none".to_owned(), + "missing shared key", + ), + ( + format!("vector://{SECRET}@example.com?socks=:1080&log=none"), + "compact endpoint requires a port in 1..=65535", + ), + ( + format!("vector://{SECRET}@example.com:2077?log=none"), + "socks parameter is required", + ), + ( + format!("vector://{SECRET}@example.com/udp:2077?up=tcp&socks=:1080&log=none"), + "up selects a carrier not declared by the endpoint", + ), + ( + format!( + "portal://outer@*:2077?next={SECRET}@origin.example/tcp:2077/../udp:3088&log=none" + ), + "must not contain '.' or '..' segments", + ), + ( + format!("portal://outer@*:2077?next={SECRET}@*/tcp:2077&log=none"), + "wildcard host is only valid for Portal listeners", + ), + ( + format!("portal://outer@*:2077?next={SECRET}@origin.example/tcp:2077?inner=1&log=none"), + "expected shared-key and one endpoint", + ), + ] { + let args = vec!["nowhere".to_owned(), raw]; + let error = tokio::time::timeout(std::time::Duration::from_secs(1), start(args)) + .await + .expect("invalid configuration attempted to run a service") + .unwrap_err(); + let message = format_start_error(&error); + assert!(message.starts_with("error: ")); + assert!(message.contains(expected), "response was {message:?}"); + assert!( + !message.contains("::"), + "response exposed internal names: {message:?}" + ); + assert!(!message.contains(SECRET), "response leaked the shared key"); + } +} diff --git a/src/vector/mod.rs b/src/vector/mod.rs index 02a4b62..7713d1f 100644 --- a/src/vector/mod.rs +++ b/src/vector/mod.rs @@ -203,12 +203,10 @@ impl Vector { } fn build(parsed_url: Url, logger: Logger, lifecycle: Arc) -> Result { - let config = VectorConfig::from_url(&parsed_url) - .context("vector::Vector::new: invalid Vector configuration")?; + let config = VectorConfig::from_url(&parsed_url)?; let telemetry_interval = telemetry_interval().context("vector::Vector::new: invalid NOW_TELEMETRY_INTERVAL")?; - let credentials = - Credentials::new(&parsed_url).context("vector::Vector::new: invalid shared key")?; + let credentials = Credentials::new(&parsed_url)?; let telemetry_summary = format!( "portal={} up={} down={} mux={} socks={}", config.portal_endpoint(), From b702f5c09d5876fb356df0be91138f7533ce074b Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 07:02:35 +0800 Subject: [PATCH 12/62] Document service endpoint configuration --- Cargo.toml | 2 +- README.md | 22 +++++++++--------- docs/compatibility.md | 22 +++++++++++++++++- docs/configuration.md | 53 +++++++++++++++++++++++++++++++------------ docs/integrations.md | 3 ++- docs/platforms.md | 16 ++++++------- docs/quick-start.md | 19 ++++++++-------- src/main.rs | 22 +++++++++--------- 8 files changed, 103 insertions(+), 56 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6e5466a..af119fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "nowhere" version = "2.0.0-dev" edition = "2024" -description = "One-port, two-transport encrypted relay with independently split directions" +description = "Two-transport encrypted relay with independently split directions" license = "GPL-3.0-only" repository = "https://github.com/NodePassProject/Nowhere" readme = "README.md" diff --git a/README.md b/README.md index a35caa9..0041200 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

- One port. Two transports. Split directions. + Two-transport encrypted relay with independently split directions

@@ -26,7 +26,7 @@ downlink independently instead of forcing both directions onto one transport. | Core property | What it means | | --- | --- | -| One service edge | TLS/TCP and QUIC/UDP share one address, port number, credential, and lifecycle | +| One service edge | TLS/TCP and QUIC/UDP share one host, credential, and lifecycle, with independent ports and address families | | Split directions | Uplink and downlink independently select TLS/TCP or QUIC/UDP | | Complete ingress | SOCKS5 CONNECT carries TCP; UDP ASSOCIATE carries UDP | | Native chaining | A Portal can forward directly to another Portal without a loopback SOCKS5 conversion | @@ -68,9 +68,9 @@ nowhere tui +------------+ +------------+ ``` -Portal defaults to `net=mix`, accepting both carrier families on the same port -number. `net=tcp` and `net=udp` intentionally restrict the listener when an -operator wants only one carrier family. +The compact Portal endpoint accepts TLS/TCP and QUIC/UDP on the same port. +Carrier paths select one transport, separate their ports, or restrict address +families, for example `@*/tcp4:2006/udp6:2017`. ### One flow, two transport decisions @@ -104,7 +104,7 @@ Nowhere flow directly with the same transport engine used by Vector: ```bash nowhere \ - 'portal://relay-key@:2077?next=origin-key@origin.example:2077&up=udp&down=udp' + 'portal://relay-key@:2000?next=origin-key@origin.example:2000&up=udp&down=udp' ``` `next` is lazy and mutually exclusive with outbound `socks`. Portal forwarding @@ -122,10 +122,10 @@ cargo build --release --locked ### 2. Start Portal -The default `net=mix` mode accepts TLS/TCP and QUIC/UDP on port `2077`: +The compact endpoint accepts TLS/TCP and QUIC/UDP on port `2000`: ```bash -./target/release/nowhere 'portal://change-me@127.0.0.1:2077' +./target/release/nowhere 'portal://change-me@127.0.0.1:2000' ``` ### 3. Start Vector @@ -134,7 +134,7 @@ This Vector exposes SOCKS5 on `127.0.0.1:1080`: ```bash ./target/release/nowhere \ - 'vector://change-me@127.0.0.1:2077?up=tcp&down=tcp&socks=127.0.0.1:1080' + 'vector://change-me@127.0.0.1:2000?up=tcp&down=tcp&socks=127.0.0.1:1080' ``` Mux, split-carrier, certificate, and chaining examples are in the @@ -155,8 +155,8 @@ The local examples omit `sni`, which disables certificate verification. A public Portal should use a CA-trusted certificate with strict verification: ```bash -nowhere 'portal://change-me@:2077?tls=2&crt=/etc/nowhere/cert.pem&key=/etc/nowhere/key.pem' -nowhere 'vector://change-me@relay.example:2077?sni=relay.example&socks=127.0.0.1:1080' +nowhere 'portal://change-me@:2000?tls=2&crt=/etc/nowhere/cert.pem&key=/etc/nowhere/key.pem' +nowhere 'vector://change-me@relay.example:2000?sni=relay.example&socks=127.0.0.1:1080' ``` Certificate pinning is also available. Review the diff --git a/docs/compatibility.md b/docs/compatibility.md index f3462a3..c207e6b 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -5,7 +5,7 @@ Nowhere 2 uses ALPN as its data-plane version selector. Portal and Vector offer the fixed protocols `nw2` and `now/1`, in that order. A negotiated `nw2` carrier is V2; a negotiated `now/1` carrier is compatible V1. The `alpn` URL parameter -has been removed and is ignored as an unknown parameter. +is ignored as an unknown parameter. | Client | Portal | Negotiated version | |---|---|---| @@ -17,6 +17,26 @@ V1 installations using a custom ALPN cannot interoperate with V2. A V1 client that had already customized its ALPN to `nw2` is classified as V2; this rare collision has no compatibility exception. +## Endpoint configuration + +The compact `portal://key@host:port`, `vector://key@host:port`, and +`next=key@host:port` forms declare TCP and UDP on the same port. The empty +Portal host in `portal://key@:port` is an alias for the wildcard host `*`. + +Carrier paths select the enabled carriers. The `net` query follows the +unknown-parameter rule and has no effect on carrier selection: + +```text +portal://key@*/tcp:2006 +portal://key@*/udp:2017 +``` + +Use `/tcp4:PORT`, `/tcp6:PORT`, `/udp4:PORT`, and `/udp6:PORT` when the address +family must also be fixed. URL compatibility is separate from wire +compatibility: default V1 and V2 peers communicate using the version negotiation +described above. Compatible compact address syntax does not imply identical +query semantics across versions. + ## TLS lane contract Vector `mux=0` opens one authenticated TLS connection per Flow. Vector `mux=1` diff --git a/docs/configuration.md b/docs/configuration.md index 1ab4860..74e10e9 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -7,19 +7,34 @@ see [Platforms](platforms.md). ## Portal URL ```text -portal://shared-key@host:port?net=mix&tls=1&log=info +portal://shared-key@host:port?tls=1&log=info +portal://shared-key@host/tcp4:2006/udp6:2017?tls=1&log=info ``` +The compact `host:port` form enables TLS/TCP and QUIC/UDP on the same port. +The explicit path enables only the listed carriers. `tcp` and `udp` accept +either address family; suffix `4` or `6` to restrict that carrier. Each carrier +may appear at most once. + +Both carriers share the host; their ports and address families are independent. +The wildcard `*` expands to separate IPv4 and IPv6 sockets, with IPv6 sockets +set to `V6ONLY`. Hostnames resolve at startup to all matching, deduplicated +addresses; listeners do not refresh DNS while running. + +An unrestricted wildcard listener can omit an unavailable address family with +a warning. Each declared carrier must bind at least one address. Explicit +address families, concrete addresses, occupied ports, and permission failures +cause startup to fail and release the listeners already opened. + | Query | Values | Default | |---|---|---| -| `net` | `mix`, `tcp`, `udp` | `mix` | | `tls` | `1` generated certificate, `2` supplied certificate | `1` | | `crt`, `key` | PEM paths, required with `tls=2` | — | | `rate`, `etar` | Mbps, `0` disables limit | `0` | | `dial` | `auto` or local IP | `auto` | | `socks` | outbound SOCKS5 configuration | disabled | -| `next` | `shared-key@host:port` | disabled | -| `up`, `down` | native next-hop policy: `tcp`, `udp`, or `mix` | `udp` | +| `next` | `shared-key@host:port` or explicit carrier endpoint | disabled | +| `up`, `down` | native next-hop policy: `tcp`, `udp`, or `mix` | only carrier, otherwise `udp` | | `mux` | native next-hop TLS: `0` dedicated lanes, `1` Mux when TCP is possible | `0` | | `sni` | native next-hop verified DNS name, or `none` | `none` | | `pin` | native next-hop certificate SHA-256 pin, or `none` | `none` | @@ -34,11 +49,12 @@ Portal. These upstream options are ignored when `next` is absent or `none`. ```text vector://shared-key@host:port?up=tcp&down=tcp&socks=127.0.0.1:1080 +vector://shared-key@host/tcp:2006/udp:2017?up=tcp&down=udp&socks=127.0.0.1:1080 ``` | Query | Values | Default | |---|---|---| -| `up`, `down` | `tcp`, `udp`, or `mix` | `udp` | +| `up`, `down` | `tcp`, `udp`, or `mix` | only carrier, otherwise `udp` | | `mux` | `0` dedicated TLS lanes, `1` TLS Mux | `0` | | `sni` | verified DNS name, or `none` | `none` | | `pin` | certificate SHA-256 pin, or `none` | `none` | @@ -51,7 +67,7 @@ vector://shared-key@host:port?up=tcp&down=tcp&socks=127.0.0.1:1080 ```text Portal URL | - +-- listener: net, tls, crt, key + +-- listener: endpoint path, tls, crt, key +-- relay: rate, etar, dial, log | +-- outbound path @@ -92,9 +108,9 @@ The primary route must acquire all lanes within `NOW_MIX_FALLBACK_TIMEOUT` (default `1s`). Failure or timeout discards its local resources and starts the other allowed route once with a new flow ID. READY failures, target dial failures, and established payload failures do not trigger fallback. The policy -has no health score or circuit breaker. `net=mix` is the recommended upstream; -a single-family listener may consume the budget on each affected flow or leave -no legal route for a fixed direction. +has no health score or circuit breaker. Both carriers must be declared for +`mix`; a single-carrier endpoint rejects a policy that selects the absent +carrier. With `mux=1`, Shards open lazily according to active flow pressure. New flows use the least-loaded shard; a shard carries 4 active flows before another @@ -104,7 +120,7 @@ least one direction is `tcp` or `mix`. `udp/udp&mux=1` canonicalizes to `mux=0`. Portal and Vector offer fixed ALPNs in the order `nw2`, `now/1`. V2 peers select -`nw2`; a V2 peer talking to a default V1 peer selects `now/1`. The removed +`nw2`; a V2 peer talking to a default V1 peer selects `now/1`. The `alpn` query is ignored under the normal unknown-parameter rule. Protocol version and Mux are independent settings. Portal's `mux` option controls only its `next` client. Inbound Portal connections accept a `0xff`-marked Mux carrier @@ -116,14 +132,23 @@ present. ## URL parsing rules -- The shared key occupies the URL username. Password userinfo, URL paths, and - fragments are invalid. +- The shared key occupies the URL username. Password userinfo and fragments are + invalid. +- Endpoints use either `HOST:PORT` or + `HOST/CARRIER:PORT[/CARRIER:PORT]`; the forms cannot be combined. Empty path + segments, trailing slashes, unknown or duplicate carriers, and zero ports are + invalid. +- Portal allows `*` as the wildcard listen host. The compact + `portal://key@:port` form is equivalent to `*`; explicit + carrier paths require a host. Vector and `next` reject `*`. +- IP literals must agree with an explicit `4` or `6` carrier suffix. Hostnames + are filtered to the selected address family. - Reserved bytes in shared keys, nested credentials, and query values use percent encoding. - Recognized query keys use their first occurrence. Later duplicates and unknown keys are ignored. -- A Portal with an empty listen host binds wildcard addresses. Vector requires - a Portal host and a `socks` listener. +- The `net` query is an unknown parameter and has no effect. `/tcp:PORT` and + `/udp:PORT` select a single carrier; compact endpoints enable both carriers. - `socks=user:pass@host:port` enables RFC 1929 authentication. Omitting the credentials enables SOCKS5 no-auth. diff --git a/docs/integrations.md b/docs/integrations.md index 21f3a53..7a4c505 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -51,7 +51,8 @@ replays a request after any FlowHeader or Target bytes may have been accepted. ## Chained Portal -`next=shared-key@host:port` creates the same transport-only client engine used +`next=shared-key@host:port` or an explicit endpoint such as +`next=shared-key@host/tcp:2006/udp:2017` creates the same client engine used by Vector, including `up/down=mix` and pre-commit fallback. `mux=0|1` selects dedicated or Mux TLS when TCP can be selected and defaults to `0`; it has no effect without `next` and canonicalizes to `0` for `udp/udp`. Authentication, diff --git a/docs/platforms.md b/docs/platforms.md index 3f9bd0d..8613ac6 100644 --- a/docs/platforms.md +++ b/docs/platforms.md @@ -36,22 +36,22 @@ Start a Portal with its generated certificate: ```text docker run -d --rm --name nowhere-portal \ - -p 2077:2077/tcp \ - -p 2077:2077/udp \ + -p 2000:2000/tcp \ + -p 2000:2000/udp \ ghcr.io/nodepassproject/nowhere:latest \ - 'portal://change-me@:2077' + 'portal://change-me@:2000' ``` For `tls=2`, mount the CA-issued PEM certificate chain and private key: ```text docker run -d --rm --name nowhere-portal \ - -p 2077:2077/tcp \ - -p 2077:2077/udp \ + -p 2000:2000/tcp \ + -p 2000:2000/udp \ -v /path/fullchain.pem:/cert.pem:ro \ -v /path/private-key.pem:/key.pem:ro \ ghcr.io/nodepassproject/nowhere:latest \ - 'portal://change-me@:2077?tls=2&crt=/cert.pem&key=/key.pem' + 'portal://change-me@:2000?tls=2&crt=/cert.pem&key=/key.pem' ``` `crt` is the full certificate chain and `key` is its private key. A Vector @@ -76,13 +76,13 @@ Bourne-compatible shells and PowerShell accept the documented single-quoted URLs: ```text -nowhere 'vector://secret@portal.example:2077?up=tcp&down=udp&socks=127.0.0.1:1080' +nowhere 'vector://secret@portal.example:2000?up=tcp&down=udp&socks=127.0.0.1:1080' ``` In Windows Command Prompt, use double quotes and the `.exe` name: ```text -nowhere.exe "vector://secret@portal.example:2077?up=tcp&down=udp&socks=127.0.0.1:1080" +nowhere.exe "vector://secret@portal.example:2000?up=tcp&down=udp&socks=127.0.0.1:1080" ``` Certificate and key values accept native filesystem paths. Relative paths are diff --git a/docs/quick-start.md b/docs/quick-start.md index 9acad16..a571ace 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -24,24 +24,25 @@ observer and does not start, stop, or reconfigure either process. ## 1. Start Portal ```text -nowhere 'portal://secret@:2077?log=info' +nowhere 'portal://secret@:2000?log=info' ``` -Portal listens for TLS/TCP and QUIC on the same numeric port when `net=mix` -(the default). +The compact endpoint listens for TLS/TCP and QUIC on the same numeric port. +Use `portal://secret@*/tcp4:2006` for a TCP-only IPv4 listener, or +`portal://secret@*/tcp:2006/udp:2017` to use separate ports. ## 2. Start Vector Dedicated TLS lanes in both directions: ```text -nowhere 'vector://secret@127.0.0.1:2077?up=tcp&down=tcp&socks=127.0.0.1:1080' +nowhere 'vector://secret@127.0.0.1:2000?up=tcp&down=tcp&socks=127.0.0.1:1080' ``` QUIC in both directions: ```text -nowhere 'vector://secret@127.0.0.1:2077?up=udp&down=udp&socks=127.0.0.1:1080' +nowhere 'vector://secret@127.0.0.1:2000?up=udp&down=udp&socks=127.0.0.1:1080' ``` The full route-policy matrix is: @@ -58,19 +59,19 @@ route per flow and can use the other once if primary preparation fails. Stateless per-flow selection across full-duplex TLS and QUIC uses: ```text -nowhere 'vector://secret@127.0.0.1:2077?up=mix&down=mix&socks=127.0.0.1:1080' +nowhere 'vector://secret@127.0.0.1:2000?up=mix&down=mix&socks=127.0.0.1:1080' ``` `mix/mix` chooses `tcp/tcp` or `udp/udp` once per flow. A single mixed -direction can resolve to a split carrier pair. `net=mix` makes every matrix -cell reachable. The primary choice has a `NOW_MIX_FALLBACK_TIMEOUT` budget +direction can resolve to a split carrier pair. Declaring both carriers makes +every matrix cell reachable. The primary choice has a `NOW_MIX_FALLBACK_TIMEOUT` budget (default `1s`). TLS Mux is enabled on Vector. Portal recognizes the marked carrier automatically: ```text -nowhere 'vector://secret@127.0.0.1:2077?up=tcp&down=tcp&mux=1&socks=127.0.0.1:1080' +nowhere 'vector://secret@127.0.0.1:2000?up=tcp&down=tcp&mux=1&socks=127.0.0.1:1080' ``` Nowhere 2 negotiates `nw2` with another V2 peer and falls back to `now/1` when diff --git a/src/main.rs b/src/main.rs index ac8d055..66fa117 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,16 +37,16 @@ Vector URL: vector://@/:[/:]?socks=... Examples: - nowhere 'portal://secret@:2077' - nowhere 'portal://secret@*/tcp4:2077?log=info' - nowhere 'portal://secret@*/tcp:2077/udp:3088' - nowhere 'portal://secret@:2077?tls=2&crt=/etc/nowhere/cert.pem&key=/etc/nowhere/key.pem' - nowhere 'portal://secret@:2077?socks=user:pass@127.0.0.1:1080' - nowhere 'portal://relay-key@:2077?next=upstream-key@origin.example:2077' - nowhere 'portal://relay-key@:2077?next=upstream-key@origin.example:2077&up=tcp&down=tcp' - nowhere 'portal://secret@:2077?rate=100&etar=200' - nowhere 'vector://secret@relay.example:2077?sni=relay.example&socks=127.0.0.1:1080' - nowhere 'vector://secret@127.0.0.1:2077?up=tcp&down=tcp&socks=:1080' + nowhere 'portal://secret@:2000' + nowhere 'portal://secret@*/tcp4:2006?log=info' + nowhere 'portal://secret@*/tcp:2006/udp:2017' + nowhere 'portal://secret@:2000?tls=2&crt=/etc/nowhere/cert.pem&key=/etc/nowhere/key.pem' + nowhere 'portal://secret@:2000?socks=user:pass@127.0.0.1:1080' + nowhere 'portal://relay-key@:2000?next=upstream-key@origin.example:2000' + nowhere 'portal://relay-key@:2000?next=upstream-key@origin.example:2000&up=tcp&down=tcp' + nowhere 'portal://secret@:2000?rate=100&etar=200' + nowhere 'vector://secret@relay.example:2000?sni=relay.example&socks=127.0.0.1:1080' + nowhere 'vector://secret@127.0.0.1:2000?up=tcp&down=tcp&socks=:1080' Required URL parts: shared-key Non-empty URL username. Percent-encode reserved characters. @@ -71,7 +71,7 @@ Portal parameters: socks= SOCKS5 outbound proxy: host:port or user:pass@host:port. Omit or use none to disable. next= Native upstream Portal using the same endpoint grammar. - Example: shared-key@host/tcp:2077/udp6:3088. Omit or use + Example: shared-key@host/tcp:2006/udp6:2017. Omit or use none to disable. Mutually exclusive with socks. up=tcp|udp|mix Native upstream upload carrier. Mix chooses per flow. Defaults to the only declared carrier, or UDP. From e62a859a37347e5b69af8db1fbb5275ced44114c Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 07:14:58 +0800 Subject: [PATCH 13/62] Align endpoint fixtures with protocol ports --- src/tests/common/config.rs | 14 ++-- src/tests/common/endpoint.rs | 64 +++++++++--------- src/tests/common/socks.rs | 48 ++++++------- src/tests/common/tls.rs | 4 +- src/tests/main.rs | 54 +++++++-------- src/tests/portal.rs | 100 ++++++++++++++-------------- src/tests/portal/conn/relay.rs | 10 +-- src/tests/portal/conn/tcp.rs | 20 +++--- src/tests/portal/pairing.rs | 2 +- src/tests/portal/pairing/version.rs | 2 +- src/tests/portal/runtime.rs | 4 +- src/tests/telemetry/hub.rs | 6 +- src/tests/telemetry/ipc.rs | 2 +- src/tests/tui/client.rs | 2 +- src/tests/tui/render.rs | 8 +-- src/tests/vector.rs | 6 +- src/tests/vector/config.rs | 96 +++++++++++++------------- src/tests/vector/flow.rs | 2 +- src/tests/vector/tls.rs | 8 +-- 19 files changed, 228 insertions(+), 224 deletions(-) diff --git a/src/tests/common/config.rs b/src/tests/common/config.rs index a261f99..b457490 100644 --- a/src/tests/common/config.rs +++ b/src/tests/common/config.rs @@ -8,36 +8,36 @@ use url::Url; #[test] fn query_first_ignores_unknown_parameters_and_keeps_first_duplicate() { - let parsed = Url::parse("portal://key@localhost:2077?log=debug&label=now%2F1").unwrap(); + let parsed = Url::parse("portal://key@localhost:2000?log=debug&label=now%2F1").unwrap(); let values = query_first(&parsed, &["log", "label"]).unwrap(); assert_eq!(values["log"], "debug"); assert_eq!(values["label"], "now/1"); - let duplicate = Url::parse("portal://key@localhost:2077?log=debug&log=event").unwrap(); + let duplicate = Url::parse("portal://key@localhost:2000?log=debug&log=event").unwrap(); assert_eq!(query_first(&duplicate, &["log"]).unwrap()["log"], "debug"); - let unknown = Url::parse("portal://key@localhost:2077?typo=value&%FF=value").unwrap(); + let unknown = Url::parse("portal://key@localhost:2000?typo=value&%FF=value").unwrap(); assert!(query_first(&unknown, &["log"]).unwrap().is_empty()); } #[test] fn query_first_preserves_literal_slash_and_plus_and_validates_the_selected_value() { - let protocol = Url::parse("portal://key@localhost:2077?label=private/2").unwrap(); + let protocol = Url::parse("portal://key@localhost:2000?label=private/2").unwrap(); assert_eq!( query_first(&protocol, &["label"]).unwrap()["label"], "private/2" ); - let parsed = Url::parse("portal://key@localhost:2077?label=now+private").unwrap(); + let parsed = Url::parse("portal://key@localhost:2000?label=now+private").unwrap(); assert_eq!( query_first(&parsed, &["label"]).unwrap()["label"], "now+private" ); - let bad = Url::parse("portal://key@localhost:2077?label=%GG").unwrap(); + let bad = Url::parse("portal://key@localhost:2000?label=%GG").unwrap(); assert!(query_first(&bad, &["label"]).is_err()); let ignored_bad_duplicate = - Url::parse("portal://key@localhost:2077?label=now%2F1&label=%GG").unwrap(); + Url::parse("portal://key@localhost:2000?label=now%2F1&label=%GG").unwrap(); assert_eq!( query_first(&ignored_bad_duplicate, &["label"]).unwrap()["label"], "now/1" diff --git a/src/tests/common/endpoint.rs b/src/tests/common/endpoint.rs index 16108a0..74bd1cd 100644 --- a/src/tests/common/endpoint.rs +++ b/src/tests/common/endpoint.rs @@ -6,13 +6,13 @@ fn parse(raw: &str, wildcard: bool) -> Result { #[test] fn parses_compact_and_explicit_endpoints() { - let compact = parse("portal://key@*:2077", true).unwrap(); - assert_eq!(compact.canonical(), "*:2077"); - assert_eq!(compact.tcp.unwrap().port, 2077); - assert_eq!(compact.udp.unwrap().port, 2077); + let compact = parse("portal://key@*:2000", true).unwrap(); + assert_eq!(compact.canonical(), "*:2000"); + assert_eq!(compact.tcp.unwrap().port, 2000); + assert_eq!(compact.udp.unwrap().port, 2000); - let explicit = parse("portal://key@*/udp6:3088/tcp4:2077", true).unwrap(); - assert_eq!(explicit.canonical(), "*/tcp4:2077/udp6:3088"); + let explicit = parse("portal://key@*/udp6:2017/tcp4:2006", true).unwrap(); + assert_eq!(explicit.canonical(), "*/tcp4:2006/udp6:2017"); assert_eq!(explicit.tcp.unwrap().family, AddressFamily::V4); assert_eq!(explicit.udp.unwrap().family, AddressFamily::V6); } @@ -27,10 +27,11 @@ fn accepts_every_declared_carrier_token() { ("udp4", AddressFamily::V4, false), ("udp6", AddressFamily::V6, false), ] { - let endpoint = parse(&format!("portal://key@*/{carrier}:2077"), true).unwrap(); + let port = if is_tcp { 2006 } else { 2017 }; + let endpoint = parse(&format!("portal://key@*/{carrier}:{port}"), true).unwrap(); let parsed = if is_tcp { endpoint.tcp } else { endpoint.udp }.unwrap(); assert_eq!(parsed.family, family, "carrier={carrier}"); - assert_eq!(parsed.port, 2077, "carrier={carrier}"); + assert_eq!(parsed.port, port, "carrier={carrier}"); } } @@ -46,7 +47,7 @@ fn accepts_port_boundaries_and_canonicalizes_leading_zeroes() { #[test] fn rejects_invalid_carrier_names_and_port_representations() { for carrier in ["", "TCP", "Udp", "quic", "tcp7", "tcp%20"] { - let raw = format!("portal://key@*/{carrier}:2077"); + let raw = format!("portal://key@*/{carrier}:2000"); let error = parse(&raw, true).unwrap_err().to_string(); assert!( error.contains("unknown carrier"), @@ -63,10 +64,13 @@ fn rejects_invalid_carrier_names_and_port_representations() { #[test] fn rejects_every_duplicate_transport_pair() { - for carriers in [["tcp", "tcp4", "tcp6"], ["udp", "udp4", "udp6"]] { + for (carriers, port) in [ + (["tcp", "tcp4", "tcp6"], 2006), + (["udp", "udp4", "udp6"], 2017), + ] { for first in carriers { for second in carriers { - let raw = format!("portal://key@*/{first}:2077/{second}:3088"); + let raw = format!("portal://key@*/{first}:{port}/{second}:{port}"); let error = parse(&raw, true).unwrap_err().to_string(); assert!( error.contains("carrier is declared more than once"), @@ -80,67 +84,67 @@ fn rejects_every_duplicate_transport_pair() { #[test] fn formats_ipv6_and_single_carriers() { assert_eq!( - parse("vector://key@[2001:db8::1]/udp:3088", false) + parse("vector://key@[2001:db8::1]/udp:2017", false) .unwrap() .canonical(), - "[2001:db8::1]/udp:3088" + "[2001:db8::1]/udp:2017" ); assert_eq!( - parse("vector://key@example.com/tcp:2077", false) + parse("vector://key@example.com/tcp:2006", false) .unwrap() .canonical(), - "example.com/tcp:2077" + "example.com/tcp:2006" ); } #[test] fn rejects_invalid_grammar_and_family_mismatches() { for (raw, expected) in [ - ("portal://key@*:2077/tcp:2077", "choose either HOST:PORT"), + ("portal://key@*:2000/tcp:2006", "choose either HOST:PORT"), ("portal://key@*/", "must not contain empty segments"), - ("portal://key@*/tcp:2077/", "trailing slash"), - ("portal://key@*/tcp:2077//udp:2077", "empty segments"), + ("portal://key@*/tcp:2006/", "trailing slash"), + ("portal://key@*/tcp:2006//udp:2017", "empty segments"), ("portal://key@*/tcp", "must use CARRIER:PORT"), ("portal://key@*/tcp:1:2", "decimal digits only"), ("portal://key@*/tcp:abc", "decimal digits only"), ("portal://key@*/tcp:-1", "decimal digits only"), - ("portal://key@*/tcp:+2077", "decimal digits only"), + ("portal://key@*/tcp:+2006", "decimal digits only"), ("portal://key@*/tcp:", "decimal digits only"), ("portal://key@*/tcp:0", "1..=65535"), ("portal://key@*/tcp:65536", "1..=65535"), - ("portal://key@*/TCP:2077", "unknown carrier"), - ("portal://key@*/sctp:2077", "unknown carrier"), + ("portal://key@*/TCP:2006", "unknown carrier"), + ("portal://key@*/sctp:2000", "unknown carrier"), ( - "portal://key@*/tcp:2077/tcp6:3088", + "portal://key@*/tcp:2006/tcp6:2006", "TCP carrier is declared more than once", ), ( - "portal://key@*/udp4:2077/udp:3088", + "portal://key@*/udp4:2017/udp:2017", "UDP carrier is declared more than once", ), ( - "portal://key@192.0.2.1/tcp6:2077", + "portal://key@192.0.2.1/tcp6:2006", "address family does not match", ), ( - "portal://key@[2001:db8::1]/udp4:2077", + "portal://key@[2001:db8::1]/udp4:2017", "address family does not match", ), ] { let error = parse(raw, true).unwrap_err().to_string(); assert!(error.contains(expected), "{raw} returned {error:?}"); } - let error = parse("vector://key@*:2077", false).unwrap_err().to_string(); + let error = parse("vector://key@*:2000", false).unwrap_err().to_string(); assert!(error.contains("wildcard host is only valid for Portal listeners")); } #[test] fn raw_input_validation_rejects_dot_segments_before_url_normalization() { for raw in [ - "portal://key@*/tcp:2077/../udp:3088", - "portal://key@*/tcp:2077/./udp:3088", - "portal://key@*/tcp:2077/%2e%2e/udp:3088", - "vector://key@example.com/%2E./tcp:2077?socks=:1080", + "portal://key@*/tcp:2006/../udp:2017", + "portal://key@*/tcp:2006/./udp:2017", + "portal://key@*/tcp:2006/%2e%2e/udp:2017", + "vector://key@example.com/%2E./tcp:2006?socks=:1080", ] { let error = validate_endpoint_url_input(raw, "test") .unwrap_err() diff --git a/src/tests/common/socks.rs b/src/tests/common/socks.rs index 03822df..7e23fa5 100644 --- a/src/tests/common/socks.rs +++ b/src/tests/common/socks.rs @@ -25,19 +25,19 @@ fn parse(raw: &str) -> Result> { #[test] fn parses_disabled_and_endpoint_forms() { for raw in [ - "portal://secret@127.0.0.1:2077", - "portal://secret@127.0.0.1:2077?socks=", - "portal://secret@127.0.0.1:2077?socks=none", + "portal://secret@127.0.0.1:2000", + "portal://secret@127.0.0.1:2000?socks=", + "portal://secret@127.0.0.1:2000?socks=none", ] { assert!(parse(raw).unwrap().is_none()); } - let domain = parse("portal://secret@127.0.0.1:2077?socks=proxy.test:1080") + let domain = parse("portal://secret@127.0.0.1:2000?socks=proxy.test:1080") .unwrap() .unwrap(); assert_eq!(domain.endpoint(), "proxy.test:1080"); - let ipv6 = parse("portal://secret@127.0.0.1:2077?socks=[::1]:1080") + let ipv6 = parse("portal://secret@127.0.0.1:2000?socks=[::1]:1080") .unwrap() .unwrap(); assert_eq!(ipv6.endpoint(), "[::1]:1080"); @@ -46,7 +46,7 @@ fn parses_disabled_and_endpoint_forms() { #[test] fn parses_percent_encoded_credentials_without_exposing_them() { let config = - parse("portal://secret@127.0.0.1:2077?socks=user%3Aname:p%40ss%26word@proxy.test:1080") + parse("portal://secret@127.0.0.1:2000?socks=user%3Aname:p%40ss%26word@proxy.test:1080") .unwrap() .unwrap(); let credentials = config.credentials().unwrap(); @@ -61,14 +61,14 @@ fn parses_percent_encoded_credentials_without_exposing_them() { #[test] fn rejects_ambiguous_or_invalid_configuration() { for raw in [ - "portal://secret@127.0.0.1:2077?socks=user@proxy.test:1080", - "portal://secret@127.0.0.1:2077?socks=:pass@proxy.test:1080", - "portal://secret@127.0.0.1:2077?socks=user:@proxy.test:1080", - "portal://secret@127.0.0.1:2077?socks=user:p:ass@proxy.test:1080", - "portal://secret@127.0.0.1:2077?socks=user:p+ass@proxy.test:1080", - "portal://secret@127.0.0.1:2077?socks=proxy.test:0", - "portal://secret@127.0.0.1:2077?socks=::1:1080", - "portal://secret@127.0.0.1:2077?socks=user:%GG@proxy.test:1080", + "portal://secret@127.0.0.1:2000?socks=user@proxy.test:1080", + "portal://secret@127.0.0.1:2000?socks=:pass@proxy.test:1080", + "portal://secret@127.0.0.1:2000?socks=user:@proxy.test:1080", + "portal://secret@127.0.0.1:2000?socks=user:p:ass@proxy.test:1080", + "portal://secret@127.0.0.1:2000?socks=user:p+ass@proxy.test:1080", + "portal://secret@127.0.0.1:2000?socks=proxy.test:0", + "portal://secret@127.0.0.1:2000?socks=::1:1080", + "portal://secret@127.0.0.1:2000?socks=user:%GG@proxy.test:1080", ] { assert!(parse(raw).is_err(), "accepted {raw}"); } @@ -77,7 +77,7 @@ fn rejects_ambiguous_or_invalid_configuration() { #[test] fn duplicate_socks_uses_the_first_value() { let config = - parse("portal://secret@127.0.0.1:2077?socks=proxy.test:1080&socks=other.test:1080") + parse("portal://secret@127.0.0.1:2000?socks=proxy.test:1080&socks=other.test:1080") .unwrap() .unwrap(); @@ -86,7 +86,7 @@ fn duplicate_socks_uses_the_first_value() { #[test] fn malformed_unknown_query_key_is_ignored() { - let config = parse("portal://secret@127.0.0.1:2077?%FF=x&socks=proxy.test:1080") + let config = parse("portal://secret@127.0.0.1:2000?%FF=x&socks=proxy.test:1080") .unwrap() .unwrap(); @@ -98,11 +98,11 @@ fn credential_lengths_follow_rfc_1929() { let username = "u".repeat(255); let password = "p".repeat(255); let accepted = - format!("portal://secret@127.0.0.1:2077?socks={username}:{password}@proxy.test:1080"); + format!("portal://secret@127.0.0.1:2000?socks={username}:{password}@proxy.test:1080"); assert!(parse(&accepted).is_ok()); let username = "u".repeat(256); - let rejected = format!("portal://secret@127.0.0.1:2077?socks={username}:p@proxy.test:1080"); + let rejected = format!("portal://secret@127.0.0.1:2000?socks={username}:p@proxy.test:1080"); assert!(parse(&rejected).is_err()); } @@ -124,7 +124,7 @@ async fn tcp_connect_uses_only_no_auth_and_preserves_domain() { stream.write_all(&payload).await.unwrap(); }); - let config = parse(&format!("portal://secret@127.0.0.1:2077?socks={endpoint}")).unwrap(); + let config = parse(&format!("portal://secret@127.0.0.1:2000?socks={endpoint}")).unwrap(); let dialer = OutboundDialer::new("auto".to_string(), config); let target = Target::domain("target.test", 443).unwrap(); let mut stream = dialer @@ -154,7 +154,7 @@ async fn authenticated_connect_cannot_downgrade_to_no_auth() { }); let config = parse(&format!( - "portal://secret@127.0.0.1:2077?socks=user:pass@{endpoint}" + "portal://secret@127.0.0.1:2000?socks=user:pass@{endpoint}" )) .unwrap(); let dialer = OutboundDialer::new("auto".to_string(), config); @@ -210,7 +210,7 @@ async fn udp_associate_wraps_payload_and_keeps_control_alive() { assert_eq!(control.read(&mut eof).await.unwrap(), 0); }); - let config = parse(&format!("portal://secret@127.0.0.1:2077?socks={endpoint}")).unwrap(); + let config = parse(&format!("portal://secret@127.0.0.1:2000?socks={endpoint}")).unwrap(); let dialer = OutboundDialer::new("127.0.0.1".to_string(), config); let target = Target::domain("dns.test", 53).unwrap(); let socket = dialer @@ -252,7 +252,7 @@ async fn proxy_failure_never_falls_back_to_direct_target() { }); let config = parse(&format!( - "portal://secret@127.0.0.1:2077?socks={proxy_addr}" + "portal://secret@127.0.0.1:2000?socks={proxy_addr}" )) .unwrap(); let dialer = OutboundDialer::new("auto".to_string(), config); @@ -289,7 +289,7 @@ async fn udp_association_ends_when_control_connection_closes() { write_test_reply(&mut control, relay_addr).await; }); - let config = parse(&format!("portal://secret@127.0.0.1:2077?socks={endpoint}")).unwrap(); + let config = parse(&format!("portal://secret@127.0.0.1:2000?socks={endpoint}")).unwrap(); let dialer = OutboundDialer::new("auto".to_string(), config); let target = Target::domain("dns.test", 53).unwrap(); let socket = dialer @@ -341,7 +341,7 @@ async fn each_udp_flow_uses_a_distinct_association() { drop(relays); }); - let config = parse(&format!("portal://secret@127.0.0.1:2077?socks={endpoint}")).unwrap(); + let config = parse(&format!("portal://secret@127.0.0.1:2000?socks={endpoint}")).unwrap(); let dialer = OutboundDialer::new("auto".to_string(), config); let first_target = Target::domain("one.test", 53).unwrap(); let first = dialer diff --git a/src/tests/common/tls.rs b/src/tests/common/tls.rs index 96f09f7..80f39bc 100644 --- a/src/tests/common/tls.rs +++ b/src/tests/common/tls.rs @@ -18,7 +18,7 @@ fn certificate_sha256_uses_lowercase_hex() { #[test] fn server_tls_config_explicitly_disables_early_data() { let (_, tls, _quic) = new_server_configs( - &Url::parse("portal://secret@127.0.0.1:2077?tls=1").unwrap(), + &Url::parse("portal://secret@127.0.0.1:2000?tls=1").unwrap(), Logger::new(LogLevel::None, false), ) .unwrap(); @@ -30,7 +30,7 @@ fn server_tls_config_explicitly_disables_early_data() { #[test] fn server_prefers_v2_and_accepts_default_v1() { let (_, tls, _) = new_server_configs( - &Url::parse("portal://secret@127.0.0.1:2077?tls=1&alpn=private/2&pool=8").unwrap(), + &Url::parse("portal://secret@127.0.0.1:2000?tls=1&alpn=private/2&pool=8").unwrap(), Logger::new(LogLevel::None, false), ) .unwrap(); diff --git a/src/tests/main.rs b/src/tests/main.rs index 2b6e840..fe93ab1 100644 --- a/src/tests/main.rs +++ b/src/tests/main.rs @@ -62,12 +62,12 @@ fn help_text_documents_usage_and_configuration_surface() { #[test] fn parse_command_url_keeps_vector_remote_host() { let parsed = parse_command_url( - "vector://secret@relay.example:2077?up=udp&down=tcp&socks=127.0.0.1:1080", + "vector://secret@relay.example:2000?up=udp&down=tcp&socks=127.0.0.1:1080", ) .unwrap(); assert_eq!(parsed.url.scheme(), "vector"); assert_eq!(parsed.url.host_str(), Some("relay.example")); - assert_eq!(parsed.url.port(), Some(2077)); + assert_eq!(parsed.url.port(), Some(2000)); assert_eq!(parsed.listen_host, None); } @@ -81,11 +81,11 @@ fn logger_rejects_unknown_or_empty_levels() { #[test] fn parse_command_url_accepts_empty_listen_host() { - let parsed = parse_command_url("portal://secret@:2077?log=none&dial=::1").unwrap(); + let parsed = parse_command_url("portal://secret@:2000?log=none&dial=::1").unwrap(); assert_eq!(parsed.url.scheme(), "portal"); assert_eq!(parsed.url.username(), "secret"); - assert_eq!(parsed.url.port(), Some(2077)); + assert_eq!(parsed.url.port(), Some(2000)); assert_eq!(parsed.listen_host.as_deref(), Some("")); assert_eq!( parsed @@ -100,25 +100,25 @@ fn parse_command_url_accepts_empty_listen_host() { #[test] fn parse_command_url_accepts_empty_listen_host_without_userinfo() { - let parsed = parse_command_url("portal://:2077").unwrap(); + let parsed = parse_command_url("portal://:2000").unwrap(); assert_eq!(parsed.url.scheme(), "portal"); assert_eq!(parsed.url.username(), ""); - assert_eq!(parsed.url.port(), Some(2077)); + assert_eq!(parsed.url.port(), Some(2000)); assert_eq!(parsed.listen_host.as_deref(), Some("")); } #[test] fn parse_command_url_rejects_empty_host_for_explicit_carriers() { - assert!(parse_command_url("portal://secret@/tcp:2077").is_err()); + assert!(parse_command_url("portal://secret@/tcp:2006").is_err()); } #[test] fn parse_command_url_keeps_normal_hosts() { - let parsed = parse_command_url("portal://secret@[::]:2077?dial=auto").unwrap(); + let parsed = parse_command_url("portal://secret@[::]:2000?dial=auto").unwrap(); assert_eq!(parsed.url.host_str(), Some("[::]")); - assert_eq!(parsed.url.port(), Some(2077)); + assert_eq!(parsed.url.port(), Some(2000)); assert_eq!(parsed.listen_host, None); } @@ -128,10 +128,10 @@ async fn invalid_configuration_urls_fail_before_service_startup_with_safe_errors for (raw, expected) in [ ("not-a-url".to_owned(), "invalid configuration URL"), ( - format!("ftp://{SECRET}@example.com:2077"), + format!("ftp://{SECRET}@example.com:2000"), "scheme must be portal or vector", ), - ("portal://@*:2077?log=none".to_owned(), "missing shared key"), + ("portal://@*:2000?log=none".to_owned(), "missing shared key"), ( format!("portal://{SECRET}@*:abc?log=none"), "invalid port number", @@ -141,7 +141,7 @@ async fn invalid_configuration_urls_fail_before_service_startup_with_safe_errors "compact endpoint requires a port in 1..=65535", ), ( - format!("portal://{SECRET}@/tcp:2077?log=none"), + format!("portal://{SECRET}@/tcp:2006?log=none"), "empty host", ), ( @@ -149,15 +149,15 @@ async fn invalid_configuration_urls_fail_before_service_startup_with_safe_errors "must use CARRIER:PORT", ), ( - format!("portal://{SECRET}@*/tcp:2077/../udp:3088?log=none"), + format!("portal://{SECRET}@*/tcp:2006/../udp:2017?log=none"), "must not contain '.' or '..' segments", ), ( - format!("portal://{SECRET}@*:2077/tcp:3088?log=none"), + format!("portal://{SECRET}@*:2000/tcp:2006?log=none"), "choose either HOST:PORT", ), ( - format!("portal://{SECRET}@*/udp:2077/udp6:3088?log=none"), + format!("portal://{SECRET}@*/udp:2017/udp6:2017?log=none"), "UDP carrier is declared more than once", ), ( @@ -165,31 +165,31 @@ async fn invalid_configuration_urls_fail_before_service_startup_with_safe_errors "carrier port must be in 1..=65535", ), ( - format!("portal://{SECRET}@192.0.2.1/tcp6:2077?log=none"), + format!("portal://{SECRET}@192.0.2.1/tcp6:2006?log=none"), "address family does not match", ), ( - format!("portal://{SECRET}@*:2077?log=verbose"), + format!("portal://{SECRET}@*:2000?log=verbose"), "log must be none, debug, info, warn, error, or event", ), ( - format!("portal://{SECRET}@*:2077?rate=-1&log=none"), + format!("portal://{SECRET}@*:2000?rate=-1&log=none"), "rate must be a non-negative integer", ), ( - format!("portal://{SECRET}@*:2077?socks=bad&log=none"), + format!("portal://{SECRET}@*:2000?socks=bad&log=none"), "invalid socks endpoint: expected HOST:PORT", ), ( - format!("portal://{SECRET}@unresolvable.invalid:2077?rate=-1&log=none"), + format!("portal://{SECRET}@unresolvable.invalid:2000?rate=-1&log=none"), "rate must be a non-negative integer", ), ( - format!("vector://{SECRET}@*/tcp:2077?socks=:1080&log=none"), + format!("vector://{SECRET}@*/tcp:2006?socks=:1080&log=none"), "wildcard host is only valid for Portal listeners", ), ( - "vector://@example.com:2077?socks=:1080&log=none".to_owned(), + "vector://@example.com:2000?socks=:1080&log=none".to_owned(), "missing shared key", ), ( @@ -197,25 +197,25 @@ async fn invalid_configuration_urls_fail_before_service_startup_with_safe_errors "compact endpoint requires a port in 1..=65535", ), ( - format!("vector://{SECRET}@example.com:2077?log=none"), + format!("vector://{SECRET}@example.com:2000?log=none"), "socks parameter is required", ), ( - format!("vector://{SECRET}@example.com/udp:2077?up=tcp&socks=:1080&log=none"), + format!("vector://{SECRET}@example.com/udp:2017?up=tcp&socks=:1080&log=none"), "up selects a carrier not declared by the endpoint", ), ( format!( - "portal://outer@*:2077?next={SECRET}@origin.example/tcp:2077/../udp:3088&log=none" + "portal://outer@*:2000?next={SECRET}@origin.example/tcp:2006/../udp:2017&log=none" ), "must not contain '.' or '..' segments", ), ( - format!("portal://outer@*:2077?next={SECRET}@*/tcp:2077&log=none"), + format!("portal://outer@*:2000?next={SECRET}@*/tcp:2006&log=none"), "wildcard host is only valid for Portal listeners", ), ( - format!("portal://outer@*:2077?next={SECRET}@origin.example/tcp:2077?inner=1&log=none"), + format!("portal://outer@*:2000?next={SECRET}@origin.example/tcp:2006?inner=1&log=none"), "expected shared-key and one endpoint", ), ] { diff --git a/src/tests/portal.rs b/src/tests/portal.rs index 5096f58..ff5195d 100644 --- a/src/tests/portal.rs +++ b/src/tests/portal.rs @@ -15,18 +15,18 @@ fn test_logger() -> Logger { #[test] fn empty_host_listens_on_both_wildcard_families() { let portal = Portal::new_with_listen_host( - Url::parse("portal://secret@localhost:2077?dial=127.0.0.1").unwrap(), + Url::parse("portal://secret@localhost:2000?dial=127.0.0.1").unwrap(), Some(""), test_logger(), ) .unwrap(); - assert_eq!(portal.inner.endpoint_addr, "*:2077"); + assert_eq!(portal.inner.endpoint_addr, "*:2000"); assert_eq!( portal.inner.tcp_bind_addrs, vec![ - SocketAddr::from(([0, 0, 0, 0], 2077)), - SocketAddr::from(([0u16; 8], 2077)), + SocketAddr::from(([0, 0, 0, 0], 2000)), + SocketAddr::from(([0u16; 8], 2000)), ] ); assert_eq!(portal.inner.udp_bind_addrs, portal.inner.tcp_bind_addrs); @@ -34,34 +34,34 @@ fn empty_host_listens_on_both_wildcard_families() { assert_eq!(portal.inner.network_mode, NetworkMode::Mix); assert_eq!( portal.effective_url(), - "portal://*:2077?tls=1&rate=0&etar=0&dial=127.0.0.1&socks=none&next=none" + "portal://*:2000?tls=1&rate=0&etar=0&dial=127.0.0.1&socks=none&next=none" ); } #[test] fn explicit_wildcard_host_selects_one_address_family() { let ipv4 = Portal::new( - Url::parse("portal://secret@0.0.0.0:2077?dial=auto").unwrap(), + Url::parse("portal://secret@0.0.0.0:2000?dial=auto").unwrap(), test_logger(), ) .unwrap(); let ipv6 = Portal::new( - Url::parse("portal://secret@[::]:2077?dial=::1").unwrap(), + Url::parse("portal://secret@[::]:2000?dial=::1").unwrap(), test_logger(), ) .unwrap(); - assert_eq!(ipv4.inner.endpoint_addr, "0.0.0.0:2077"); + assert_eq!(ipv4.inner.endpoint_addr, "0.0.0.0:2000"); assert_eq!( ipv4.inner.tcp_bind_addrs, - vec![SocketAddr::from(([0, 0, 0, 0], 2077))] + vec![SocketAddr::from(([0, 0, 0, 0], 2000))] ); assert_eq!(ipv4.inner.outbound.dialer_ip(), "auto"); - assert_eq!(ipv6.inner.endpoint_addr, "[::]:2077"); + assert_eq!(ipv6.inner.endpoint_addr, "[::]:2000"); assert_eq!( ipv6.inner.tcp_bind_addrs, - vec![SocketAddr::from(([0u16; 8], 2077))] + vec![SocketAddr::from(([0u16; 8], 2000))] ); assert_eq!(ipv6.inner.outbound.dialer_ip(), "::1"); } @@ -69,19 +69,19 @@ fn explicit_wildcard_host_selects_one_address_family() { #[test] fn explicit_carriers_have_independent_ports_and_families() { let portal = Portal::new( - Url::parse("portal://secret@*/tcp4:2077/udp6:3088").unwrap(), + Url::parse("portal://secret@*/tcp4:2006/udp6:2017").unwrap(), test_logger(), ) .unwrap(); - assert_eq!(portal.inner.endpoint_addr, "*/tcp4:2077/udp6:3088"); + assert_eq!(portal.inner.endpoint_addr, "*/tcp4:2006/udp6:2017"); assert_eq!( portal.inner.tcp_bind_addrs, - vec![SocketAddr::from(([0, 0, 0, 0], 2077))] + vec![SocketAddr::from(([0, 0, 0, 0], 2006))] ); assert_eq!( portal.inner.udp_bind_addrs, - vec![SocketAddr::from(([0u16; 8], 3088))] + vec![SocketAddr::from(([0u16; 8], 2017))] ); assert_eq!(portal.inner.network_mode, NetworkMode::Mix); } @@ -89,10 +89,10 @@ fn explicit_carriers_have_independent_ports_and_families() { #[test] fn carrier_paths_select_network_mode_and_net_is_ignored() { let cases = [ - ("portal://secret@127.0.0.1:2077", NetworkMode::Mix), - ("portal://secret@127.0.0.1:2077?net=tcp", NetworkMode::Mix), - ("portal://secret@127.0.0.1/tcp:2077", NetworkMode::Tcp), - ("portal://secret@127.0.0.1/udp:2077", NetworkMode::Udp), + ("portal://secret@127.0.0.1:2000", NetworkMode::Mix), + ("portal://secret@127.0.0.1:2000?net=tcp", NetworkMode::Mix), + ("portal://secret@127.0.0.1/tcp:2006", NetworkMode::Tcp), + ("portal://secret@127.0.0.1/udp:2017", NetworkMode::Udp), ]; for (raw, expected) in cases { @@ -111,7 +111,7 @@ fn network_mode_checkpoint_values_match_listener_modes() { #[test] fn net_is_an_ignored_unknown_parameter() { let portal = Portal::new( - Url::parse("portal://secret@127.0.0.1:2077?net=auto").unwrap(), + Url::parse("portal://secret@127.0.0.1:2000?net=auto").unwrap(), test_logger(), ) .unwrap(); @@ -123,7 +123,7 @@ fn net_is_an_ignored_unknown_parameter() { #[test] fn socks_configuration_is_validated_and_redacted_in_effective_url() { let portal = Portal::new( - Url::parse("portal://secret@127.0.0.1:2077?log=none&socks=user:p%40ss@proxy.test:1080") + Url::parse("portal://secret@127.0.0.1:2000?log=none&socks=user:p%40ss@proxy.test:1080") .unwrap(), test_logger(), ) @@ -134,7 +134,7 @@ fn socks_configuration_is_validated_and_redacted_in_effective_url() { assert!(!effective.contains("p@ss")); let duplicate = Portal::new( - Url::parse("portal://secret@127.0.0.1:2077?socks=proxy.test:1080&socks=other.test:1080") + Url::parse("portal://secret@127.0.0.1:2000?socks=proxy.test:1080&socks=other.test:1080") .unwrap(), test_logger(), ) @@ -145,7 +145,7 @@ fn socks_configuration_is_validated_and_redacted_in_effective_url() { #[test] fn native_next_defaults_to_quic_and_redacts_the_shared_key() { let portal = Portal::new( - Url::parse("portal://relay-key@127.0.0.1:2077?next=upstream%40key@relay.example:2080") + Url::parse("portal://relay-key@127.0.0.1:2000?next=upstream%40key@relay.example:2080") .unwrap(), test_logger(), ) @@ -165,7 +165,7 @@ fn native_next_defaults_to_quic_and_redacts_the_shared_key() { #[test] fn native_next_uses_shared_endpoint_grammar_and_single_carrier_defaults() { let portal = Portal::new( - Url::parse("portal://relay-key@*/tcp4:2077?next=upstream-key@relay.example/udp6:3088") + Url::parse("portal://relay-key@*/tcp4:2006?next=upstream-key@relay.example/udp6:2017") .unwrap(), test_logger(), ) @@ -174,7 +174,7 @@ fn native_next_uses_shared_endpoint_grammar_and_single_carrier_defaults() { assert_eq!(portal.inner.network_mode, NetworkMode::Tcp); assert_eq!( portal.inner.outbound.next_endpoint(), - "relay.example/udp6:3088" + "relay.example/udp6:2017" ); assert_eq!( portal.inner.outbound.next_transport().as_deref(), @@ -186,7 +186,7 @@ fn native_next_uses_shared_endpoint_grammar_and_single_carrier_defaults() { fn native_next_reuses_transport_identity_and_source_binding() { let portal = Portal::new( Url::parse( - "portal://relay-key@127.0.0.1:2077?dial=127.0.0.2&alpn=private/2&next=secret@[::1]:2080&up=tcp&down=tcp&mux=1&sni=origin.example&pin=abc", + "portal://relay-key@127.0.0.1:2000?dial=127.0.0.2&alpn=private/2&next=secret@[::1]:2080&up=tcp&down=tcp&mux=1&sni=origin.example&pin=abc", ) .unwrap(), test_logger(), @@ -209,7 +209,7 @@ fn native_next_reuses_transport_identity_and_source_binding() { fn native_next_and_socks_are_mutually_exclusive() { let result = Portal::new( Url::parse( - "portal://relay-key@127.0.0.1:2077?next=secret@origin.example:2080&socks=127.0.0.1:1080", + "portal://relay-key@127.0.0.1:2000?next=secret@origin.example:2080&socks=127.0.0.1:1080", ) .unwrap(), test_logger(), @@ -217,7 +217,7 @@ fn native_next_and_socks_are_mutually_exclusive() { assert!(result.is_err()); let disabled_socks = Portal::new( - Url::parse("portal://relay-key@127.0.0.1:2077?next=secret@origin.example:2080&socks=none") + Url::parse("portal://relay-key@127.0.0.1:2000?next=secret@origin.example:2080&socks=none") .unwrap(), test_logger(), ); @@ -232,7 +232,7 @@ fn disabled_next_ignores_all_native_upstream_options() { "next=none&up=%GG&mux=%GG&pin=%FF", ] { let portal = Portal::new( - Url::parse(&format!("portal://relay-key@127.0.0.1:2077?{suffix}")).unwrap(), + Url::parse(&format!("portal://relay-key@127.0.0.1:2000?{suffix}")).unwrap(), test_logger(), ) .unwrap(); @@ -254,7 +254,7 @@ fn enabled_next_validates_only_effective_upstream_options() { ] { let result = Portal::new( Url::parse(&format!( - "portal://relay-key@127.0.0.1:2077?next=secret@origin.example:2080&{suffix}" + "portal://relay-key@127.0.0.1:2000?next=secret@origin.example:2080&{suffix}" )) .unwrap(), test_logger(), @@ -275,7 +275,7 @@ fn native_next_accepts_mix_and_normalizes_pure_udp_mux() { ] { let portal = Portal::new( Url::parse(&format!( - "portal://relay-key@127.0.0.1:2077?next=secret@origin.example:2080&up={up}&down={down}&mux=1" + "portal://relay-key@127.0.0.1:2000?next=secret@origin.example:2080&up={up}&down={down}&mux=1" )) .unwrap(), test_logger(), @@ -297,7 +297,7 @@ fn native_next_accepts_mix_and_normalizes_pure_udp_mux() { fn next_uses_first_duplicate_and_rejects_empty_value() { let portal = Portal::new( Url::parse( - "portal://relay-key@127.0.0.1:2077?next=first@one.example:2080&next=second@two.example:2081", + "portal://relay-key@127.0.0.1:2000?next=first@one.example:2080&next=second@two.example:2081", ) .unwrap(), test_logger(), @@ -307,7 +307,7 @@ fn next_uses_first_duplicate_and_rejects_empty_value() { assert!( Portal::new( - Url::parse("portal://relay-key@127.0.0.1:2077?next=").unwrap(), + Url::parse("portal://relay-key@127.0.0.1:2000?next=").unwrap(), test_logger(), ) .is_err() @@ -317,7 +317,7 @@ fn next_uses_first_duplicate_and_rejects_empty_value() { #[test] fn direct_portal_reports_exact_zero_ping() { let portal = Portal::new( - Url::parse("portal://relay-key@127.0.0.1:2077").unwrap(), + Url::parse("portal://relay-key@127.0.0.1:2000").unwrap(), test_logger(), ) .unwrap(); @@ -328,7 +328,7 @@ fn direct_portal_reports_exact_zero_ping() { fn all_network_modes_reject_tls_zero() { for mode in ["mix", "tcp", "udp"] { let portal = Portal::new( - Url::parse(&format!("portal://secret@127.0.0.1:2077?tls=0&net={mode}")).unwrap(), + Url::parse(&format!("portal://secret@127.0.0.1:2000?tls=0&net={mode}")).unwrap(), test_logger(), ); assert!(portal.is_err()); @@ -366,14 +366,14 @@ async fn network_mode_binds_only_selected_transports() { #[test] fn portal_url_contract_rejects_invalid_structure_and_selected_values() { for raw in [ - "vector://secret@127.0.0.1:2077", - "portal://secret:password@127.0.0.1:2077", - "portal://secret@127.0.0.1:2077/tcp:2077", - "portal://secret@127.0.0.1/not-a-carrier:2077", - "portal://secret@127.0.0.1:2077#fragment", - "portal://secret@127.0.0.1:2077?socks=", - "portal://secret@127.0.0.1:2077?rate=-1", - "portal://secret@127.0.0.1:2077?dial=not-an-ip", + "vector://secret@127.0.0.1:2000", + "portal://secret:password@127.0.0.1:2000", + "portal://secret@127.0.0.1:2000/tcp:2006", + "portal://secret@127.0.0.1/not-a-carrier:2000", + "portal://secret@127.0.0.1:2000#fragment", + "portal://secret@127.0.0.1:2000?socks=", + "portal://secret@127.0.0.1:2000?rate=-1", + "portal://secret@127.0.0.1:2000?dial=not-an-ip", "portal://secret@127.0.0.1:0", "portal://secret@127.0.0.1", ] { @@ -388,7 +388,7 @@ fn portal_url_contract_rejects_invalid_structure_and_selected_values() { fn portal_ignores_unknown_parameters_and_keeps_first_duplicate() { let portal = Portal::new( Url::parse( - "portal://secret@127.0.0.1:2077?unknown=value&spec=ignored&alpn=private/2&mux=2&pool=8&net=tcp&net=udp&rate=1&rate=2", + "portal://secret@127.0.0.1:2000?unknown=value&spec=ignored&alpn=private/2&mux=2&pool=8&net=tcp&net=udp&rate=1&rate=2", ) .unwrap(), test_logger(), @@ -408,7 +408,7 @@ fn portal_mux_is_ignored_without_next() { for value in ["", "0", "1", "2", "true"] { let portal = Portal::new( Url::parse(&format!( - "portal://secret@127.0.0.1:2077?alpn=private/2&mux={value}" + "portal://secret@127.0.0.1:2000?alpn=private/2&mux={value}" )) .unwrap(), test_logger(), @@ -422,11 +422,11 @@ fn portal_mux_is_ignored_without_next() { #[test] fn certificate_parameters_are_tied_to_ca_trusted_mode() { for raw in [ - "portal://secret@127.0.0.1:2077?crt=cert.pem", - "portal://secret@127.0.0.1:2077?key=key.pem", - "portal://secret@127.0.0.1:2077?crt=cert.pem&key=key.pem", - "portal://secret@127.0.0.1:2077?tls=2&crt=cert.pem", - "portal://secret@127.0.0.1:2077?tls=2&key=key.pem", + "portal://secret@127.0.0.1:2000?crt=cert.pem", + "portal://secret@127.0.0.1:2000?key=key.pem", + "portal://secret@127.0.0.1:2000?crt=cert.pem&key=key.pem", + "portal://secret@127.0.0.1:2000?tls=2&crt=cert.pem", + "portal://secret@127.0.0.1:2000?tls=2&key=key.pem", ] { assert!(Portal::new(Url::parse(raw).unwrap(), test_logger()).is_err()); } diff --git a/src/tests/portal/conn/relay.rs b/src/tests/portal/conn/relay.rs index 66089f2..98f1e7f 100644 --- a/src/tests/portal/conn/relay.rs +++ b/src/tests/portal/conn/relay.rs @@ -24,12 +24,12 @@ fn paired_path_contains_both_carriers_and_both_client_links() { let uplink = LinkPath { version: crate::protocol::ProtocolVersion::V2, peer: "198.51.100.1:1000".into(), - local: "192.0.2.1:2077".into(), + local: "192.0.2.1:2000".into(), }; let downlink = LinkPath { version: crate::protocol::ProtocolVersion::V2, peer: "[2001:db8::2]:2000".into(), - local: "[2001:db8::1]:2077".into(), + local: "[2001:db8::1]:2000".into(), }; assert_eq!( paired_exchange_path( @@ -40,7 +40,7 @@ fn paired_path_contains_both_carriers_and_both_client_links() { Carrier::Quic, &downlink, ), - "UP[TCP] 198.51.100.1:1000 -> 192.0.2.1:2077 -> 192.0.2.1:3000 -> target.test:443 | DOWN[UDP] target.test:443 -> 192.0.2.1:3000 -> [2001:db8::1]:2077 -> [2001:db8::2]:2000" + "UP[TCP] 198.51.100.1:1000 -> 192.0.2.1:2000 -> 192.0.2.1:3000 -> target.test:443 | DOWN[UDP] target.test:443 -> 192.0.2.1:3000 -> [2001:db8::1]:2000 -> [2001:db8::2]:2000" ); } @@ -50,11 +50,11 @@ fn symmetric_path_uses_the_same_carrier_prefix() { symmetric_exchange_path( Carrier::Quic, "198.51.100.1:1000", - "192.0.2.1:2077", + "192.0.2.1:2000", "192.0.2.1:3000", "target.test:443", ), - "UP[UDP] 198.51.100.1:1000 -> 192.0.2.1:2077 -> 192.0.2.1:3000 -> target.test:443 | DOWN[UDP] target.test:443 -> 192.0.2.1:3000 -> 192.0.2.1:2077 -> 198.51.100.1:1000" + "UP[UDP] 198.51.100.1:1000 -> 192.0.2.1:2000 -> 192.0.2.1:3000 -> target.test:443 | DOWN[UDP] target.test:443 -> 192.0.2.1:3000 -> 192.0.2.1:2000 -> 198.51.100.1:1000" ); } diff --git a/src/tests/portal/conn/tcp.rs b/src/tests/portal/conn/tcp.rs index 91fcb2d..4d954ff 100644 --- a/src/tests/portal/conn/tcp.rs +++ b/src/tests/portal/conn/tcp.rs @@ -58,7 +58,7 @@ async fn portal_accepts_delayed_dedicated_flow_header() { let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let listen_addr = listener.local_addr().unwrap(); let portal = Portal::new( - Url::parse("portal://secret@127.0.0.1:2077?log=none&net=tcp").unwrap(), + Url::parse("portal://secret@127.0.0.1:2000?log=none&net=tcp").unwrap(), Logger::new(LogLevel::None, false), ) .unwrap(); @@ -111,7 +111,7 @@ async fn tls_tcp_relays_through_socks5_connect() { let listen_addr = listener.local_addr().unwrap(); let portal = Portal::new( Url::parse(&format!( - "portal://secret@127.0.0.1:2077?log=none&net=tcp&socks={socks_addr}" + "portal://secret@127.0.0.1:2000?log=none&net=tcp&socks={socks_addr}" )) .unwrap(), Logger::new(LogLevel::None, false), @@ -164,7 +164,7 @@ async fn tls_tcp_uot_relays_udp_and_counts_logical_udp() { let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let listen_addr = listener.local_addr().unwrap(); let portal = Portal::new( - Url::parse("portal://secret@127.0.0.1:2077?log=none&net=tcp").unwrap(), + Url::parse("portal://secret@127.0.0.1:2000?log=none&net=tcp").unwrap(), Logger::new(LogLevel::None, false), ) .unwrap(); @@ -220,7 +220,7 @@ async fn tls_tcp_uot_relays_through_authenticated_socks5_udp() { let listen_addr = listener.local_addr().unwrap(); let portal = Portal::new( Url::parse(&format!( - "portal://secret@127.0.0.1:2077?log=none&net=tcp&socks=user:pass@{socks_addr}" + "portal://secret@127.0.0.1:2000?log=none&net=tcp&socks=user:pass@{socks_addr}" )) .unwrap(), Logger::new(LogLevel::None, false), @@ -265,7 +265,7 @@ async fn tls_tcp_auth_failure_waits_for_deadline_without_application_response() let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let listen_addr = listener.local_addr().unwrap(); let portal = Portal::new( - Url::parse("portal://secret@127.0.0.1:2077?log=none&net=tcp").unwrap(), + Url::parse("portal://secret@127.0.0.1:2000?log=none&net=tcp").unwrap(), Logger::new(LogLevel::None, false), ) .unwrap(); @@ -305,7 +305,7 @@ async fn tls_tcp_flow_header_timeout_closes_unused_connection() { let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let listen_addr = listener.local_addr().unwrap(); let portal = Portal::new( - Url::parse("portal://secret@127.0.0.1:2077?log=none&net=tcp").unwrap(), + Url::parse("portal://secret@127.0.0.1:2000?log=none&net=tcp").unwrap(), Logger::new(LogLevel::None, false), ) .unwrap(); @@ -346,7 +346,7 @@ async fn tls_mux_carrier_closes_after_becoming_fully_idle() { let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let listen_addr = listener.local_addr().unwrap(); let portal = Portal::new( - Url::parse("portal://secret@127.0.0.1:2077?log=none&net=tcp").unwrap(), + Url::parse("portal://secret@127.0.0.1:2000?log=none&net=tcp").unwrap(), Logger::new(LogLevel::None, false), ) .unwrap(); @@ -406,7 +406,7 @@ async fn tls_tcp_coalesced_auth_and_flow_bootstrap_relays() { let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let listen_addr = listener.local_addr().unwrap(); let portal = Portal::new( - Url::parse("portal://secret@127.0.0.1:2077?log=none&net=tcp").unwrap(), + Url::parse("portal://secret@127.0.0.1:2000?log=none&net=tcp").unwrap(), Logger::new(LogLevel::None, false), ) .unwrap(); @@ -446,7 +446,7 @@ async fn tls_tcp_carrier_mismatch_returns_invalid_request() { let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let listen_addr = listener.local_addr().unwrap(); let portal = Portal::new( - Url::parse("portal://secret@127.0.0.1:2077?log=none&net=tcp").unwrap(), + Url::parse("portal://secret@127.0.0.1:2000?log=none&net=tcp").unwrap(), Logger::new(LogLevel::None, false), ) .unwrap(); @@ -488,7 +488,7 @@ async fn mismatched_open_leaves_invalid_request_for_later_attach() { let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let listen_addr = listener.local_addr().unwrap(); let portal = Portal::new( - Url::parse("portal://secret@127.0.0.1:2077?log=none&net=tcp").unwrap(), + Url::parse("portal://secret@127.0.0.1:2000?log=none&net=tcp").unwrap(), Logger::new(LogLevel::None, false), ) .unwrap(); diff --git a/src/tests/portal/pairing.rs b/src/tests/portal/pairing.rs index a25695c..dd66b94 100644 --- a/src/tests/portal/pairing.rs +++ b/src/tests/portal/pairing.rs @@ -67,7 +67,7 @@ fn path(label: &str) -> LinkPath { LinkPath { version: crate::protocol::ProtocolVersion::V2, peer: format!("{label}.client:1234"), - local: "portal.test:2077".into(), + local: "portal.test:2000".into(), } } diff --git a/src/tests/portal/pairing/version.rs b/src/tests/portal/pairing/version.rs index 2b868b7..7830f02 100644 --- a/src/tests/portal/pairing/version.rs +++ b/src/tests/portal/pairing/version.rs @@ -5,7 +5,7 @@ fn versioned_half(version: ProtocolVersion, label: &str) -> LinkHalf { LinkHalf::tcp(LinkPath { version, peer: format!("{label}.client:1234"), - local: "portal.test:2077".into(), + local: "portal.test:2000".into(), }) } diff --git a/src/tests/portal/runtime.rs b/src/tests/portal/runtime.rs index a8d855d..a8e7ac8 100644 --- a/src/tests/portal/runtime.rs +++ b/src/tests/portal/runtime.rs @@ -66,8 +66,8 @@ async fn tcp_startup_failure_releases_already_opened_quic_socket() { #[test] fn injected_family_failure_warns_only_when_degradation_is_allowed() { let addresses: [SocketAddr; 2] = [ - "0.0.0.0:2077".parse().unwrap(), - "[::]:2077".parse().unwrap(), + "0.0.0.0:2000".parse().unwrap(), + "[::]:2000".parse().unwrap(), ]; for allow in [false, true] { let mut warnings = Vec::new(); diff --git a/src/tests/telemetry/hub.rs b/src/tests/telemetry/hub.rs index 2868e42..3eb0e8a 100644 --- a/src/tests/telemetry/hub.rs +++ b/src/tests/telemetry/hub.rs @@ -17,7 +17,7 @@ fn descriptor() -> InstanceDescriptor { uid: 1, incarnation: 3, version: "test".to_owned(), - endpoint: ":2077".to_owned(), + endpoint: ":2000".to_owned(), config_summary: "portal net=mix".to_owned(), telemetry_interval_ms: 1_000, } @@ -26,9 +26,9 @@ fn descriptor() -> InstanceDescriptor { #[test] fn listener_summary_uses_bound_addresses_without_inventing_a_second_family() { let hub = TelemetryHub::new(descriptor()); - hub.set_listening_addresses("0.0.0.0:2077", "none"); + hub.set_listening_addresses("0.0.0.0:2000", "none"); let summary = &hub.descriptor().config_summary; - assert!(summary.ends_with("tcp=0.0.0.0:2077 udp=none")); + assert!(summary.ends_with("tcp=0.0.0.0:2000 udp=none")); assert!(!summary.contains("[::]")); } diff --git a/src/tests/telemetry/ipc.rs b/src/tests/telemetry/ipc.rs index 09c5491..1274e01 100644 --- a/src/tests/telemetry/ipc.rs +++ b/src/tests/telemetry/ipc.rs @@ -115,7 +115,7 @@ async fn slow_frame_writes_time_out() { async fn multiple_clients_can_read_and_change_subscriptions() { let mut descriptor = InstanceDescriptor::current( InstanceRole::Portal, - ":2077", + ":2000", "net=mix", Duration::from_secs(1), ) diff --git a/src/tests/tui/client.rs b/src/tests/tui/client.rs index 96473de..e6c96ef 100644 --- a/src/tests/tui/client.rs +++ b/src/tests/tui/client.rs @@ -12,7 +12,7 @@ fn hello() -> Hello { uid: 0, incarnation: 7, version: "test".to_owned(), - endpoint: ":2077".to_owned(), + endpoint: ":2000".to_owned(), config_summary: "net=mix".to_owned(), telemetry_interval_ms: 1_000, }, diff --git a/src/tests/tui/render.rs b/src/tests/tui/render.rs index 01a77ea..f720e52 100644 --- a/src/tests/tui/render.rs +++ b/src/tests/tui/render.rs @@ -22,7 +22,7 @@ fn app_with_instance() -> App { pid: 42, uid: 0, version: "test".to_owned(), - endpoint: "0.0.0.0:2077".to_owned(), + endpoint: "0.0.0.0:2000".to_owned(), config_summary: "net=mix tls=1".to_owned(), telemetry_interval_ms: 1_000, telemetry_version: 2, @@ -108,7 +108,7 @@ fn renders_full_dashboard() { assert!(!output.contains("ACTIVE")); assert!(output.contains("CPU")); assert!(output.contains("RSS")); - assert!(output.contains("LST 0.0.0.0:2077")); + assert!(output.contains("LST 0.0.0.0:2000")); assert!(output.contains("? help")); assert!(!output.contains("telemetry 1000ms")); } @@ -210,7 +210,7 @@ fn renders_narrow_two_page_dashboard() { let output = rendered(72, 20, &app_with_instance()); assert!(output.contains("Overview")); assert!(output.contains("SELECTED")); - assert!(output.contains("0.0.0.0:2077")); + assert!(output.contains("0.0.0.0:2000")); assert!(output.contains("1000ms")); assert!(output.contains("? help")); assert!(!output.contains("telemetry 1000ms")); @@ -277,7 +277,7 @@ fn full_sidebar_keeps_long_lifecycle_labels_visible() { uid: 502, version: "test".to_owned(), endpoint: "[::1]:1082".to_owned(), - config_summary: "portal=relay.example:2077".to_owned(), + config_summary: "portal=relay.example:2000".to_owned(), telemetry_interval_ms: 1_000, telemetry_version: 2, }, diff --git a/src/tests/vector.rs b/src/tests/vector.rs index e1ba5b0..3273e49 100644 --- a/src/tests/vector.rs +++ b/src/tests/vector.rs @@ -19,7 +19,7 @@ fn vector_constructs_for_each_carrier_pair() { ("mix", "mix"), ] { let url = Url::parse(&format!( - "vector://secret@127.0.0.1:2077?up={up}&down={down}&socks=127.0.0.1:1080" + "vector://secret@127.0.0.1:2000?up={up}&down={down}&socks=127.0.0.1:1080" )) .unwrap(); Vector::new(url, Logger::new(crate::common::LogLevel::None, false)).unwrap(); @@ -29,7 +29,7 @@ fn vector_constructs_for_each_carrier_pair() { #[test] fn effective_url_prints_none_for_absent_sni() { let config = VectorConfig::from_url( - &Url::parse("vector://secret@127.0.0.1:2077?socks=127.0.0.1:1080").unwrap(), + &Url::parse("vector://secret@127.0.0.1:2000?socks=127.0.0.1:1080").unwrap(), ) .unwrap(); assert!(config.effective_url().contains("&sni=none&")); @@ -41,7 +41,7 @@ async fn socks_bind_failure_moves_lifecycle_to_stopped() { let port = blocker.local_addr().unwrap().port(); let vector = Vector::new( Url::parse(&format!( - "vector://secret@127.0.0.1:2077?socks=127.0.0.1:{port}&log=none" + "vector://secret@127.0.0.1:2000?socks=127.0.0.1:{port}&log=none" )) .unwrap(), Logger::new(crate::common::LogLevel::None, false), diff --git a/src/tests/vector/config.rs b/src/tests/vector/config.rs index 805066d..b407e0a 100644 --- a/src/tests/vector/config.rs +++ b/src/tests/vector/config.rs @@ -6,7 +6,7 @@ fn parse(raw: &str) -> Result { #[test] fn defaults_to_quic_both_directions() { - let config = parse("vector://secret@example.com:2077?socks=:1080").unwrap(); + let config = parse("vector://secret@example.com:2000?socks=:1080").unwrap(); assert_eq!(config.up, CarrierMode::Udp); assert_eq!(config.down, CarrierMode::Udp); assert_eq!(config.mux, MuxMode::Disabled); @@ -18,25 +18,25 @@ fn defaults_to_quic_both_directions() { #[test] fn explicit_endpoints_select_ports_families_and_single_carrier_defaults() { - let tcp = parse("vector://secret@example.com/tcp4:2077?socks=:1080").unwrap(); + let tcp = parse("vector://secret@example.com/tcp4:2006?socks=:1080").unwrap(); assert_eq!(tcp.up, CarrierMode::Tcp); assert_eq!(tcp.down, CarrierMode::Tcp); - assert_eq!(tcp.portal_endpoint(), "example.com/tcp4:2077"); + assert_eq!(tcp.portal_endpoint(), "example.com/tcp4:2006"); let mixed = - parse("vector://secret@example.com/udp6:3088/tcp4:2077?socks=:1080&up=tcp&down=udp") + parse("vector://secret@example.com/udp6:2017/tcp4:2006?socks=:1080&up=tcp&down=udp") .unwrap(); - assert_eq!(mixed.portal_endpoint(), "example.com/tcp4:2077/udp6:3088"); - assert_eq!(mixed.remote.tcp.unwrap().port, 2077); - assert_eq!(mixed.remote.udp.unwrap().port, 3088); + assert_eq!(mixed.portal_endpoint(), "example.com/tcp4:2006/udp6:2017"); + assert_eq!(mixed.remote.tcp.unwrap().port, 2006); + assert_eq!(mixed.remote.udp.unwrap().port, 2017); } #[test] fn policy_must_use_declared_carriers() { for raw in [ - "vector://secret@example.com/tcp:2077?up=udp&socks=:1080", - "vector://secret@example.com/udp:2077?down=tcp&socks=:1080", - "vector://secret@example.com/tcp:2077?up=mix&socks=:1080", + "vector://secret@example.com/tcp:2006?up=udp&socks=:1080", + "vector://secret@example.com/udp:2017?down=tcp&socks=:1080", + "vector://secret@example.com/tcp:2006?up=mix&socks=:1080", ] { assert!(parse(raw).is_err(), "accepted {raw}"); } @@ -45,7 +45,7 @@ fn policy_must_use_declared_carriers() { #[test] fn tcp_pair_defaults_to_dedicated_lanes() { let config = - parse("vector://secret@example.com:2077?up=tcp&down=tcp&socks=127.0.0.1:1080").unwrap(); + parse("vector://secret@example.com:2000?up=tcp&down=tcp&socks=127.0.0.1:1080").unwrap(); assert_eq!(config.checkpoint_mode(), 0); assert_eq!(config.mux, MuxMode::Disabled); } @@ -65,7 +65,7 @@ fn parses_all_route_policies_and_preserves_checkpoint_modes() { ]; for (up, down, mode) in cases { let config = parse(&format!( - "vector://secret@example.com:2077?up={up}&down={down}&socks=:1080" + "vector://secret@example.com:2000?up={up}&down={down}&socks=:1080" )) .unwrap(); assert_eq!(config.up.to_string(), up); @@ -92,14 +92,14 @@ fn mux_is_available_for_tcp_or_mix_and_normalized_for_pure_udp() { ("mix", "mix"), ] { let config = parse(&format!( - "vector://secret@example.com:2077?up={up}&down={down}&mux=1&socks=:1080" + "vector://secret@example.com:2000?up={up}&down={down}&mux=1&socks=:1080" )) .unwrap(); assert_eq!(config.mux, MuxMode::Enabled, "up={up} down={down}"); } let config = - parse("vector://secret@example.com:2077?up=udp&down=udp&mux=1&socks=:1080").unwrap(); + parse("vector://secret@example.com:2000?up=udp&down=udp&mux=1&socks=:1080").unwrap(); assert_eq!(config.mux, MuxMode::Disabled); assert!(config.effective_url().contains("up=udp&down=udp")); assert!(config.effective_url().contains("&mux=0&")); @@ -108,7 +108,7 @@ fn mux_is_available_for_tcp_or_mix_and_normalized_for_pure_udp() { #[test] fn parses_authenticated_socks_and_preserves_plus() { let config = parse( - "vector://secret@example.com:2077?socks=user%2Bname:p%40ss%3Aword@%5B%3A%3A1%5D:1080", + "vector://secret@example.com:2000?socks=user%2Bname:p%40ss%3Aword@%5B%3A%3A1%5D:1080", ) .unwrap(); let credentials = config.socks.credentials.unwrap(); @@ -118,14 +118,14 @@ fn parses_authenticated_socks_and_preserves_plus() { #[test] fn rejects_missing_or_empty_socks() { - assert!(parse("vector://secret@example.com:2077").is_err()); - assert!(parse("vector://secret@example.com:2077?socks=").is_err()); + assert!(parse("vector://secret@example.com:2000").is_err()); + assert!(parse("vector://secret@example.com:2000?socks=").is_err()); } #[test] fn ignores_unknown_values_and_keeps_the_first_duplicate() { let config = parse( - "vector://secret@example.com:2077?wat=1&%FF=x&alpn=private/2&pool=8&up=tcp&up=udp&down=tcp&socks=:1080&socks=:1081", + "vector://secret@example.com:2000?wat=1&%FF=x&alpn=private/2&pool=8&up=tcp&up=udp&down=tcp&socks=:1080&socks=:1081", ) .unwrap(); assert_eq!(config.up, CarrierMode::Tcp); @@ -137,11 +137,11 @@ fn ignores_unknown_values_and_keeps_the_first_duplicate() { #[test] fn rejects_invalid_selected_values_but_accepts_disabled_identity_options() { - assert!(parse("vector://secret@example.com:2077?socks=:1080&up=auto").is_err()); - assert!(parse("vector://secret@example.com:2077?socks=:1080&rate=-1").is_err()); + assert!(parse("vector://secret@example.com:2000?socks=:1080&up=auto").is_err()); + assert!(parse("vector://secret@example.com:2000?socks=:1080&rate=-1").is_err()); for sni in ["", "none"] { let config = parse(&format!( - "vector://secret@example.com:2077?sni={sni}&socks=:1080" + "vector://secret@example.com:2000?sni={sni}&socks=:1080" )) .unwrap(); assert_eq!(config.sni, None); @@ -149,42 +149,42 @@ fn rejects_invalid_selected_values_but_accepts_disabled_identity_options() { } for pin in ["", "none"] { let config = parse(&format!( - "vector://secret@example.com:2077?pin={pin}&socks=:1080" + "vector://secret@example.com:2000?pin={pin}&socks=:1080" )) .unwrap(); assert_eq!(config.pin, None); assert!(config.effective_url().contains("&pin=none&")); } - let config = parse("vector://secret@example.com:2077?pin&socks=:1080").unwrap(); + let config = parse("vector://secret@example.com:2000?pin&socks=:1080").unwrap(); assert_eq!(config.pin, None); } #[test] fn effective_url_uses_canonical_order_and_prints_identity_options() { let config = parse( - "vector://secret@example.com:2077?log=debug&alpn=private&mux=1&pool=8&down=tcp&up=tcp&sni=relay.example&pin=abc&etar=2&rate=1&socks=:1080", + "vector://secret@example.com:2000?log=debug&alpn=private&mux=1&pool=8&down=tcp&up=tcp&sni=relay.example&pin=abc&etar=2&rate=1&socks=:1080", ) .unwrap(); assert_eq!( config.effective_url(), - "vector://example.com:2077?up=tcp&down=tcp&mux=1&sni=relay.example&pin=abc&rate=1&etar=2&socks=:1080" + "vector://example.com:2000?up=tcp&down=tcp&mux=1&sni=relay.example&pin=abc&rate=1&etar=2&socks=:1080" ); } #[test] fn ignores_removed_alpn_and_validates_mux_inputs() { for raw in [ - "vector://secret@example.com:2077?socks=:1080&mux=", - "vector://secret@example.com:2077?socks=:1080&mux=2", - "vector://secret@example.com:2077?socks=:1080&mux=true", - "vector://secret@example.com:2077?socks=:1080&mux=-1", + "vector://secret@example.com:2000?socks=:1080&mux=", + "vector://secret@example.com:2000?socks=:1080&mux=2", + "vector://secret@example.com:2000?socks=:1080&mux=true", + "vector://secret@example.com:2000?socks=:1080&mux=-1", ] { assert!(parse(raw).is_err(), "URL unexpectedly accepted: {raw}"); } for alpn in [String::new(), "a".repeat(256)] { let config = parse(&format!( - "vector://secret@example.com:2077?socks=:1080&alpn={alpn}" + "vector://secret@example.com:2000?socks=:1080&alpn={alpn}" )) .unwrap(); assert!(!config.effective_url().contains("alpn=")); @@ -195,7 +195,7 @@ fn ignores_removed_alpn_and_validates_mux_inputs() { fn preserves_pin_without_early_validation() { for pin in ["abc", "ABCDEF", "not-a-fingerprint"] { let config = parse(&format!( - "vector://secret@example.com:2077?pin={pin}&socks=:1080" + "vector://secret@example.com:2000?pin={pin}&socks=:1080" )) .unwrap(); assert_eq!(config.pin.as_deref(), Some(pin)); @@ -204,18 +204,18 @@ fn preserves_pin_without_early_validation() { #[test] fn rejects_invalid_authority_shape() { - assert!(parse("vector://example.com:2077?socks=:1080").is_err()); - assert!(parse("vector://secret:password@example.com:2077?socks=:1080").is_err()); + assert!(parse("vector://example.com:2000?socks=:1080").is_err()); + assert!(parse("vector://secret:password@example.com:2000?socks=:1080").is_err()); assert!(parse("vector://secret@example.com?socks=:1080").is_err()); - assert!(parse("vector://secret@example.com:2077/?socks=:1080").is_err()); - assert!(parse("vector://secret@example.com:2077/path?socks=:1080").is_err()); + assert!(parse("vector://secret@example.com:2000/?socks=:1080").is_err()); + assert!(parse("vector://secret@example.com:2000/path?socks=:1080").is_err()); } #[test] fn normalizes_ipv6_portal_authority() { - let config = parse("vector://secret@[::1]:2077?socks=127.0.0.1:1080").unwrap(); + let config = parse("vector://secret@[::1]:2000?socks=127.0.0.1:1080").unwrap(); assert_eq!(config.remote.host, "::1"); - assert_eq!(config.portal_endpoint(), "[::1]:2077"); + assert_eq!(config.portal_endpoint(), "[::1]:2000"); } #[test] @@ -255,12 +255,12 @@ fn upstream_authority_decodes_the_shared_key_exactly_once() { fn upstream_authority_accepts_explicit_carriers() { let query = HashMap::new(); let (config, _) = PortalClientConfig::from_upstream_authority( - "secret@origin.example/tcp6:2077", + "secret@origin.example/tcp6:2006", &query, "auto", ) .unwrap(); - assert_eq!(config.endpoint(), "origin.example/tcp6:2077"); + assert_eq!(config.endpoint(), "origin.example/tcp6:2006"); assert_eq!(config.up, CarrierMode::Tcp); assert_eq!(config.down, CarrierMode::Tcp); } @@ -285,31 +285,31 @@ fn upstream_authority_requires_unambiguous_key_endpoint_separator() { fn upstream_authority_rejects_every_invalid_endpoint_shape() { let query = HashMap::new(); for (authority, expected) in [ - ("secret@*:2077", "wildcard host is only valid"), + ("secret@*:2000", "wildcard host is only valid"), ( - "secret@origin.example:2077/tcp:3088", + "secret@origin.example:2000/tcp:2006", "choose either HOST:PORT", ), - ("secret@origin.example/tcp:2077/", "trailing slash"), + ("secret@origin.example/tcp:2006/", "trailing slash"), ( - "secret@origin.example/tcp:2077/tcp6:3088", + "secret@origin.example/tcp:2006/tcp6:2006", "TCP carrier is declared more than once", ), ("secret@origin.example/udp:0", "1..=65535"), - ("secret@origin.example/sctp:2077", "unknown carrier"), + ("secret@origin.example/sctp:2000", "unknown carrier"), ( - "secret@192.0.2.1/udp6:2077", + "secret@192.0.2.1/udp6:2017", "address family does not match", ), ( - "secret@origin.example/tcp:2077?inner=1", + "secret@origin.example/tcp:2006?inner=1", "expected shared-key and one endpoint", ), ( - "secret@origin.example/tcp:2077#fragment", + "secret@origin.example/tcp:2006#fragment", "expected shared-key and one endpoint", ), - ("bad%GG@origin.example/tcp:2077", "malformed percent escape"), + ("bad%GG@origin.example/tcp:2006", "malformed percent escape"), ] { let error = PortalClientConfig::from_upstream_authority(authority, &query, "auto") .unwrap_err() diff --git a/src/tests/vector/flow.rs b/src/tests/vector/flow.rs index 1237606..5eb9feb 100644 --- a/src/tests/vector/flow.rs +++ b/src/tests/vector/flow.rs @@ -18,7 +18,7 @@ use crate::vector::config::VectorConfig; fn test_portal_client() -> Arc { let url = - Url::parse("vector://secret@127.0.0.1:2077?up=mix&down=mix&socks=127.0.0.1:1080").unwrap(); + Url::parse("vector://secret@127.0.0.1:2000?up=mix&down=mix&socks=127.0.0.1:1080").unwrap(); let config = VectorConfig::from_url(&url).unwrap(); let credentials = Credentials::new(&url).unwrap(); PortalClient::with_session_id( diff --git a/src/tests/vector/tls.rs b/src/tests/vector/tls.rs index c2c1934..d1c8164 100644 --- a/src/tests/vector/tls.rs +++ b/src/tests/vector/tls.rs @@ -54,11 +54,11 @@ async fn client_prefers_fixed_v2_alpn() { #[test] fn missing_sni_uses_unverified_policy() { let tls = ClientTls::new(&config( - "vector://secret@127.0.0.1:2077?socks=127.0.0.1:1080", + "vector://secret@127.0.0.1:2000?socks=127.0.0.1:1080", )) .unwrap(); assert_eq!( - vector_config("vector://secret@127.0.0.1:2077?socks=127.0.0.1:1080").sni, + vector_config("vector://secret@127.0.0.1:2000?socks=127.0.0.1:1080").sni, None ); assert_eq!(tls.quic_server_name(), "127.0.0.1"); @@ -67,13 +67,13 @@ fn missing_sni_uses_unverified_policy() { #[test] fn ipv6_authority_builds_an_ip_server_name() { - let tls = ClientTls::new(&config("vector://secret@[::1]:2077?socks=127.0.0.1:1080")).unwrap(); + let tls = ClientTls::new(&config("vector://secret@[::1]:2000?socks=127.0.0.1:1080")).unwrap(); assert_eq!(tls.quic_server_name(), "::1"); } #[test] fn explicit_sni_enables_system_verification() { - let config = config("vector://secret@127.0.0.1:2077?sni=example.com&socks=127.0.0.1:1080"); + let config = config("vector://secret@127.0.0.1:2000?sni=example.com&socks=127.0.0.1:1080"); let tls = ClientTls::new(&config).unwrap(); assert_eq!(config.sni.as_deref(), Some("example.com")); assert_eq!(tls.quic_server_name(), "example.com"); From d00fdea7dcd430ea1de4357670f7597cf811e7b0 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 07:36:20 +0800 Subject: [PATCH 14/62] Expand service endpoint documentation --- README.md | 20 ++++++- docs/README.md | 33 ++++++++++- docs/configuration.md | 126 ++++++++++++++++++++++++++++++++++++++++++ docs/quick-start.md | 53 +++++++++++++++++- src/main.rs | 16 ++++++ 5 files changed, 241 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0041200..cecf9ef 100644 --- a/README.md +++ b/README.md @@ -68,9 +68,23 @@ nowhere tui +------------+ +------------+ ``` -The compact Portal endpoint accepts TLS/TCP and QUIC/UDP on the same port. -Carrier paths select one transport, separate their ports, or restrict address -families, for example `@*/tcp4:2006/udp6:2017`. +Every service URL has one shared host and either a compact or explicit +endpoint. The compact form enables both carriers on one port. The explicit +form enables only the listed carriers and gives each one its own port and +address-family policy. + +| Endpoint | Meaning | +|---|---| +| `@*:2000` | TLS/TCP and QUIC/UDP wildcard candidates, port 2000 | +| `@*/tcp:2006` | TLS/TCP only, IPv4 and IPv6 | +| `@*/udp:2017` | QUIC/UDP only, IPv4 and IPv6 | +| `@*/tcp4:2006/udp6:2017` | TLS/TCP on IPv4 and QUIC/UDP on IPv6 | + +`*` is a Portal listen host. Vector and `next` use a concrete IP address or +hostname. The compact Portal form also accepts an empty host, so `@:2000` and +`@*:2000` have the same meaning. See +[Configuration](docs/configuration.md) for the complete grammar, validation +rules, DNS behavior, family availability, and canonical output. ### One flow, two transport decisions diff --git a/docs/README.md b/docs/README.md index 3a14de9..7681a04 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,8 +13,9 @@ The documentation has one source of truth for each concern: | Understand version negotiation and peer interoperability | [Interoperability](compatibility.md) | | Implement another client or integration | [Integrations](integrations.md) | -`protocol.md` is normative. Portal and Vector share one internal bounded TLS -Mux engine. +`configuration.md` is authoritative for command URLs and runtime settings. +`protocol.md` is normative for bytes exchanged between peers. Portal and +Vector share one internal bounded TLS Mux engine. Portal and Vector have the same transport behavior on Linux, macOS, and Windows. Platform-specific packaging, process control, filesystem paths, and @@ -54,6 +55,34 @@ Each Portal chooses exactly one outbound path for a flow: direct target access, an outbound SOCKS5 proxy, or a native `next` Portal. The carrier choice on one hop does not constrain the carrier choice on another hop. +## Endpoint summary + +Portal listeners, Vector remote endpoints, and Portal `next` endpoints use the +same carrier grammar: + +```text +HOST:PORT +HOST/CARRIER:PORT[/CARRIER:PORT] +``` + +The compact form declares TLS/TCP and QUIC/UDP on one port. The explicit form +declares only its listed carriers. `tcp` and `udp` accept IPv4 and IPv6; +`tcp4`, `udp4`, `tcp6`, and `udp6` restrict the address family. Both carriers +share `HOST`, while their ports and address families remain independent. + +| Role | Host rule | Endpoint result | +|---|---|---| +| Portal | `*`, IP literal, hostname, or compact empty host | Opens every declared listener | +| Vector | IP literal or hostname | Dials only the declared remote carriers | +| Portal `next` | IP literal or hostname | Uses the same client engine as Vector | + +`portal://key@:2000` is the compact alias for +`portal://key@*:2000`. Vector and `next` reject `*`. A Portal resolves listener +hostnames once at startup and binds every matching address; clients resolve and +filter each carrier by its declared family. Configuration errors stop startup +before service traffic is accepted. The full syntax and error rules are in +[Configuration](configuration.md). + ## Protocol summary | Client Mux setting | TLS/TCP | QUIC/UDP | Failure scope | diff --git a/docs/configuration.md b/docs/configuration.md index 74e10e9..a0bf23c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -4,10 +4,67 @@ URLs and environment variables have the same meaning on Linux, macOS, and Windows. Shell quoting and filesystem path syntax follow the host platform; see [Platforms](platforms.md). +## Service endpoint grammar + +Portal, Vector, and native Portal chaining share one endpoint model: + +```text +portal://KEY@HOST:PORT[?QUERY] +portal://KEY@HOST/CARRIER:PORT[/CARRIER:PORT][?QUERY] + +vector://KEY@HOST:PORT?QUERY +vector://KEY@HOST/CARRIER:PORT[/CARRIER:PORT]?QUERY + +next=KEY@HOST:PORT +next=KEY@HOST/CARRIER:PORT[/CARRIER:PORT] +``` + +The outer URL remains a standard URL. `KEY` is URL userinfo, `HOST` is the URL +host, and each `CARRIER:PORT` is a path segment. RFC 3986 defines the standard +[authority](https://www.rfc-editor.org/rfc/rfc3986.html#section-3.2) and allows +the colon in a [path segment](https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3), +so the explicit form does not replace or extend URL authority grammar. + +| Form | Enabled carriers | Ports | Address-family policy | +|---|---|---|---| +| `HOST:PORT` | TCP and UDP | Shared | Unrestricted | +| `HOST/tcp:PORT` | TCP only | TCP port | Unrestricted | +| `HOST/udp:PORT` | UDP only | UDP port | Unrestricted | +| `HOST/tcp:PORT/udp:PORT` | TCP and UDP | Independent | Unrestricted | +| `HOST/tcp4:PORT/udp6:PORT` | TCP and UDP | Independent | TCP IPv4, UDP IPv6 | + +Carrier names have the same meaning in every role: + +| Carrier | Transport | Accepted address family | +|---|---|---| +| `tcp` | TLS over TCP | IPv4 and IPv6 | +| `tcp4` | TLS over TCP | IPv4 only | +| `tcp6` | TLS over TCP | IPv6 only | +| `udp` | QUIC over UDP | IPv4 and IPv6 | +| `udp4` | QUIC over UDP | IPv4 only | +| `udp6` | QUIC over UDP | IPv6 only | + +`tcp` and `udp` mean that the endpoint does not restrict the address family. +They do not require both families to exist on the host. An IP literal narrows +an unrestricted carrier naturally; an explicit suffix that conflicts with the +literal is invalid. + +Both carriers always share `HOST`. Use separate service URLs when TCP and UDP +must use different IP addresses or hostnames. The explicit path controls which +carriers exist, so an omitted carrier is disabled rather than assigned a +default port. + +Canonical output lists TCP before UDP regardless of input order. It uses the +compact form when both carriers are unrestricted and use the same port; +otherwise it prints the explicit path. Effective configuration, logs, and the +TUI use this normalized endpoint and omit the shared key. + ## Portal URL ```text portal://shared-key@host:port?tls=1&log=info +portal://shared-key@*:2000?tls=1&log=info +portal://shared-key@*/tcp:2006/udp:2017?tls=1&log=info portal://shared-key@host/tcp4:2006/udp6:2017?tls=1&log=info ``` @@ -26,6 +83,20 @@ a warning. Each declared carrier must bind at least one address. Explicit address families, concrete addresses, occupied ports, and permission failures cause startup to fail and release the listeners already opened. +The Portal host controls binding: + +| Host | Listener behavior | +|---|---| +| empty in compact form | Alias for `*` | +| `*` | Separate wildcard sockets for every permitted address family | +| IPv4 literal | Bind that IPv4 address | +| bracketed IPv6 literal | Bind that IPv6 address | +| hostname | Resolve once and bind every matching, deduplicated address | + +IPv6 TCP and UDP listeners set `V6ONLY`, including wildcard listeners. A +dual-stack wildcard therefore consists of distinct `0.0.0.0` and `[::]` +sockets instead of relying on an operating-system dual-stack default. + | Query | Values | Default | |---|---|---| | `tls` | `1` generated certificate, `2` supplied certificate | `1` | @@ -49,9 +120,23 @@ Portal. These upstream options are ignored when `next` is absent or `none`. ```text vector://shared-key@host:port?up=tcp&down=tcp&socks=127.0.0.1:1080 +vector://shared-key@host/tcp:2006?socks=127.0.0.1:1080 +vector://shared-key@host/udp6:2017?socks=127.0.0.1:1080 vector://shared-key@host/tcp:2006/udp:2017?up=tcp&down=udp&socks=127.0.0.1:1080 ``` +Vector uses the TCP carrier port only for TLS and the UDP carrier port only for +QUIC. Hostname results are filtered independently for each carrier. `tcp4` and +`udp4` never fall through to IPv6, and `tcp6` and `udp6` never fall through to +IPv4. If no resolved address matches the selected family, dialing fails with a +configuration-specific address error. + +When an endpoint declares one carrier, omitted `up` and `down` both select that +carrier. When both carriers exist, each omitted direction selects UDP. An +explicit direction may select only a declared carrier, and `mix` requires both +TCP and UDP. These checks run before the SOCKS listener begins accepting +traffic. + | Query | Values | Default | |---|---|---| | `up`, `down` | `tcp`, `udp`, or `mix` | only carrier, otherwise `udp` | @@ -62,6 +147,30 @@ vector://shared-key@host/tcp:2006/udp:2017?up=tcp&down=udp&socks=127.0.0.1:1080 | `socks` | required local listen address, optionally credentials | — | | `log` | logging threshold | `info` | +## Native next endpoint + +The `next` value omits a scheme but otherwise uses the Vector endpoint grammar: + +```text +portal://relay-key@*/tcp4:2006?next=origin-key@origin.example/udp6:2017 +portal://relay-key@:2000?next=origin-key@origin.example/tcp:2006/udp:2017&up=tcp&down=udp +``` + +The local Portal listener and upstream endpoint are independent. The first +example accepts inbound TLS/TCP over IPv4 and opens the next hop with QUIC/UDP +over IPv6. A carrier or family chosen locally does not constrain the next hop. + +`next` must contain exactly one encoded shared key, `@`, and one endpoint. Its +host must be concrete; `*` is invalid. It has no inner query or fragment. +`up`, `down`, `mux`, `sni`, and `pin` remain query parameters of the outer +Portal URL. Reserved bytes in the nested key are percent-encoded once and are +decoded once when the upstream credentials are built. + +The `dial` IP from the outer Portal URL also constrains native upstream +connections. The selected endpoint family and the local `dial` family must +both match a resolved upstream address. No connection crosses an explicit +family boundary to recover from a failure. + ## Option scope ```text @@ -152,6 +261,23 @@ present. - `socks=user:pass@host:port` enables RFC 1929 authentication. Omitting the credentials enables SOCKS5 no-auth. +The following inputs fail validation before a Portal or Vector reaches its +running state: + +| Invalid shape | Reason | +|---|---| +| `host:2000/tcp:2006` | Compact authority port and carrier path are mutually exclusive | +| `host/tcp:2006/` | Trailing slash creates an empty carrier segment | +| `host/tcp:2006/tcp6:2006` | TCP is declared more than once | +| `host/sctp:2000` | Carrier name is unknown | +| `192.0.2.1/tcp6:2006` | IPv4 literal conflicts with IPv6-only TCP | +| `host/tcp:0` | Carrier ports are limited to `1..=65535` | +| `*/tcp:2006` on Vector or `next` | Wildcard is limited to Portal listeners | + +Errors identify the role and invalid endpoint component, exit with a nonzero +status, and do not print shared keys. Dot segments, including percent-encoded +forms, are rejected before a URL parser can normalize the path. + ## Environment Durations use humantime syntax such as `250ms`, `15s`, `2m`, or `1h`. diff --git a/docs/quick-start.md b/docs/quick-start.md index a571ace..09cf895 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -31,6 +31,33 @@ The compact endpoint listens for TLS/TCP and QUIC on the same numeric port. Use `portal://secret@*/tcp4:2006` for a TCP-only IPv4 listener, or `portal://secret@*/tcp:2006/udp:2017` to use separate ports. +On a host with IPv4 and IPv6 available, choose a listener form from the service +edge you want to expose: + +| Portal endpoint | TCP listeners | UDP listeners | +|---|---|---| +| `@:2000` | `0.0.0.0:2000`, `[::]:2000` | `0.0.0.0:2000`, `[::]:2000` | +| `@*:2000` | `0.0.0.0:2000`, `[::]:2000` | `0.0.0.0:2000`, `[::]:2000` | +| `@*/tcp:2006/udp:2017` | `0.0.0.0:2006`, `[::]:2006` | `0.0.0.0:2017`, `[::]:2017` | +| `@*/tcp4:2006` | `0.0.0.0:2006` | disabled | +| `@*/udp6:2017` | disabled | `[::]:2017` | + +The IPv6 listeners are `V6ONLY`; the IPv4 and IPv6 rows represent separate +sockets. A hostname or IP literal replaces `*` when the Portal should bind +only selected interfaces. Hostnames are resolved once during startup. + +For independent carrier ports, start Portal with: + +```text +nowhere 'portal://secret@*/tcp:2006/udp:2017?log=info' +``` + +Portal prints one listening line for each bound TCP or UDP address. The TUI +shows the actual address lists after startup. A carrier must bind at least one +address; explicit families, concrete addresses, permission errors, and occupied +ports fail startup. Only an unrestricted `*` listener may continue when the +operating system does not support one address family. + ## 2. Start Vector Dedicated TLS lanes in both directions: @@ -45,6 +72,28 @@ QUIC in both directions: nowhere 'vector://secret@127.0.0.1:2000?up=udp&down=udp&socks=127.0.0.1:1080' ``` +When Portal uses independent ports, Vector declares the same endpoint: + +```text +nowhere 'vector://secret@127.0.0.1/tcp:2006/udp:2017?up=tcp&down=udp&socks=127.0.0.1:1080' +``` + +The carrier path describes what can be dialed. `up` and `down` choose from +those carriers for each logical direction. A single-carrier endpoint needs no +explicit direction policy: + +```text +nowhere 'vector://secret@127.0.0.1/tcp4:2006?socks=127.0.0.1:1080' +nowhere 'vector://secret@[::1]/udp6:2017?socks=127.0.0.1:1080' +``` + +The first command defaults both directions to TCP; the second defaults both to +UDP. Vector rejects `up`, `down`, or `mix` when the endpoint does not declare +the required carrier. Vector resolves TCP and UDP independently and never +ignores a `4` or `6` suffix. Check the Portal log, local firewall, container +port publication, and the Vector endpoint together when one carrier is +unreachable. + The full route-policy matrix is: | `up` ↓ / `down` → | `tcp` | `udp` | `mix` | @@ -64,8 +113,8 @@ nowhere 'vector://secret@127.0.0.1:2000?up=mix&down=mix&socks=127.0.0.1:1080' `mix/mix` chooses `tcp/tcp` or `udp/udp` once per flow. A single mixed direction can resolve to a split carrier pair. Declaring both carriers makes -every matrix cell reachable. The primary choice has a `NOW_MIX_FALLBACK_TIMEOUT` budget -(default `1s`). +every matrix cell reachable. The primary choice has a +`NOW_MIX_FALLBACK_TIMEOUT` budget (default `1s`). TLS Mux is enabled on Vector. Portal recognizes the marked carrier automatically: diff --git a/src/main.rs b/src/main.rs index 66fa117..dc134b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -107,6 +107,22 @@ Carrier endpoint grammar: tcp4, udp4 Use IPv4 only. tcp6, udp6 Use IPv6 only. host:port Shorthand for TCP and UDP on the same port. + Explicit paths enable only their listed carriers. TCP and UDP share the host + but may use independent ports and address families. Carrier order is ignored; + effective configuration prints TCP before UDP. + Do not combine an authority port with carrier paths. Empty or trailing path + segments, duplicate or unknown carriers, family conflicts, and port 0 fail. + +Portal binding: + An unrestricted * carrier opens separate IPv4 and IPv6 wildcard sockets. + IPv6 listeners are V6ONLY. Hostnames resolve once at startup and bind every + matching address. Each declared carrier must bind at least one address. + Only an unavailable family on unrestricted * may degrade with a warning. + +Vector and next dialing: + * is invalid. DNS results are filtered independently for each carrier family. + A single carrier is the default for both directions; with both, UDP is the + default. Explicit up/down must exist, and mix requires both carriers. Transport capabilities: TLS/TCP TCP relay and UDP-over-TCP (UoT). From a2cb4d098405320f100c6e0133afc318e58a5280 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 07:36:35 +0800 Subject: [PATCH 15/62] Document listener deployment behavior --- docs/operations.md | 47 +++++++++++++++++++++++++++++++++++++++++++++- docs/platforms.md | 41 ++++++++++++++++++++++++++++++++++++++++ docs/security.md | 30 +++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/docs/operations.md b/docs/operations.md index 64fa343..7fc52ae 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -12,6 +12,45 @@ Run `nowhere` without a URL and select: - `1` Overview; - `2` Logs. +## Listener lifecycle + +Portal validates the complete URL, resolves every declared carrier, and opens +its UDP and TCP listener sets before entering `READY`. Each successful bind is +logged with its actual transport and socket address: + +```text +listening on TLS/TCP 0.0.0.0:2006 +listening on TLS/TCP [::]:2006 +listening on QUIC/UDP 0.0.0.0:2017 +listening on QUIC/UDP [::]:2017 +``` + +The effective configuration keeps the normalized logical endpoint, such as +`*/tcp:2006/udp:2017`. TUI instance summaries show the actual TCP and UDP +address lists after binding. Disabled carriers appear as `none`; shared keys +are absent from both views. + +One hostname may resolve to several addresses. Portal deduplicates the startup +result and binds every address that matches the carrier family. These sockets +form one logical carrier listener set. DNS is not refreshed while the process +runs, and an unexpected exit from any active listener set stops the service. + +Startup is all-or-nothing for declared carriers. A carrier must bind at least +one address. A port conflict, permission error, unavailable concrete address, +or explicit family failure stops startup and releases sockets already opened. +The only partial-family case is `*` with unrestricted `tcp` or `udp`: an +operating system without one address family logs a warning and continues with +the other family. + +| Symptom | Check | +|---|---| +| TCP works but QUIC does not | UDP port publication, firewall, and the endpoint's UDP entry | +| QUIC works but TCP does not | TCP port publication, firewall, and the endpoint's TCP entry | +| IPv4 works but IPv6 does not | Carrier suffix, IPv6 route, and the separate `[::]` bind log | +| Startup reports no matching address | DNS results and the carrier's `4` or `6` suffix | +| Startup reports address in use | Each transport/port pair and any duplicate service instance | +| Vector rejects `up`, `down`, or `mix` | The remote endpoint must declare every selected carrier | + ## Capacity The important memory bounds are the 1,024 concurrent TCP flows and 256 UDP flows @@ -110,7 +149,13 @@ configured shutdown deadline. Functional validation belongs on every deployment platform: -- Portal reaches `READY` on every configured listener; +- Portal reports every expected TCP and UDP address before reaching `READY`; +- compact endpoints accept both carriers on one port, while explicit endpoints + expose only their declared carrier/port/family combinations; +- wildcard IPv6 listeners are `V6ONLY` and coexist with IPv4 listeners on the + same numeric port; +- hostname listeners bind every deduplicated startup address, and a failed + startup releases listeners opened earlier; - Vector accepts SOCKS5 CONNECT and UDP ASSOCIATE; - every configured uplink/downlink carrier combination reaches a target; - every Mix policy resolves only to its documented concrete pairs and cleans diff --git a/docs/platforms.md b/docs/platforms.md index 8613ac6..d7543a1 100644 --- a/docs/platforms.md +++ b/docs/platforms.md @@ -22,6 +22,32 @@ cargo build --release --locked cargo test --all-targets --locked ``` +## Network exposure + +Portal binds one socket for every address selected by each declared carrier. +The endpoint path therefore defines both process listeners and the firewall or +container rules required around the process. + +| Endpoint | Required inbound exposure | +|---|---| +| `*:2000` | TCP 2000 and UDP 2000 | +| `*/tcp:2006` | TCP 2006 | +| `*/udp:2017` | UDP 2017 | +| `*/tcp:2006/udp:2017` | TCP 2006 and UDP 2017 | +| `*/tcp4:2006/udp6:2017` | IPv4 TCP 2006 and IPv6 UDP 2017 | + +An unrestricted `*` carrier opens separate IPv4 and IPv6 wildcard sockets. +IPv6 sockets use `V6ONLY` on Linux, macOS, and Windows, so an IPv6 firewall +rule does not replace the corresponding IPv4 rule. If the operating system +does not support one family, only an unrestricted wildcard carrier may start +with the available family and a warning. Explicit families and concrete bind +addresses fail startup when unavailable. + +A hostname listener resolves once at startup and binds every matching address. +DNS changes take effect after a process restart. Vector and Portal `next` +resolve each remote carrier independently, filter by its `4` or `6` suffix, +and fail rather than crossing the declared family boundary. + ## Container image GHCR publishes `ghcr.io/nodepassproject/nowhere` for exactly two platforms: @@ -42,6 +68,21 @@ docker run -d --rm --name nowhere-portal \ 'portal://change-me@:2000' ``` +Publish separate carrier ports when the endpoint uses an explicit path: + +```text +docker run -d --rm --name nowhere-portal \ + -p 2006:2006/tcp \ + -p 2017:2017/udp \ + ghcr.io/nodepassproject/nowhere:latest \ + 'portal://change-me@*/tcp:2006/udp:2017' +``` + +Docker publication is transport-specific. Publishing `2006/udp` does not +expose the TCP carrier, and publishing `2017/tcp` does not expose QUIC. For an +IPv4-only or IPv6-only carrier, align the Docker host binding and host firewall +with the endpoint suffix. + For `tls=2`, mount the CA-issued PEM certificate chain and private key: ```text diff --git a/docs/security.md b/docs/security.md index f7a3978..b6c946c 100644 --- a/docs/security.md +++ b/docs/security.md @@ -22,6 +22,36 @@ TLS is version 1.3. Deployments may use a certificate pin, normal system-root verification with SNI, or the explicitly configured unverified certificate mode used by generated local certificates. +## Endpoint exposure + +The service endpoint is also the network exposure policy. A compact Portal +endpoint exposes TLS/TCP and QUIC/UDP on the same port. An explicit path exposes +only its declared carriers, ports, and address families: + +```text +portal://key@*/tcp4:2006 +portal://key@192.0.2.10/tcp:2006/udp:2017 +portal://key@[2001:db8::10]/udp6:2017 +``` + +`*` and the compact empty host bind wildcard interfaces. Use a concrete local +address when the service should be limited to one interface, and enforce the +same transport, port, and family policy in host and perimeter firewalls. Every +IPv6 listener is `V6ONLY`, so IPv4 exposure is always represented by a separate +socket and firewall decision. + +A hostname listener binds all matching addresses resolved at startup. The +result is not refreshed dynamically, which prevents a later DNS answer from +silently expanding a running process, but operators must review the complete +startup address list after each restart. Vector and native `next` honor +explicit family suffixes and never retry through the other family. + +Effective URLs, startup summaries, TUI descriptors, and configuration errors +omit the shared key. The original command URL still contains the credential; +protect shell history, process arguments, service-manager configuration, and +deployment logs accordingly. Reserved key bytes must be percent-encoded, and +nested `next` credentials are decoded exactly once. + ## Admission Portal bounds pre-authentication work and applies per-source admission before From 53ec045fdc61d0d1bbcb89ce696a4f75f18c824e Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 07:37:09 +0800 Subject: [PATCH 16/62] Clarify endpoint interoperability contracts --- docs/compatibility.md | 29 +++++++++++++++++++++++++++ docs/integrations.md | 46 +++++++++++++++++++++++++++++++++++++++++++ docs/protocol.md | 29 +++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/docs/compatibility.md b/docs/compatibility.md index c207e6b..c772136 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -37,6 +37,30 @@ compatibility: default V1 and V2 peers communicate using the version negotiation described above. Compatible compact address syntax does not imply identical query semantics across versions. +The endpoint path is local process configuration and never appears on the wire. +It determines which TCP or UDP socket carries the TLS/QUIC connection before +ALPN and AuthFrame processing begin. + +| Configuration | V2 behavior | V1 interoperability consequence | +|---|---|---| +| `HOST:PORT` | Both carriers use one unrestricted port | A default V1 client can reach both carriers | +| `HOST/tcp:2006` | Only TLS/TCP exists | A peer must select TCP and use port 2006 | +| `HOST/udp:2017` | Only QUIC/UDP exists | A peer must select UDP and use port 2017 | +| `HOST/tcp:2006/udp:2017` | Carriers use independent ports | A client must understand or otherwise know both ports | +| `HOST/tcp4:2006/udp6:2017` | Carrier DNS/address results are family-filtered | Peer reachability must satisfy the same families | + +A V1 client URL has one authority port and cannot describe separate TCP and UDP +ports. A V2 Portal intended to serve unmodified V1 clients therefore uses the +compact form on that compatibility edge. A V2 Vector may use an explicit +endpoint with a V1 Portal when each declared address and port matches a listener +that the V1 Portal actually exposes. + +`net` has no meaning to the V2 endpoint parser. For example, +`portal://key@*:2000?net=tcp` still declares both carriers because the compact +endpoint declares both. A TCP-only V2 service uses +`portal://key@*/tcp:2006`. This query difference does not change ALPN or the +compatible V1 wire encoding. + ## TLS lane contract Vector `mux=0` opens one authenticated TLS connection per Flow. Vector `mux=1` @@ -94,6 +118,11 @@ is fixed or mixed. The upstream Mux selection defaults to `0`, is ignored without an enabled `next`, and canonicalizes to `0` for a fixed `udp/udp` route. +Each hop has its own endpoint constraints. A V2 Portal may accept an inbound V1 +TLS client on a compact listener and use an IPv6-only QUIC endpoint for its V2 +`next` hop. The incoming ALPN, local listener family, upstream ALPN, and upstream +address family are evaluated independently. + Interoperability tests exercise both peer roles: one endpoint as Portal and the other as client. The complete 3×3 `up`/`down` policy matrix covers all four concrete routes and all five policies containing `mix`, together with both diff --git a/docs/integrations.md b/docs/integrations.md index 7a4c505..b9112aa 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -26,6 +26,41 @@ An integration chooses one boundary. Applications normally use SOCKS5; alternate clients implement the wire protocol; Portal chains use the native client engine. +## Command URL endpoints + +Launchers and configuration generators produce one of two standard URL shapes: + +```text +SCHEME://KEY@HOST:PORT +SCHEME://KEY@HOST/CARRIER:PORT[/CARRIER:PORT] +``` + +The compact form declares TCP and UDP on one unrestricted port. The explicit +form declares only the listed carrier entries. `tcp`, `tcp4`, and `tcp6` map to +TLS/TCP; `udp`, `udp4`, and `udp6` map to QUIC/UDP. The numeric suffix limits +DNS and literal addresses to IPv4 or IPv6. + +Configuration integrations should preserve these invariants: + +- encode the shared key as URL username data and never as password userinfo; +- keep one shared host for both carriers; +- use either an authority port or carrier path, never both; +- emit each transport at most once and order canonical output as TCP then UDP; +- emit the compact form when unrestricted TCP and UDP share a port; +- reserve `*` and compact empty hosts for Portal listeners; +- keep `next` policy in the outer Portal query rather than adding an inner + query to the nested endpoint. + +Portal accepts `portal://key@:2000` as the compact wildcard alias. Explicit +Portal listeners use `portal://key@*/tcp:2006/udp:2017`. Vector and native +`next` endpoints require an IP literal or hostname. Implementations that show +or log effective configuration omit credentials and retain the normalized +carrier path. + +An invalid URL is a startup error. Integrations should display the process +error without retrying a different carrier or rewriting an explicit address +family, because doing so would change the user's declared service edge. + ## Alternate clients Implementers should follow [Protocol](protocol.md). QUIC uses native reliable @@ -44,6 +79,12 @@ An alternate client provides: - matching OPEN and ATTACH metadata for split-carrier flows; - bounded retry and reconnection behavior after carrier failure. +The command URL is not transmitted. It selects the remote socket used for each +physical carrier; the negotiated ALPN, AuthFrame transport byte, and FlowHeader +then identify wire behavior. TCP and UDP may arrive at different Portal ports +and still belong to one session because the authenticated `session_id`, rather +than the socket address, defines the pairing scope. + The `mix` URL policy is client-side only and resolves once to TT, TQ, QT, or QQ. The primary pair has a one-second preparation budget by default. Failure or timeout selects the other allowed pair once with a new flow ID. A client never @@ -57,3 +98,8 @@ by Vector, including `up/down=mix` and pre-commit fallback. `mux=0|1` selects dedicated or Mux TLS when TCP can be selected and defaults to `0`; it has no effect without `next` and canonicalizes to `0` for `udp/udp`. Authentication, flow setup, bounds, and failure semantics are identical at every hop. + +The nested value contains no scheme, query, or fragment. Percent-encoded key +bytes are decoded exactly once. `up`, `down`, `mux`, `sni`, and `pin` stay on +the outer Portal URL, while the outer `dial` address also constrains the local +family used for upstream TCP and UDP sockets. diff --git a/docs/protocol.md b/docs/protocol.md index 476307b..74b9c7d 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -26,6 +26,35 @@ Nowhere 2 offers `nw2` first and the default V1 value `now/1` second. A selected `nw2` carrier is V2; every accepted non-`nw2` carrier is V1, which in this implementation means exactly `now/1`. +### Command endpoint mapping + +The command URL chooses the socket for each physical carrier before this wire +protocol begins. Its endpoint forms map as follows: + +| Command endpoint entry | Physical carrier | Wire transport byte | +|---|---|---:| +| compact `HOST:PORT` TCP side | TLS 1.3 over TCP on `PORT` | `0x01` | +| compact `HOST:PORT` UDP side | QUIC over UDP on `PORT` | `0x02` | +| `HOST/tcp:PORT`, `HOST/tcp4:PORT`, or `HOST/tcp6:PORT` | TLS 1.3 over TCP on its own port/family | `0x01` | +| `HOST/udp:PORT`, `HOST/udp4:PORT`, or `HOST/udp6:PORT` | QUIC over UDP on its own port/family | `0x02` | + +TCP and UDP may use different ports and address families while sharing one +command endpoint host. Port numbers, hostnames, wildcard selection, and +address-family suffixes are not serialized in AuthFrame or FlowHeader. They +only select the local listener or remote socket on which a carrier is +established. + +Disabling a carrier by omitting it from an explicit endpoint does not create a +new wire mode. It prevents the local process from listening or dialing that +physical transport. Client `up`, `down`, and `mix` policy must select from the +declared carriers before a FlowHeader is encoded. + +Separate TCP and UDP socket addresses do not separate sessions. The same +authenticated `session_id` joins all physical carriers created by one client, +so split OPEN and ATTACH lanes can pair across carrier ports and IP families. +Address family is never negotiated on the wire; reachability and family +filtering complete before TLS or QUIC authentication. + One client session has one random 16-byte `session_id`. Every physical carrier is authenticated with that ID, so Portal can pair logical lanes belonging to the same client session. From 75237f79867b69c0f4737a7b074cbdeb4444b880 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 09:18:10 +0800 Subject: [PATCH 17/62] Redesign Nowhere artwork --- assets/nowhere.png | Bin 786898 -> 923911 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/nowhere.png b/assets/nowhere.png index 4eaac7409c125a283e1397dc838818a62543a5a7..79bb4c34736143fed24254feacb1c4fd05e801ca 100644 GIT binary patch literal 923911 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%0DW;&ns))TK1@_ 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Þ)mW%g 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

lVXHx{}Nkua>UhMKc20gTUCJ;j6AZ81I!# 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 xhjm6P1P+LSuFD8((I7Ux00001b5ch_0Itp) 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�!U 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

0dje-n^AKJWYd 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%0A4=7!l^Y3dljsO z>#&SduXR@2I-8Y=psKjWm4ii`jsjm+feHqbX{F`Hjl99PY#fov*_rf^OhhbL;90e= zT~!C!2yJba!AOnQQyg?Ty&=Ch7&#nqc>OLnA~W43_K>~A8FGbO4trgo*lG88LM8sX zK(*avS-<*c8&wW1Z0N>Oaa>g&U?_;hAC zJSJ8y8~FtCr1YY;#3B}F6T;d&KZSCYR*AS&M8L&G%>&Y)iXD$Hlh`Vmx zv1=9M-SXue>dfSd|4m1F3GO5e6Kw}vh`@&OM7NmmI72f<{0Sxi7|-eICSTGhOA*`^ zs4bfaN~C5!Usc-W$*|Iv4N?W%NwKU6f^1AL$ac9}bAnZ6tCz=Patn5M-G+fMXte(T zehtFA@upNVnKTj!R$>~JKGeDz(aJ=3Q&U+}Q?mDVqOZ+@$|EteeAn(SSDC4ncM9|N z9O~GQ-P^}pH^&7)2n}Q7I46opDbL3pidF5|vmYxxx(BxgeZreKBz>Wmj=S!T9^J2( ze7FMEave~Hs4%Y9>yXFTEBchZbP;#dpHe#SxLHW`-q9oNSBWYM<5V=Jzw$#fGZS#P z)?{~(s%$@T`PSNH3hsVD?5^ScQ~jq-{EOi3$J?{Y%me>9GreGS_Zj={czI_MJmB3o zGSmdgyK?o))D=3@Y3g3iNe*24hL0z7!Z9Uaca^Y?m>q-Nw(^E@W_Mc@ySvI-m486T zqT%j@?C#zbxI6XF7Y*g+FmsVIGZ*ey(#Y{6*Sm5UUbgR!E6ag##I{mpT*spFkkf$t9VZq3uI0U-zN79rgz%Q6+t zW%2IYMcj_r-T$7?&kMVo2fZVH$4IB4j@aGQ<0(4dpQ7`9g1g2?JkP`#^iH6zdjY{) zxWUVgGe3s|@15oJ->OHM!tFRIdWBZ2{6wXyc)cNKwZrQQxPu;#A4$q*>fC`dXZ)TT zyT={~I=yaJ&>yTREiE3TUyw0$%2R4P)|IIIdU@%zX#S4@^o~HrRX$nkY|(a ziFCu9E)uz>=+&iR0k*3=%dK1a7ORXptyV2avDj`6VrOU7Su9!orY*l~b z6Qj(Yfq_dyf1RCuFafz_OG}#m)GJehiqUKh*PAm&H26F~B?7_SSWMvV?XP?G9o*ML z^ltV6!QGfx%rU~GcP(@&+^R`in8U?lbX`4`cZ62MwZpYLcTN&zoK45Vv2X=}D2#tX z=~|Cm=;2i8)=7FW4q1t%&o@(SFS|8E9Yu6 zv@pB7)(g0UzaYWsVKB5IM5Vo5GFIq4nI_j`j;GL0rGu&Hu0tI< zy^G%;?Jz6sG}_6^Po$S}4f!($(4P3d< zuS)fv5ZoPG40rE7f35$E=LC16%v@B7py2NNBf@%s2-o?~bw<03OW>{v@z6eBlTlfj zxYk?Q#}05d)QJg!=pCk+yvyUZIt6wcS$A1hrqwB`)?6iGDq^>hQPih>3Q_{g+LgY( zXd;2ZW#1g9PXj#X)D+a0Qo0nRVD45CnM$SEtnf~?yzXYm+byl_zuNrfhyuH>vJuf} z1p4oHu)9U8yBQrIHnVhfH~IBqb_cBrXq|wa;&*(oTIHVTGy!!4c0}*)KZ?)^1W))K zL0shJLdeS^-pky1=I?(+MIrqEJID&3>&}FYK36++3T<$tFAp{(b$RClGR$YwU-HJbNTP=qQR@p>=2dPTW zKx_C2ltIanz;#m=7SfQwlus@s_+?iZ*7f+4CemF32NAx(2o*Rg9d!P6He7^mP zW?w*C+J0Lj;py|f&yP^)(7;$d!<|AqKIzMpnei>2nc5M~^vBwpV*Tx6n(H4N?C&4s zez`#|nTvJA`eXdPqZ*tz>x&){?e9MtMFJV!O84uR$41drF5ZTAz7+;L6zJh^nDSt% zqo*5eZ;wVrs&eqCWK2iZYoNQdv)2xH)vrhVe5%^jBvpBCn90Cr=~~cmmuA3SIL_`i zWT)-!=`q9IjTwswAh$?g(jc!YO4o$LZnGamr%KCJ5PRUu-WPDXI| z*M%LDnfYR>N}e&yL>mGtG8^L!K#kAY&3`>o3dAai)Qx#UrKLj=wwZfSAzbPa{bfM7 zpK^qYCM8$NFZo6pT($*5xw^`-Gc+gxJLwcQY%G zM^7F;xvtq=x5B)C$@1>!%8qbr>d#Ym?oZtU+~H#Zc*onjV#qV=fN$Fc>>_c$d7R_L z3le~K==~9yieW=AK^>bCFv)xaJi(-}001BWNkl4g2f`^NSg*bX&6cpiHaB)5`cG>?8|59PiwYKx`%iL1P_sB6bT85 zXNv81yTk7FdVO}U!v}JTDx*xNGt*@+^t$Znna-RXVyg~k`sL3Y<*s6%E8k^F-Sv9v zu2hIsM9M*E_oH`-;2rqj90;ig!3z!Ramy(->x+zgcmde)vx64j`tkuw1NJw+em|o-^`B z)N<n3y35DwFChD&a1bBC+Ort5bL~|p8Fgigw`GmqlYgP6& zb@z35;1lU5oP5&FsVbFy9o>~2ZGxngj=sK__~G_7we+<_yOB56f?hmCk0FDFABQWk zmhOH;NFSs-_I0SZl~_w9^e;$E;XBd!&|w#4ap<8zz5!Adq^fkAR5J41tufrArSiW` z2M@-rdeEx;=BICIvS+~FtKkI`yF=eyRUGb;lh@O2yL&z&yjiXsU7J^Bgou@duP^^C z-sq!OcTDddEHW>n_v%180y@khcPM_QjuLfj+eUx*aS5u=aJZ`rp!fB{(h^W_X=&kW zf;&OG9q=Fc{Xo3k0(U)vckt@nxFv9>ZRXr3yX5bDaAC)ni<{D3beSiM?=D$v<^&BR zDoX5*!W~0F;@}4G@N{xza?!JAA$6Td{#Rr-S+jSvM6P(3GY%( z3@>#TTz9BY{*33+Y&*?tlE_AzHzI>;T5V4oL2UwD6 z;lEA(PgZ$!{D6wG?z(>r+UbD}!XBwaljTSQ1$W zlZ2feie$NEkB6C97V2Jec&SpXWk;N@bhme_FWsK+ayWc0hl}9O=d_nQw$neyYj@<> zGqyVI`RPt?mb*OT$O-4x^m5Ct*9q?k?%0q(a7W1SHk{cXD9hYDa7SSsG?9cX4K)?a z=Y*R5u}BRx5t@00;ck3<=gj=<{LJZlXHg?``qSa_+-c6J*f4!N$Ob#09mCwt!&-AG zmIR`Aj~^4vHPp-miCW&Ao#(Y3yzR%?e*n}wE}#d%J1#F}rv!2aYZ_)*s!}v_d;0d= z+}{s?-a&-3Zu7d0n>Vg{wfHC&tD?p`p5F)T>9(Pm7kZd7#NJrD%Jl$K zSAHu(#6AQuA`r6z&@rZTa2!})2feYExpJy!QFQfKfL}@xLYl72X^NNyL%4qsubKA#=FXQP>29#w`jt;(a}!R)(sEx zGr-Z2N5_Uuc^l;5Fqgc%?{Cwnr$CQGHby(Upvc4rgPq^M+s|;fL&_E|z?y#J#$D7T zR588lS`s5d<)Xjm&V}~{?q1IB(1>tJU`+;^)vQ>N6wPWgy`y(jBEpnDa}Vk@7{fRx z%tO~ar4vKQ(g2Y_Zogyz^LW&tQpJLqGE@waN?UGeTbm|$u#fAlT)Yg3Z4YhVgi84& z9+gj~C|+)9prCey!48wlAHLbRSJ#;fbi+U2IKuPUOO3z$iC`f63#+TIDt2d5W`f@R z`?;XZhc9YW9lQvCaI+lPAlJaUCYSyMJE~G}Z2o&`#Lh-LLL` zwSW)dc8cB=KdmEz7g2!sGlSi|s8{*NYpd64lzX1u#d+T5ci&)}xx?!ssPp+;IbL_M z!(n%178bjmIpz6IXNJptVtcxi(1R|VNZ)$KQdY(tJ!R{Sy&0e?B_UqlBKf{N%#vAT zf?lv0d73&%5doaG?DdJJDsu&Q#QSWT@g?bAbNpJiSgpF{oFg;U=}?T8kdl)98PQXR z*Xeb;GJUXABtyu%O z+YGqlapuQoPY=(|26?Zhs2!{Si{Q4~xkV%@j8}@>VVZg6apmm!8uZ|8yXGEjq zb$0fqxbqN>Sp*FxTkm$t8gPp|L6Epzu;F$?O>h?4%!=XR;XmCR5B_I*Tqq<@GAm5k z)N%!~%p!Rubh?^`X@rP11cUF*+?%_0ZSEQ(q8ZEy@HTFIZR4sWJ!ll4{;8|=fV+!G zQGw&G7F#9AT7(hxi-K80rNH`do7zn{;ZWUH%OfzkGKcxI}QgF_$6v024 z3#9PR(TaJd&FF`+S&jZ59ZbOg>96kjym@Ew|7WVF+0o+W7_wWHj|M;hMD`0BD#wQjQ!Cl_t9~HSBxi` z{m2;Oac8Va4KokYT{-6~idm&(eZ~0lAU%jr)+8AyDh&+NNj^7D9z{a}f1AD)V}sto zLr5@Fg?Z5!7wNUj6R4+;Vwl-PUxy6OT^!(ak)PPi>C^K6;BIwta`uK~KSS2?UkL7c zd2@$)*OKrk!JT4vdX~Ak_~7oMTDS?;g$3(MO5$t|SVs_-#$rk1NK=Ijd3oEI@bxn2 zog1YCa0j0q&obqx@|&lb9KU%>;I5?CsCNb2y?^0O&zH*tcdK7WRoRr6#+2_A7Iuvz zDuC;f?nTgt=692!A^7eJJaQ1Ln`9+sAeWbJ2viB7>^z{#Ta}rd>07}-F1Os}#uX~E zpbK&70m4)QI-MnuJ6WKVDF0*Z>SEeD&#;%;vYmz`Eu)TobbK6AJ475T3q&taWH}O> zDvWYbS}C)-R$`QR7fXN05}-m(8bAnWK&x0rsmLZIpoE{0(u!Sdka-$6X%sSv(oM2Z zmKKzVv?Yt&ES<9V=ljk%Ho2HFU=GH{H|O#5zRwS8UCKmY7%uhDSsraJ8I&u*s%TlX zLku(f=HC9v&!oRjCW&D>+T7dJ($2>@T@yPIq%yPI1iXX$gff$pRJP2;-4?am<$~VU z{rfr9Kigy~g57PAR^@R%zr$JPABFi2Cz;hsBzPdp($>*4>;&F#@ORztaHn#Ft_j=yG4o=hORgr#rWNU%+D^kov@g5 zJ31QiIBbJm(1>hKw-b|9EUtBeLKM$9fK8ytd~|uaQwwUouut=Q0KWC0u2*WFU@%f$ zUDp|iMLZFIL{U5*;CX?NFB}YOz-wuk30Md30I>RW$Ieo00D>zQiC@lUmk7w>lv;wR znPe@m5N{2q0 zL1ys$H&aJ}v)1CE_Q!jVHXVKI2pRR=!qx3^USH_JR286ixTot92`aRx8|dg*xPiy0 zXD-d%u1ElTI_4*ORyjnBFiyyZSeMD(Iyy?E4sTO$0uLvP9I_R`3U|B|#;9qR7?29^ zlU@|t>o{=bldERI`7g49*wmSV$1u zkv0;zX?}#EGz8d{(A`ZbW!*wBijiY*w_oQ~neX169}VgxxU(oT0qqd( zT1>cuP=nbP37kN@zD%CP-e@pe#49ik7ib6Oe8?D#;zW{YqYA zJ3QvToS@*aKKWvV*mRD4gc9=lPF(#J)xO@4sVb7&?Za~OeGGP6IYZ2U=g%>vFf$MA zp0v9Idbha^?dl(YVsQz)Crld>E4aM_FPHajieTsEiVbv)M+kt}>)n@#NVkb$^u zqskSR)17;c#HEnC_^->(B8UCT>7viEwOr96{-7riiuhGcRTULbPxA)6q3VdLdeopA zQNpKBEA{%As#nHhHVjn3y&YWtb2If`iiVj-f!(n(^ImqNH?z@(=)@V9oDALGE=&ms zGY#?fI8e4YaJx3l%v@VpT>13(Xz>2vW-3MTo0soCSmHz#O#6Zls(Fo5q?qtA#49ox zj8)lKL(shU;1i&CD<<606`lnP@*3e!8-eQq>YgW^3N8{W$j^Yi3=DUR$@cU3Pc)@c zFEkxFP>VTY2M#nf!FRVqH4EV_^XCtqZd<(V#F* z0nuI48@8n|6LJ>S(aE!Z8$ygJ;u!c3lN zJ}cr?M1mN%nbSP;oOB*gz1%prjgND!`1psE;)NQ?GeSjqhERyS0_Zd7mhoWSY+4X7 z#3!7 zc7=tOKrai~vd}yUdw_XN^LT`c$=J69Q(uhF2JAk?`z7+KC%gy!m_07OTK)k?uAOx3KKa)H2BWI#%Lf#KY_HJi( zPr8&ILw4s02eg3V4b=H`Euwlu9#l3)6n|yC6842rz905SDuZ4{uSBT|S?ufu;%rn@ zB-D|@fZ%c&wXLi&0IpVK&4iMcJ*+yny9|*EpU&KFY2y=uPKO=e(PLU!u>($ajv~NL z*&J((PT_9~mPg^_>FBIpK3(an4pwTNvC1&8xriSdfb^i^QOCx@nlGfOK=u@`U-v0v z^=f@({g|pD+&x#jXHRME@mhep`!|o4)*emK?(T!x%Zp3d>;}@UWQ8mq;7uFgRQsh% z9C+D~$uzWOdd2b%3xE*rxQ%=^g?ZEO$!R9dEk!07C1jF9Q(o3GfSQ0;ZM_+6Vpv3N zCcxcdcCAM24r`Fp0l|#-#(A_!lg#tPG}|lMGHvlTiim!24|jKs|D1*Y0RXNkb^Ji- zkz=)3@VaBik9KfGwCKroyKeOK3=Vby)C~;K<_?#0n4j6xF|hD45I#(dW*1%ucHoiE zygwaHq@!ux-AOl!5sD&heQuea!ao_4ZTR`s0;d@*?J~KcTQ=Rvs1{>tj&Qe-P6M}_ zpHEMBpC(#pracFYt0=d9N80PrU)a1;C3saLraD&X~@NJ5} zV2Ie%)VORRpW#F>eDJBEp&7QU;~!0tbsY|MQxG&h~I?2ziHPh5Cyvr!3=4rg!hY`}1E|;O-?R zk;C6#J9*6lcV8cT^6rjU=C0TKPP7U^CWeU3;b>D1G5cgh^jzP>bTcY3iF#qD6j=~B zLWMM!&1GaJsN#dHIk`88x=k5_QX-_7RhE7q%ixm@=j<;qdk@UWm8#XmU|BOKl~qX> zdt&aD_uqW;{W*?=Zsp(<>9ymAc5(ZvaGCSr6J}<;K1vpZVe-2tdWv6>b(94P$FMM(^XwngGwys(SnhHSE(OU^hiJ(`)=7;;E;1GD&lk& zj}{b>)RMTGTV`XT8n1{?b+&?0x1l_sGoORs;M>VcMZAv&oKBG`>@u3oUA9pSS3$UQ z6&JakRORY2XJE$Le+T>q-nY9)I%8qKuQO2Zuj^DjsK>T zNB0EB>g&~N9qvUTV$T7&JASNo@6H3Mrqa?{bl#yE0Z{6bY__*|V<{Q05V8`&6@a+5 z_Vd_Ka4CKXVXL750TIWM40kICcgX90nL zc(NhWiwc$`&P10s?%iHmTmiI$$Knt%j#P)lve zyNz)7KgO;itG!^i~}nk+)v1YFYHmNaPuFH1D* zR5)vhAYhC#fu%%GOi$BL##j!tHW&E+ksEPZ7Ts5(58Z{;or?dX*=$_B>Jm2?T zuwDAXz3An=A4J24&+~kK5c4~`o4abI+;x4^8(2N=Y1U>89gi~2vSBzPm6u7>j+}N4s#0GAq0s@m>4}j`s=o* zH581kFPY{1S2uoacEKm4s}&SAwO zTh#{MWTYFgGBXhojrJeTQQ<-!9!-W!o*3ayCZ=-DSR4{g(Xiy}UD-*;98Z#_;V)2t ztF?^g3~e#9;!BBFJ(Zg05VG3ocq*9;CF6{!sc#*9+Pa?A6-{9s^#U-FVLgO41k5EL>f}BFusk2Q21Vi6^NWg)I4$ zgz&BTq%%OSGXZ~HC$9kMh5HHa4#J)O zSRS&sH~ZS259eCNcmv&Zu72)Li#>)pAzzsb()ZaNv8Muo#L8z{0#XbX@b zmf`Ik?;B$RcTUS@E-51`R@iG@En%j?yx%C^5!)I$#msDE906aaIM0iC8nL_6r_fmk z8h7ZFtnl_D77R(wBk6QnlbpxXX(DE64*nu`H`K*Ogsz^`Bjfap$b&x3EYZg9(rJDY z>32PS=*~$5j>+pY^Gm144YM0(f;n<>JbfzPWKK|L6J@T*o9~t{ckMh^v$jI_W|eDd z7Qo%B3U|kkAGhGHyW6lk@3p+|?$XT%EKsq5-Nw2a#XDwoOJC3PHU=|58GL?x)X100 z%F2r3VjB_5175pGLJ98b=xU*Nt*t*dRSOJv*xeB_9(|$g?wP{fTHCHL4|i)T8SdT{ z-<=?YWRYkrv;}zB-Q{XQtT+KPtn^KLSq0K~t{Gz(42PAYAtYKCdB#(Rd8l%_Nuyga zeR(pZ42N`*Nsmm%jJ`9331=-^Afz%qzAs7lXEe$B*J3l5rt$&#W?-`0GNsmY+@$k$ zYD$d=mQbZebSkUX=rD6Zmdbw?`RzDW1%a854AedP9%{$zjsWlZvzN#nef}5zyG87d z0S{nDXm=m8Okf_N9)lcC^%mF-F3wo_<_ZeTSH8A#<;oubc`M)f$#YLxOR(DKjYd7* zXnC-uJnD8=MXJkv9=AK-^)|Rq1V8x{g|8i#>#r6eG1FmW=TbC|U@&e(a2D9b2UZRu z3S0}dgC}Oq3V;zoys~;mqjDw_&#ob8Qr9_mS>#oZY zcceVv@i#iI|%I}5rR8UX-7*W>JQeJHV~POxIOQc`^(BpOM`x&r;6C!W~o8| z=rti*@{Oi#yBY0{waxU;JwZizqA_t}wx1B~#tn|S?B&k7-rh!(@oX@uDhYO)1MW7Q z0o)PP!SzPR9(t@lNg5Vgn(# z8ylOSn`XIXAA1eB=?4t&z}OfXfDR01SiXY(y9|5t=(@>Eb0LB|Y)_p|Yn}~ni4_u- zukT?grbAOQ9abffEtf~TG8`_a$&O=!=uma1?(W7Wr?cVtSS*Ps!)$I6KJx4|_cjn6 z?VC8AJHf*B$2Hf zgd#(TN)ZLi(2%tI&|h$M4`RiJI5Z4jjtn7tZ2u9ut}CCa@`o-H(8Ad&L(&7pn?9tE z;SW7RFo!h7g(Fyctac%lxZrh8a>tNt?iSn24YG@BWqD!y(V9AiyJEmybNff#8<$$6 zMgpfw3^O;r_~Q5?xU0&;-K8&XK2YlcEOyq;PAmr%q6sOnMwYUhTU%EL)M{^*Pu|QC z>@nc!xRVjDxWW#28~GxO-YsBvELGXL^W2N=V0R36_ii73Anb1OFtbwNuJ~PMKqf|v z*LLS5(+L&{?iO|-tYWRo*9)UmzGIZhsQ1VMJDcC`Wqt>+TfDpr z2;kA}OC@-)n)4`=m>qo(+9}o%y%UC4&r?i32k8dH+h@;MA+$UHV;N!EBt>Ip==J}-SX_}#upb)c-2fFj@zMg#T!Kv_qb$6MtN*88p&IzNFEL150MTbV-n z@gOQ`_8kTccp>uT}@QN%efKEI!*+M(*-&@0$yZ9x3mys`^xDX3skvJ_)d7;L4S35BoOeGy4@&k zf6rGHh!Wgwe#dnG$hoO$JqpdSyPE*Jo4qwlObj9tw#31L_vdw5Q@5$@;5xCPGrdbR zUbw*QZbO@}yKSr#;cA65cjqqfC=+b1fA-d`$G2|X`lm#RiPHfA(FtDNVSaj-OYhnU z?$8G$c#5@OnSl0-UawC7Hb&dJ~-xvJl$MGTa@!{*OJv?wZy&Ay}nJ@NS8$ z?Hp1~;VQf`c0P4!5|oW=f4C3VtQVulOr@eQ{OLMibnX}Q($_96q zu=m7aRrkoo&S5p!Nd~4X9}aCi3nnufDdaK%H|4=5PZd%X;%zZFdb43p?@i{W#S%#G zcX({ybe!m3W;&kCpv!zPlgmwJ_wAbwC&BJO@33hkcDGQK`HEypuCO`It-?E2W!hAc zD$W$4r3Q=P*dbUa1P?Krs>9UGFLd4E&K(jWwnMYQq&Lq7cII|OTlTv0Do@dbsz{U1 zLn`G|X(oi3`4OgpyL@J*QDy?}+SgjEJ3^oK?!|ET%T|TEjolwvct@aOV|UkXKfSL@ zJDxZa&aJWhbroiSxl||}(5p_IcmD>tBX)Pc`S`t?yB^qwnKrmvW7r+Hxqcwr?xpe{qtT>al+eM$+7sQ)#gifK z=c!HR;@NCSGgRh>heI;IpI@el}&_FSuVk~4#=(Q|GsWr~Ll-b(hoW!$+;<7Qs zy}6_aGUbS8`~HZ%;sCcGOj+v70W-SX^4TvwGl`&6T5|run?J)vGR(A^U76g0-PuN& zI>UUpFgr|5=HC+v{T;iz=g)y~-+K7w5v+?6y;Hm+c1K^o{E7hYE2ejM#C-ScPMH!s zqIiS&J zQa0L{q)`ejkyTJ|LMbLpB~!*#V4Ss?++QK`79>LKq+XI6g5CxRW(u9)|8|K8oUZ4W)W9;tC-`8wf2 zKZ%F2ryc>Lb}&`vz7W8sjEm&^8k zvQtsF&*uv?_>HL7H*H^Chw4PWcA*ad^E}CZRj`Utn>zjAhW|s(fOHFe;szSn$ zo?gO;p5c{q`g)qbm#Iy2_pj;1^}mq1`vq`!v9lAnd!o6SRjY&mZQY8kd=1aVdym)q zfjgbWL?8kNRr>q8CV;!{ZuV7~geNnqnLNmXg>zPSz#VTMluJ0Fn1wlGIhEouLAooJ^&X;pNLZL5lOTGSf5D^iI|e-q$qtPA4!uoxpL;r`1H6C=^SpDfxQ@Am|~kT`kb2Pk0~tEjdLJ6cz0%oZONvlho-E1;w#;A zMxL5OV_zO0nmLDO9h#ZaCzVg$day$^Nw)$bjRbLv$}gjc()06ZG^i|{w(vD`$}OaxZ5e}PPrwhyFm1!(}6vD!oe*$ z*~{C+yDcK{O7xvs%{=~tCx6El(TcklUTE+5)eULg0e7F?IR44aFWkGUX53Y8Iebi< zl_Bgl!|sT}gs#gbF{&~cPsQ2aMAVw3`(XPeyUblVu&=-w3B|1Ppn@(1ITS4H>5)VsY78?kklHth5; zq@0oM_A#N(B%Pnm{q>+6&g!|MnTEPcm#90YT~XHEznuBd*VoE6?e=>Yoc2mIK1i8WIONwD$-G0A zHrbZCXOF6ccUIJyj+;0cU*;pqxJl!%jPTL;!?uI%+OI2YzZ|u(_XlQSY@?(4I~Dn7 zHo4m7z-6bc;#7jfn-NHiC4g+Nmw@K=g}l*3#2xV^T51wwW08>0MG`PzM3MVSw-N5l zDj(M}`Dku_?>$y`edmEYbkcZ%yhk(0k-)>tcWQ2;>8jX8c$DNFaF?6M+MVK#dDl1p z*R|DLF4sh$Tj@E`%BQszUT|PbF|78%5(RZm;@;fs+O0Xl-O?P?7Rrm%x972r}A#cm+bJ5bXpT2hr2KTn{d|!UDprCOr+f* zvAnx`ae~ENm)NWzbO7oP!z!t4n~6KwOtBXe@5~fmnQKzBa8u%y_+_#)jy&A1chDRK z{2#>&>`p5q59)4lBA%aIj^~rh5^tA811}aNuo0UP(1*?P~BzuYtCKb1b>&-UCBC zMZ!ZUm~I&vM&ccv!5L*ZGw`aEGJo6Bc6~YsL-|XL&c&b=>?*YOc;mxdNlEP~5=osCMyM6oi zFz(*ijJvk|{PE6>Pg&ftw%b#;Yd3Gs?UqU=9XqRcmZ~~dc8@c+4(w6j@v*CJ*DiYS z!(9F>=Jiynz^hY%SHipFuRV#_*x!Ee!Q0}h(%#g|V*mHK4%s=l*=U5|)Ybnh}IG4~FJC$nJzU6Em7>BaGIO4yt}v0@KooW)vB2&#b? z%984~8B6l`N>FDtgJIoI$3#pxE*lGU1VSFe$}D#ajWEQx~EzhcdPI8U{VF@?xf(3r&Z>2D|d3a zrKhwv$NFw<^+eO7>XykU)jV^QHD_0NFDF1ktvP=(!Tr38G{TDG(H?Y?b|W&$2BG46yZFbiW7F``Z;!`}>GGhrx` znI72BxO@54sSMwfpam>(8Z1c1u9t+n%<#y-ndh~fIYkeg2FKV?laV^omf=ZH3wN-R z9vGRPfzjB=Fibdy#CwdsW`>6OrODj|cM_w3Gs+l&G+iET`)|NFF%O(oeX5k%%)kuWG?bm zd&4@6666k`ID{2<&*O13(L5*)nn7#d0QVovA6|?}fS%>784HKIAL$i?%wE$Ox&(H| zcveM{tj)u4d;JDIk}0Gb)*JTiM=@ zAChqiI8t!@As+q!s+>4aaxNNndKZRS0Pp!c&+~oXHgcXFTeo)aI~qnm{XEa-heTS_ zyM=Y0l_O$jC35J-W4MduY`82}Cj`7jsuVm@aES+PF4SIq}TSvywXD}vH(h;7I z@?>(1&WB-Umdyop_lFL-S>8P|GFEOcKR`lc+h9!FwjDSQyE`@q98Fi)J#hpyYgM zf=asg?%h#%@?@uGXr1XUkIKw5Ev*Q)6f+o<>1R;=cd=Nwpza_YP3FQHx5>lISa0(B z9HS#s$^`0O>Cog#o1Dr(lm0wzz)s8+Mx~ zcGJS{81VKF8%bu3;dcsm*T4POVV-0vy<;PSZM-vh2h|GbT?X_{v??o9u|o7NGrH(D zCUzA+XlrM~UBv70*Hi_F6Zr_~ynavh3NKb#)ztxSh**%{8}U7JeCG7mDv$b z?n<=Z$qEQr&ShF*;ONUusad(>y>pej3!dfB3ab;Z^2H@(YBmtJammib;C9UJxb=YE zDmi|bRp}Usg5#Y!g-Iu|yQQa=qU)~BAM&HT+(&fvx&6x`Ay33xTeGy9zHaaksr< zHh%=}yQ*rB&kML?5Kxv!xUg>ZmR(59-1X|j+1KW-%n(zrG^YVL+ipEvA>@MEFCBt>S*T89l|?b_!E2Y2zFoH_XbxaK*!S5Y{T6yma43gzPn$@ zBdn-cq%A$tHBjoh`!usVOfQj_f<+jd2GFH|-f=)Q7#~EARz4%6O~73`MeI(>ew-C; z3D?t=mDxGs@(=2k*E7ZHUANtDr=Q7hkK4AUJ7N`;s>I?@qD+P-`!b2iSOS62v0N&V zV^Hp;+Y%_EwXoaEWGo)O65M4@yZHBqQ!`nra)SyW?YjG^BUI7)x~&owra}=31S1%F z`biOyY!$f*Cxg))tQ;}QOle=;CbANa1d|Zw_0qV2FfbFSVqa9Qfh`wk zwMtdYy-(Z1%)-P>qd=8rztWpZ8w=r%NLDAaJI4kNxFhnl{QVOz5!@lbQnV^P0(T5} z1b0mCWN}x-?G^*0hE6RUU+7xs>YDFtQBDV_>+XK+#RngL=&)WMdiX`cJfeBa>*{0} zDi#LgmaBv%VQnYyZdIoYK|${r?oOUL!*GY#v7Sp3naOZ>^o#a?ezx`oaQ72|yZR*r zcN=+x+1lF5aQBD%54IlEY%a;?LaIt43V&zJinHo6$f``Se+Q$v(Oi(ROq~j*+%+s* zPS~8Lrf2|WT4bE;W-zS?(R4h|;h4PK%OrwfC36^+T2?xp;LsKXRb!}1H_oM#t(zQb zw_OLBkE@$pvsI4SveRSR%`9`%-+x(nv3GX`{)6vg-FsNyiOKvw|1&tO0P?`@AkJK< z`8EPd_pLHKgLa1Bu~g+lk*bV;!R!vpyJ{7y)$TLXp})^|FSVHV)zPRrzZo4WXJ4LC4aafo0XttXTH0i>H<) zrOuO9iC7V2URh8{ZHGmht^~8eqwlOVd6)X^SjrN0E(9`x_~lp;mbqL*l?-<-%@~7} z5LJ3?O)o8#euOgD>qC51xwD3FEfi^s5UGnaRJFDFB9Z5g(*yEEK<8>h4gRXC27^=@MJ%EZ|w9g9Nwg48iRkkg$zI+{eM(m&nNKMlDjsTYg1@FKY#wcEq`3I?ykE~ z{!<~3vEnv)(svtncjxZD#9RVOOkUbaSmx*`bScR_g)1vF{&b1hG+RFzXNp9(REC+?<7lyq z$0q42j~uZ?m;i7x2iHV0mCMJ&;kaexbAV{Tot5gf6z&Y*-Rv$cy6z>%*t&I_dI3~_X~$r06Bv^M^d0t*+n%OF(G#RKu<~sJb|Vr@R)~6> zgsE_6KH4Hpg}EJKP7uZClV)RA1a~*xV(K46nc1krOu!w;oenb#QcnPPoplU%`w8wa z&NS>!GsLjE!zi;$Mwv@E@lyPDx8Dx9BgWV>-!s38PTw*QUia7b_InBJ9_CCGHWGaB z!MSsX4-?`M$pgpZE#K>n4`7RDfY;EwJv}{)cLwgxoIH8*%()}X?rgYYcK5~6f9(0_ zqqPp&{n&xKz1fgMMWyeTDE7H`YAFq59Y)17!olFZ0I^VnSv1@nvTUKAUcyb zlS}A?!|)o2%{&~BnE|E~5Us{?r_x-*#S$?Sz`{E{*!VcDOeHVLH?3&YN=`CF@-#EG zeUtQ=Z!$4q&LGoX;&H&rLGhZa0+olE%T0WkCT7+#yDRh`{JUV5x!@F;za_rA@1=Hy zySs(JFtjSySfp~T#^!do1R3uTn)%744?q6|S`}H|ef|yO-95a&%K`4LBD*jpVwDxd z@0K#)onBO73DQP(uxpM|^(7hWUM=P{`*cZr6Z%RFBUe2vmCl)iAOJ zs=NYsH?#G?%%TkNxgAxCEh;Z`5SzP8UG8^L^IED-V|^C1_kcrl6m6S*g8l;|X*U&xw1|L0;kjE1lUiRCD;YbA77V-IOYYE=y5qhD< z93J*Jc-v}y$d2(=FY^KJ9$l@fOcpK@5!&+Qc|yBKn)_!jK6Ryknm|VsBI@4 z*_pR5(odc`K-UMFjOVVYqaWq2vZ;IF4Hc?D9;zM%Lc6KynYr{VTG%)2{dDtrVt0>X zb+>x;nxBK*RosS7x{?y++$a;gvoAS07vH^G!ZBepAoAAN$JKYEqb$?pk!B{7M60Tm zB4|k^8SXeDTH1ul-P-xYem*S%F6#8rmqtZhKkjndWaoA=Fi2}F14rHZ7f$Lqlb3q%`h2k01 z(@t|0Z<<^+_lslI&r9?e*d9Ey1eV`aU!A#?!Mv5aa*Q>;IKvx z$XqNQg06WSg%c5;eq7A$4BRQ;Jthoq`Eu58qOL*U?&co}+!3O^yhr(6OG|tAgMhme zC$YS1XQ_%wh$gsO_0`cMFC8s}JLghQa93Z~xa9X6f2ZOU;&umDt`wmA)j_)pVI^N# ziFlA+Ss^opXkIXukd0g-4+NxFSs7^{hfXuhG134<5I(Jc4Qqk9N;T=lSvWl@i@Kns zr}VygI+cz}m^4RmTKOOs=mpi7myY*MMx(imbRxjVU~&IXt0uy1Gz~w0JLafX6yBb= zA5+Z7ITw^@-$r709~U&;DYyICZ|48s9MdVgbMWm;eJ_H#=}#SY_slSXU9AzR*o(Vs z*RKh`V`irj(aP?W-vvIu2iy*Vl~lE^?+m+3U8VolLyLsdRTN4y3*qjDD$QE~cb-~* zsMh2066%FQ4gY2A{9~fN&p3Wb5ARNEt5tU8zTOXi971--Zjv(@nu|@YSpzm?lPySS zx?QV1U1~H{IW@#-!BAlQoE(S=I3plKC@2eCQg?F!PxBPZ$Gjd%l5?*JumL_q>PGmOGrLf(zO|2T!#k6Ky&Je6JVM8sh znRxaS<4qz@G1=0))_`<)cQsi{(@V?jcDRIPxC2_y?g)0fyb*8E>m|@>K`$ZP)g5uV z>@K@!TRa8^e+3>S$EV5%2L*S9^J7#kMH7M>;!5t$IzS~??`>8T zfHb)rzzRA=8FwEJcMf+pA03=Vnb>^1d2qNcnn+B|-h{7aR0No{<Av&H}SZ1)IBrLW2Fv^ zo0y2j`r%_ZF%p9!g~Ryhg_o4TKMa$+(O#k-Rul;3Ri7AZ<^cV3DSnwx7_0n9UoaO> z6>K6@cNB62Yn&ay=*ziWi<6_@y!igYE?8U}74^DS2_uxx=rto*TeH z`Pc*d3oo=679#sK&e5$f5o05<_8m%EXAd>~ZeevNc?g&9+`o~e z-9uU%=l}p907*naR65>~gGuJ<6})+nU4)4sShvCGWm0+fn8Dl@#!~O_?Cycg?j8+A7Ai7TBU3JZ_ZJXneoe$9(*5l&5AVW4 zJA4`C8`pPK)Onb5TZDL{m8Hv;KmVEQJIC#E zyPft7XHHHyylqR4yX-_}mnrG>tmNcoU4%(*byR_+Tl8FOrK~gS9V^+&GuhUdiBrlZ z%wnuusXSYBBwb}^MczX1UsiKh*AKe3Ih`o) zTrPrKFzAXzB3{kz4Y~r}h?}kfPmbmdYK}lg4)Km4sM#GJmq=A)vh@mmy!ZOX;#ZDO z-y=qK4&O}7?w_x%9U~0o-^{N7T`F?xI*Bp4xjVU+xwBwuvZCPa=+w;2WclnA=6u|P z%pV>c9Be*5INf~o`1NMAGVetbNCAqz9Ys_SmdUYYH;G*YR%_NyM)SGTi80+Q4m8F` zx|o<9Cnk!U3RqXLvrbkISfMi52_rFNyykO?UO|4M9R1I^+33!?TerTtb-lUy`s?q! zQ@nBS*1a3o!ft2TVoqB%^Z5l@!q$^DBXf_H0+WYyqmpCZJIXl&C!xjU?jTIWIMw`Q zL)GYbVP4gnCbL<$B(oYHsxV8*@Da~&F7I@kl@!D91rFk7t5RjEA)d%v#8su`kFk;7 ziIGu4T`WY<>-T5TDR^%KbTx23s~jcT!6~z$vH|fx^!`%S8zS7bSdxyJ=Cl`{lc<1~ z77pxItx{14I`7yd`apI2x%fc4h%lj}0KP1gw!awztMR zknH+RJdV8sTJv(>q+LR z(oDlUvwr*G`hENAuhj2(mph-Z7gMvl^?LS?s_wq?uRlLya`#|?VW#vl4a}*sZaSI* zI&~X+@W>e6saB>YLKUW6TnAqG3lU}t@bv1AX~(b&qsAlLWpXpKVOxV3if!o~TCvQ) z+{$Dvr~hE(|DoM?4@KO*NYLf&c1GMDdoYmU_GP%dcDLQ(Dk}>{G98-7qd79&4o^68 z&7_>7jP|51Ibp?}X=!P?MQPD($zV?Q%}leh1sFYS>DjI{weBVB1&l1&uu4-#RA$3v zF-_Mo@LS?in}lzh1b0o?$P*L9G)w8(^lO?gL(?MYUIyGw%^3^^eLjL8edqGIw4BVQ zp>Vj&hjPzHA1)`AcXqE{-RTp&t?2x{$HvZ(s??6p%i0~4RKw6h=2k%Wzq6o1MXMG} zP9yzia%yUlZc{T;vw#$4cU03IZKmpOubSO;e)yMr^A~T5#1lK~`KaqAYh^iH?ew9# z6JHg&%#6=pOpGO_fIGcunjbCiZ0-c$>ac&nysN9IsH>YU&qo?wej-0wJ2g+ZONdm3 z{*qlA>2F)Rc;!m&WiDH`Se;&=8spH3^NhQbXlKGzrIIV9O3o~YHCJT~fLBw4MRNj6 zFQl7vnl)FLRrRJc2_@rI&+t*2O3d)o|A?ePr)_~SH#Pj%mOS&3PPm*i=@!v=p)D=E zcNdG5gt)vL3He7*&yDu7w;8?ChLMSp3G5)C813)nXujBZtg^+z@vdpgB3iZ(#9K?J zopDKKA}E{a#=gGBQv*GXJ$;S1+t}ArjRaQuAzF&aTJ5PmHPBbhx{}5oHHDK#V-Lsn z&_hmDBd-SltUy!0le+2yB3VIt0y1Mc)rW(9=+=WSv$7b><+XnP(pAxVwM)&Yja=KZ!fVyX=D-5TH)D+oNwF zY`-K9(Ol>Od1}#|zJBn*?cO};VwSKerav?hlOCn?DmlJf8kOLoD##S%vWyi3Xk5^{ z^y_wEp%DOW^k?V&Jp6-0G+ZSu7MyWkTLRiGTAmEKTlsB; znJ+Fs7IbWJ1>G4gpEH9BJG;|PbzR_3O|Bfzi82J}*zIK9l_K;h-p~j%!S@uCT7E~+1rnwknGItVDvc(zQmh<*}p6C0%eYt5ady(3d`a+|=pMIX_ z^P{7Y!LG3p4LnHhYLt;=IGn*SbCTii(EdY@*od$nO|Rplqoa>!M{nM2l{H*oowANM zDt7@lniF(pfOepFJk4ad%Y?%+$Xs_buY!^7o(J4`d%+qdpE*SlhMNBFj3EgJ|3?eH8%KjdN}{DjpfFc`4-U&TU;S-h@J zrK&LaZQuSo`Xy*b@Mt*B^Yl*OZs3GY@7~YDoj4Ir0PZl(1l-;K&3{aAXF|JIDhck^ z5XBpm?Uyj5izo-j02$q6$TLh($RI~g_v!X@={WaC zVi(a}w|9p!)A6FNTlKQ@!K%{F(D6!-{DMcBP^r`{(mkcki-J z<%UWSdo=gAx%?P&juCFoKsp2U40iXyP|MdXI@CGMqfC~oeB~9}-91)j-n)N}+1X@`FcB{oU69ULu9Db=_vJTrS8(& zjbmeF8%xWyhBBX4syRGvt<2ppogFk9$n-XGLz}|8AaFCalS5uu1?v(-QCDOMjTKs~ zND2!LT5JZ~ae%aCx){6>7hmo`gMx(s(lVCiE^J(th1A)KES6C5^cNmGA*Qv^ZV63? z=qkpuH2Yw-$?5i$H#9jlSGm_&zLC>1TZrnp+^!av=5`Een)ib-hvuy&Sa+6JyQ-@R z4=qrtn2zS=H`|(Tpr-E7`L4j^?6oJenM?+&yVh}bwzXn#h&@?{>UUKUVu5!8owph8 zb`ji#!zQ?UZ$I5T*4ldY>Yc~P5o^>FzaUy!7f>pcFmt{z7bC!Vcjpe~q8Bn5B6*!$ zr!L4CfT~DYLHT-m0zV(YoqvLVNCH0q zcu%w^(U*!|FDhn_xn!#>UeNBHHz6zlyiG>5%3;;1dImYqXO0{}wQ4^%x6$<|}6zO|&JWQ7bg0d;E(c6a5_~ zA}&#uiqi#Fz3G6isi*f?S)y*Ugt8XB@D&uT$THoKt&oeIeXzhJ*|S){8M#@e&|Yj0 z*^xkIv7G+LwX6kKNV}Eb&c>~sh4h4JpR3jhYq^`ao6oUlb-7d1d|JzpkH}rE2i4}z z>Y*VYa#qUu&|I~pPV+Dm0b=XdH*enj=mwFy{l~@|0e5$1q0MY0Od%3A+SS-9Hh_+G z9ZbzSI6@4x45bO-eSo_Q#O}_+XMPB2WL)ri=ooTP=AO*m%6<`@rBHVyVvf{?vvCNpeyHfLa4$X1a{le=Xk4L9LtS&^Yee43_vV{ zIs_B|F>~GA47|hcPPzeXR+0T&j>oXOV|o;grg&5tL+wu#^2{jv5aRJfw5KPAWf< zj|S7JsYq@@xLpVt9G_g&3n>mP8|H)62BWUyaE7HF8%9)Ebmmk zs~8ckefjVbc4^4EBy!h&;?EBh+-=>uW&8Udf4l(hB#XKdlT83#N#$~Od(%e=0pE%> z-_;9&!0htLD$MR)saUmP>*<4pcIaNfN$a+3S8(^IzX;qN9C)gfnb#g(`upz({%VFh zlM`Xh?z+lKg1fd{cDECF*ORWvrGt@lBp77uf>JZbOwUj3ELfF` zr~4#GO!8LhRm)vpB*%0RbwDtT6Gw|&o?y)-Lmr-06PdY{7_RU0*Hl4sRTUh5d*=A@ z%hBLOe0V0FiuDYqnAAO!Zu5G7E*h_yfA(y?M$8l4O0S~!b?}=T9e3=)!(-?K+WXm# z9cX~)Cb(O&WcdQmT?sy3q{-ALnOANIw|hkFPN#QtZ!T<%FwK}J><+iyuYCDI<^r&* z!i}H!9nUer@XkGDcIQ?6ZVG}G-rgZPQ}8Yo%Pw8|>Y~>c8ErrRhfam@?)7#}E7vrq zmyiKn0WB?FkJndQu6g&AdKyZdo>Gs;>2kPSo{t)uLaZjYD*1@?vPwu;flm1Jz+YTQ zalPUp6*>#7_6jSZB3*6<_k3h6EYw#xgkae(vY%%yEM5rNERv>av1je}Q1)~&E}M%B zibC1X$C|R7qiCscY49RUPt(QIVzbNJ^`g+=V1u*PS?+6bR#&?m^a`-Ob80QVA(zL| zQeCapR{QAn>}l{4z0<0}?X>b*C^L0(*Y;j>+na4|&6}Is)b8&5C<44P8A2}%Ge^Pc zB$}BaC!oT_@K}ITFVTBG`()SMOnCAX!QHX_hu$T)Yvz;9b)GtPbrb<9k_Y|HzT1zV z-0j4^@9~{ZqGSYg1elKjnVobRl(gQNf0ALi>*PQc8F&iLsnuZ@OEcMXCq9LnpoT0} zxiHyviUXn%AtoM+^*>k&ddKXJ4Q@i48SeUGN{<3QM=TCwO|BZ~ff0}Onj8=vmq%kU zhP&hFyFju2CED$7`^veMLd zM`-seo@5?A`{@tS-^y8)EVbmjQm}gRzl>cCOw{)oX7vzJuFlfy4X$?w*Bu`T5LQDI zjUjD98>X&iRx_kzGqj4P+mua?qiX60$0#3q9-b(es=0Z4M@D$w3FDJN@iJ9d>sxlw$gc0G@gX5Fw*{-=s z33rK76@t6d=P=6@mn+%b8g8u#fiH+g1e2^mBrl_>AVZ@4sVMz zAk=Qg{;sy&jSO-aJ1bc|SqhXxQ$b;AA-Y%Xm9pcY(w=uJQGvM{Q^`D*wiJpS=u>&E z{wa~9qSU(Pt#*G9bQB7r2k}d=`NX~xzu4E>x^>7A3i`O9BqWQ0h~5#$bTpruIyIH& z;HVTPrBbPaRtMShqv|t1R072$AYyk1hV#<=S>{U$yIX*HH`VRk&3`esBd|j@^dm{| zl<{Sp;4!-k$JyO~vIya<40DEs#O~SeaLVfk(gEgxb!Gi!{kRaA7sB#Rc+H_!$?-W| zRl-~T;)?HDVE(n;T|-YviPczLU@%!tfCqhsxyWel>Cxw8WtNrcv&u?{8xG~(OGmlz#V;PPl>vsE)#nQ zq02NH3Asy5B_(ueEzq;$uE&^DWHr3=PF9iL3_#L(C&>2I>7Bd zI&|smc7nTm;c&PO(@f!Ng9o8eC?ny$lhedTnDKVp0dTd=Y`A|JaChkt9UTOB%9%Yk`xfjqI&pxuu26t~;ygHb-@baGQ4 z0p3ub)9EI-bNrYM2wJvZ0PbFxzps;B=5M{swWtYjhv1XCE_AAPp1FWV0@=h0c6a6i zXM~;UhcBdS9e5rOJhLU*i8qlf=$+0Y;4b!flHLfL5w;z_9cOn9 z3)tPm)3MQc!^}9`f!$^2+icg5{u-2S3tw!J3QZXz_9GzP=2}*)3_0`inp=Z;T+j+< zT`B0Ay^sUN;DQk*1f>I3k+4WBLLry4a(_^%v#;>0N@90NNZ>OuI>$bz-5Io(0#7ST z{r<7O6E{BJ6>uDFwwL;``#(^Zcg*eJyZdH-w^Om0Gr6OGU&hULJ5&=w_)$1vl$prx z>IK*Zg-B%~;1RwkE6BJ=JFRbe=jG7;qnrMXMc-F0puZo~CFjT|vD`-_}^X(HsJ{5)+yb3aloAZUQoeID!=VG+i`Xz zJo6`R0Ycgs5itxf=?rqX63)9ea64SXi9qN3#1t<-n7S9noKx^SW|J~1Q--UEtF@vj zfg>})?rt3!5xYC$mkf5O{$V4+b81PiWOp|lPQV@KVRGXE(IYq3cIX4*Q6^{;UAoe%UCdZb>q##8`#|Ea1S99&5b(R%2B@xcX4H=jE8<+X*5@4nekS0 zVKj$Iq=mClgffMNB!`c6$vhM6?OJ5ASDgq5`wbFP-W6cU{XMe#u0{02a@v?Ic&$5d zSm~}4M@zyXlTsKL4;=38Qtoq%SAbno<F4ELDl8s=PeUY^PW*l5+s4xkELCA) zCQ?VL!N7k z&BhMqA^QcX5*bE5N zP{FZUXfgAe-F6WWCL0js?v8I{V>n@CbD*^~(B2;C>kHuD5Ir$n1=?%dceMw8xw$qW z&ZYhHkGZ>5Htv0hd*KnawS8{7edyyp2g@<##Zg|&sYO2uulv={10QVNx+{>#^Z0)Z zcZ-(`tcwHQzyJDe2LMk{yGOjbdz4@?S3yq2xyly#;6%ExHIYp4{=VTR?8I1aZ zB12hmNxh}1N1tIT&}WpCc|DnCeUZr(sgKGz6<15ADj&#YJ!^_aBOG$lO-vu`lQqJU zt_nG^rXg6=4G(y{5lV?$iM1fP{9BI zAOJ~3K~#%$iqtm^xP~JHj;?ywaCF!;T%YNR3`Zl8$Z*8t^?E$sXcGz!tf;li)K{0_ zFlXqkh*lxWXSG%v3QW}oD*>Sq3RI)1z^I3$B?(B!=iNIiYHBKKDqgEtzq^CD-KDcf z)&lO}fJ3a;N(MVNcOSv>tZ{8)BPf?BzFW6)-GkBT@ctdbEq8SXLNh-C+yU?27W=zP zBNwOta*?}PM@B|2Uc_uN9xB$hUu3GV>6~5@G;M0;_XKzMAypCi(0B`+r=nn;3U+k5 zMc231UcDNo$6da7@sB*r#OuR9|H>;c782rp|2b(JSRz+<-@D;(OpPL!#L>)l11EbA zh$dm836SHVpaUB+AJm#~<2jm@-JSTYkZE6{qfVpzZpd{mYf<8qEuj`e+Uz8sQyna7 zthfX{X)`}D6!iPX#)#bce4*gjm_5jI&B{u@&*|WIO+fAB*cZFAuT}g!UlF@o#RhY! zNYyII(D7iExHCazdvC>y5e}<`sw@#flXw@)BNO7jT10KDP+T4micD6S2&qb!QdZs~ zyI|`C63WcX1=(RNNf7~^bqShG(d5e6WDIul5&Re-%zXE!UpF8q%1pVhe11`yZR0tH zyL<)i%ICqIlB&`m;cjvsyW_~r@wq{sW#$vw**=+jGXBLZ;EteY?-k)3hgb!j2d~;< zPh)FV@T!ox9S7z1z6= z%HRJc;O=-L+_5tA&-BHf@dUVAwoJjhT$}BGjGcc>lldLTiHH0caHm6U`aJ2=ir2~| zwE-_RcV;1&K(ff9iHv}ZBhCU(5Ca_jp#*hPUVTuDa#!hM#6a_&~dQjTV!v0}wdOI>{hOpoH|2EZ3Oyxi({#BI%_? z%+aVV8pjP}NmRgtStnYESn_LquBvx9o;NYIMb4ze_~gXiNuR|a)wrS?&&j!_T-j%@ zkz9p^-nfv>GG18YO-%HLycuIs-dI{Pi;qu3df&o=&*58e_$KF_4#zE@Z$_^ySPed( z&+L#~7TLbwvzb8E0nlWf;sD0kjW#R-a!_8TVpkkK*#OKlXJsj7E9pNyW`!c|0CxCR zatvVit6jUkC?VrOcWXJ|Zhi@A7r`hLq75FGbq}9DcpC23J!m<|6$yA6S8#9|&)6c* zoO7eVtI=URyQA`*J-Y~ar!Y5jdiDy!t{ZW;TQs|30Cfeax{A6;1=S)%>LQrC@|S1x zbA&tmUV)k;hT&nSsm8n<1I5S(p|+ckl{Ia4&NU@@@|(JW_XR zPzmYiJI!}qIooh21?}4YFuCri{;q+QUOV;zVJ$*9%=;{$^1^^SVMM^g*tB@Yoru50 z166jG(4=!MW$vimcd_iuL^Y0u38!uv%XU7Zw&{5dSSR%i({Y( ze|Q2OVqO?v7(s}UVx=JV;0t%!8@syv&G;N_M8N4cTM&d1AyM$|Jx%kih!W$~%zh8W zh6QMlIS`=nB_@C21d>Y+a8ya%bl(j0W9C=?1wVQ5|Ih?X>i; z0s5}!Pr!jE)*PUn?lLvT4qz@B`|jZGWpNXMV&w1Z??=-@tPJ5bFM`-o;a2k)?zXcP zK*Jq1qN=#teq!d|AEt(^FE!kS)J6p7%zrL^NwnMi(XYR}`*Lz};m4;T%o^*thWTLs zxcXb%R-wX!-#vfIU9#Br3d~ubvLOXXw+3%1DeK`2siJB&eRKL7xJyY(VB7)iUOsyH zv^u>uIJ*#M~J)hRk%~tUG>(@TdRK=t=f;yyZu92lPe1>q3`k;ckZ^G zz_&S0op1eZ^L`!D3`FJ{pY7@JNVq2Wy%697yuKBP15O% zq<9pg&SKFU6^9Oll1?&71|=HujVGnCGaxC^>lHQxIsii8ZA^=$iP3-bMd43UMp>NJ z=*e3p8G5^9kCsV=-dua3Nmg==vdf#1l$JJTF_?4?y*Dn}`)Q3L*&X9)WnO&qx*R^m zZnw&2&}+V#(712LARA;Xt+UGx^O(2B=KvjNwBgE)8PDrb-YHgqo`OMQ2t1x^g7~xQ zYb5xR$zqjlCY>$r3&I^9?U*0I0(!GmDhqojv~dl259!yEaEOwM^1OqW&UyN&DQean<(&hqtq13E3K`q zS5V%Kw#-d0VfI<;lPPLMsNaLyZp)URt%DDYyJR-=3*6mq|GIQf~~5vKFJ6qy>ku z-BR1o6fM570Xq6t%5JyUj`)K?zdslT+6A%it~Mvw=AQ^sY9?s8_O>8;C>qnS3;i^i9s8#LU+v(x-FkBMx zP=Z(ot*QX;2B_H1`G51 zft8~A;E;DI>rw!GDH}w$9WrmT_jXyv>R~4Q5y0K}*g3%6qnVjUAn#~b<=$WM@D5YN z2zOWj-la5a92%b>YBLYC*O*=19cbOkD-}pyF0165wH^BYz%pXJAYXVUFOb-3E`-#4dUmVotU9H5zL|el*~+jxQncKTx}4fWlta$f)1n|e1J!w z>zWQ!HWe4+G!wL3em>9_ekh+T@7eSYKggtXBk-y!4=shruA@;)KS)*y?Ak~WJmqtp=0M? zoIii+Z-aw3AGSo6rd#H4lvyC`aqKMTqq5Tn3up|f_Ontc9pG-Mr2>bQH;0FxOp&&$ z$6S@c?o+3_uYA8Wcc0T!;IAB|_kuZQ9RB&1*|`cDXGU74F_Ub1{@I@qcT}LDIq%9T zI1B?kk$fU8nx3(|9B4N^D>gH6SEc^=`NiYA2zLNHfjc(u$BWj$I4|NZO!hjW8u`g7 zh7zS7)+2!IYiHv*Wvih0^Upg=8$P46J6=8#$#HBh=d-tX)mtYlqp{^g#FS7#Xyh161FC?OMIEA{JFd-yIGU1DzMJOrIUHtIpCH^Ntis*)YQSAW z$ohzIS53H^7X?5IcYCTiAoCX|K-}G$e5ucUdT#97i`TG-4uN;|>gU(K!t3`Jzx9HS zOG;kD&p@Vxt?HHv84^(UrKYE*d(Y*L-QJv@x*B(>fV+^3a5wpX!p=XY={t?%Bu52? zMmNP?+v^X}Z^eu@RnvcnLA=1?sew#nLM6G&H`&H={y0u2KaAO_2Scz5xc&hW!?J}c zaTORdn4ANJ%Uuj65_4QbE{hk~G_sMAi&01J$Mb%k&-dHv@YZR?wv?JIef9l$p7;Ab z{q)A$y9sw6{he|5w5r75;>)pA&nD2mOQJ z`j>-uigxsC_0M0ns=^1E0z4LXU&~=8Yq+Y}7iM;hI{G~;>W;4)I6y^PWOm2mj>mV< zccSbbTu?3qMqQy{$9r(0am@H@4fwV8MsH7QYF4EsU2+!9?9BA6?2MFjGwB|0O=;;pJ?yQ+Cw%c3 zs}J#6mMr>soigEe>w)%mPtaOHi0s(iS%}uW< z2&m0_8#UcI@2TvuM{OP?pXpJenx;qF$Mk5fi}f5yK5Upm;02v%y~E_rQ{WBK-|@9- zMzDD`m+lU_ZAAB!tpvKQIL>@`?TUAI{q8VY1GcTjddbQ2wNh}V!cP$xau{joWw^$rBB~h{=(3u+#+Y)V&#|xm-q!=4upD zRfdO#M(Kh8=-#1PoNC77y$!wb*4|h{q=}O=5&VMgzRQ;*=)G%b?eG!qEGno4EVca1 ztaMEp*8SpnAHu$UiQLR)BzED%u(R{CtH}Cl?!3Z7Gl~4d9nGDcr+9WJSu0p&p<7Pj zTi_w@B;-yL*#2P2UiJzxG=4-bxbgB-aE5uL;~OO~T#x>z9lzh`KwvMd1##JF;+% zZs4w;Ef-1LjZZHGyx+E8ym;*qH}fdoU1!{#xHgtWpRh7BxlDGqR3jYYqfRqdY|}dW z!LRq!Jof&uA(?mF@(SG5c%S7iOixXH^WDdUJGHCwXM|>AU`V(-GESG0FTdM<;@S9A z$@I~r0-#L8*RWpUZusC^FdeW&65Q2uR?X*`&+qU_u@p`-VV$F|<8dUV z_}GQ#V>5Y3s_Wu$x*La`VnN9B<&0Jj?k~e(W*K9SFJT7Wp+kGaW&8J6Z!8+zScLAq z3cA7%E@E*fxU1%qOvtC=aPds<$sw6@mQoAT`dA6ep-!LoX@?+*KzYQ9JJ2<~`E zQPGan-Ri9`T~+y)I?GgxnX_Ndinb%z3E*aVZZ~W24#Z=1H>-g6@5R*Jhb-&xXWoZu zekXgGyr@Fz?!to?E)%Z^;xZX}nX3%=3I2=;-cOxv0(V+K*K9DV1+77Ush0@h40v2t ztM2f)-A<3y;jntFx+~YO+k4Vdv$AA;AS0>Q$Aa2|H2y@c0>e@)V|UWf@QHpp<2?&z zgsibU?GruAvc{qXHfv?}fX80wrJKguNvG-UJ$8Em$x6F3Fwj`)_4ost&0A_Ov=(}t zM78<$I}SY%^-PSp=A*$}T?;snYwZl`HH|I@z5XC!&Xt?%@OYgLR2Nwtr1I!_wVE0| zn5#vjnk$-XcWPD_);9}EJ#LNktQYF8yqu3S%gf8@8mN<9O5kn;Mg)}2Dct>WhvbG~ zMUo+R{Hx;P2a}RlThkKFj0O?Oy+BI~LbzZtZ*7TSHB$n*NZqv%Nm%u<5kYWgv7{Px ztWtevI_)xyJrpE)o47Ob!*MchNm@-2lO4C^Wtb3EFEdSWcX_CmUtlX*?`lWcgV#%n zZZsByBaoeg9F-Y~5bo%o6NvzK!zmUcjt(Qn=QUjt?&RQ4m}g+`&3&>$DZO)G*eO}W zRPCy2f08|V?;Cnh)=N}!7#`YeY?~OYd*4VLYcrQDRgPF2(Xy@C)POjlWMjHf?%JHF zc1`^JgaJWmxumh3xvpZ&?g)1$l5zK(x>LB@PPqGqaTis%+j3N?I}%X~T_=uiz^>(* zJ=Y-a=AS+*XdJlMbx)$3(KLQt&NJzG>ONT`ZF8H{&)aRJX|7|bM`EpO(^AY0EAI?U zY+1L`JkR78gu~4C0$Z+=aQEf(qb?l29=lGsJECw$>h9jO;BM;P_Kz2SjJxO6-HG9Y z9HUZnuKr-cvCX&>!}&SUSaGiK>Fo`LVxgS;+I)_)tj)(-rj#Y*#hcdX?5$48ZW7+i8PB?$}=*O zI_zMR%TNn;%T!OfB`ejEEg|@Lf62~HlN?1$&%^`?#LAulZQh=>tY>1(?x`85oG8?6 zHqF!Sb~;PFe!p(LQ)(;pqj=Bh@oJja-C1pppytwad#Tll zpcKtTuhrrAdL1sOz0}6F2zpSD>VCJwl}mrsU3RaF^qt$KxeID)3Jd6m+0EGVO*-x+m z?TUGLhh3G~CpgXA!6};~Ea$M1$^Y&6N;WX15QwejXJ+Q^v%149uWEKQu5^{jo%YMf-O-ZK zNQ3wcU@vcx)s#pB9|r;+4NcN}$DMejnP5ctkD>0Ai9A(hL#HPUD@N^WI@jx^WhAAF z8-qUiUrFjPuuEb#ysDeU#mT8EzrH*)gg{S1-q27e)*FkB#%p1hh>ap5vuP9=Fr%#; zr!qX;5E&+o*U}Mcx|?E2S4f8C(-_?e^A>gC8Nn(z)MOC`ERo2<#t7$psmQNOoD;^t zYUW)_t}s`6th1S=A9UN*OKcdpbfxW-3eJ>vSeWz@v19UE{n4p5E@dY)5ltrdU6Q3t z&M(8JXxkrD55gNiUd}X1Uge*iwnX7>slpw}yG7L9(M@{@cdH0@Pl3BVTlO3^)LqH= z)2_Nb8$iqrb>GfUP0cSndj@gW_2{ppxxhQZ9cFhV+t#mN%IyoAj@`I%<0Ern6Q;bC z{5G^?>8mSOio1O6iX|&ItTX$6Pyn=Y%^Kj&o9l7~=LxLeU&FV*orJp+b#=+O>zW^* z2JRBRyQEYVg}b-VamTvuAO}N3(4j#dHs>U{A~ULLevC={>F ziS>pgD9pzu1Rs}>=dhVPNBPWiM#Io?02^4hv52`BCc#IvQ-W_}5sVSgcK9{O4SYrF zOp`6|;Y0K-WjNW~yI1K^W6wr>b!vo%vrJ6w>Wj`vyE!mdJt+NmH`!+Xs%&noX4sLq zTTI=(XG**OQFi_@QQmhP2YYa!5OuV@+&z8nxhh97K3=Z6OIAY&P0pqfJ2TNu!DQyL zP?9A$x+@S35ycbyfnY5jdd!m;rCj;35jFgyP;s$YV5w2B3>V_6MKh*>G|ttAWuN!w z`+Xh`w8=c~2gq~BU4r-G{rT~J8_m1PZ|^YfgqzBwoK%?|R58Wf^-0oo-^y7P$IQ&k z3)P)*Wqb~-d3Y+bdmiifbbcX=yQPa>HCuOvPv*;|nQNMtG?l8Lc&KR5S5j&nKB)^|Pk-)-);J;GV(``&_BdEOWShE)UYjeXb&#;**QA`P9NM;ynrLAR$x(a>#7p)_zMbL z4n4`F9m%^7SAGaO{oEL&yh!Sf=T1c49Z0jEUA%<5%IAe^WFd16rYqNo<{dmU;};P| zAaEyX2izg!b@0jkc;WhZoC}Y@UCsD-jW83dxjW8UkM&&*79j4Dw0nXybBwMWDI--_ ziV&jGqlIfQn7O{R@aUf|7Q!-aX=8Z6GA=1gu4^g6xqBJy9k1Jayrv2 z1PFFnED7m%7)e1NZ8iYNh{2HVD;7}%wMd6(-n%1pSKW_t<i{#=5?+e+SgPCo^6VIEOY}6iqu0-8l!_ zFLffI~_u5ZXy409$J~5&P1b+ zOZmJ|bctrqjfLH;PXmMfwQU`md%W_5A(sHj?U_+2qutH5MMMEBr8#J&zS zR{Uz`&VzEw62ybT<8tPf3Mu*k-}3Tu(MjBPVeS$j%9;mpXPg^%G#Wwd3_LlkVyL-Y zyM%*SlM#-jQ& z)vjoo&8_)!>~>uzTp;OO?NICve^HL+E7)#Lk?)V`bG8o5C?IQ6y4>c@ zcf+L2Ob)nCvRbS+^u8OP*m7R9#m+Hr;0P(<@QDL_x)^Vmy;T>QYehbgEu#p9kQ& zqAK)5>F%1VNLQ114MFVQ{ch`ql^aNoZ4kxG9MwxS>}0M& zO&Q0(UVr%T>eHw9dGC&piy_dXXM~d5Pm&J%?fcJM#E=f|9-jaJAOJ~3K~yN??kJ=k z*E@l`IH|jRT+7_I2e@0#7SWJ*3pt%xU5jb^WfU-4uF4)=%+b>L9V(7A!&BQQ=84RDQ{rDJIhyhBG zzRHuCba*R!fAM5VBq3O#?o=zm0}mPr2ZK>|CxiPK;$4GbG0oXqdn~+>zlgT-Sg0K2 z48onPRo+Iz8#hJ|iCsRC!Cq4G(X5e}A@$y1=tlld+M9Zm)qXY=6a(gIXNwzpTOcQ!&!x%%$ppPe*Q+EeR_JjqXS*S zN9~o|yi3I0-b)k1tnThz+H`YxY##1X=czmH-LXU}=ic2m(^N&uyKOSwH9tR}w+{$< zvC&+%56k5^AOyLtBAoR!b1`!j@1Y|J4-T2tqiaPxE0ST0b7PI@)5$*E&oE-SPITld zwn+Gae{a)sDR26vly@j@%Gf;~60HC&ajDq(+Z|GtBIp3&2x8%mH_v$)<yKaYtW@ z7NuA-e|>R=v2y*o?A^VnnE5P0+i{9H>X~=YyPK3b2xZ*4qpLE5djF8*n=~g?o;~X; zo_T)VPA-`9d?tT_56>LD_0Cgxf0M&1iy-k53z><)d-eaooz3Oc6ixL70yfe|cG6WI zuPa9(G3OzYcu5)gwH!6zC{U}GSu+#fnZzcYRb1qm8!QC3WtmnnpknwYX5w9Jq%S3j zr(9l3S=Q%pDNdKasEAag>P8x!T~$f3VU$vLs}3(A%;^g_3yL(i-D}qa9q_?aRl-$= zp5qP>xpacFPt{e`2tyg)-J7CYS1IdHf+WGFkBtsvmMx##gPaF>}WA1%dVN-`tVKVY+8vy5|vrfX(} z#{osymk)Jz9*zyQz!`&ZmzIe~7KsSJ-H**?GVk6qc_%j!y28Ei`ig|RyGV%jhT+nT z^}7db){N;&`hK*R)LmU~1b$y#5ixSv1-*vD;rkVhuN){CQ{R>RVt(wM#GO&h z%tE&7i`3mL?lzgYGkbR^W{y3QlPaQ?IU#(;_O^Y0ZI z$;k^AF2TFf;w)H0XJ=<+Eaf>8`VPnUCMOf|vWSuf?vVboA}=rREa8r*I*k>BAKEHc zS8m!p2Y2n4CWN}X*H-yx_};yZ6Pr^ro(EpGfxAD*p4~1Ha@WKQ2(n5jO-|@ijs1dr zMjUagnv)Q6`$w@}z_CB8)>emNoK|qMry*ZRy0s@y7LOXa2t30XYpgI%>ndjTo!Q3| zn|E)o+g9Er`gcamY(-;*Av!M*E)O1TywF5KamSV>4kyR8EN1Td!WdeiS0W0R=JtUo zcCy<0yRm=97s0!^>h9ljhE&8*CZkShyUEGK#REg!NomKdL+|dpdCj|i3A^fsJl6bud!zz$8hsbZa2c!33fiGTXp@9v2%-wGC#vOB9mbhx9h2sZ@$ia zBk>y{ogo8eZU`}B!!#7!-DE*`rCv>1oQsV|MX8;~YZbZ=Uz?JidTU7#^^r@sOD$ z8j{WB8^@vgG2WwxpgHe$MrN!Y79&4I04%uJ^zpH0n8 z-}s7anMBOpGAe>%CaxbQ-o1Kq5895m>r(02x$n|X0JK0$ztVG0T2h> z3=YD>7j3&i1iYg7iiN=m_Acdq3{%*=S^CNF#EGuv3>Wi>u06Olrys!ii%i6q1f9 zUls1noz0ktbxYDKx8}HccM7Iqm-SGK7>Ma~p+6{pvu@Dtmtw2(bH_&PC!1CoiLK4x zZgU2A&8rs?KIXK#7lgZ8%W>z|>h7iBZd+OF509pvF1@^SrM0!W_0DVT<<&p>7t?M# z(XP;`CK9~ZB`i<2$;D!Y*X>{%#@QU063)%e`*p=;HDQvs8LTTS>+gFITwEaBEz*WyB-OUDdnh z6B*b!!Y)HP1|3*;CPKU$;h%rJQPiZ{hIm*-HS027DqG%J*lBg=k9tk7ZTN!0kY)P9 zv0&V51p`Jjkn|V%qITG@+Hd)+#`0jOI-h8jzt!#5jXSoe@6XBdXrbrf00j5e{tdbK z22YR8$x#6bE|2TMtt^$Okd>8{Z&=Z&Kj^jmAtP! zn539x6L9T7zyzZFJ|de*aI?+!o^p!-Ns7)SAxB)a8+&X!N}z)UbRdb~@?^k2;H?hE zjOuu`9f*?NqZf%1B2zGg7~$?75)oP^aRh{RR{km_%{s=N zcs8@t6K#hk+^ea%dpEe4dHm$fi_AKCLRe^`d-vqk__)krwxnBTXP-TrolT?lw-dcN zOlyji!#2VsXS|B53V4_rujH~SMrA5h1`xAXuSit#n@Dp2kSb;#|L{{(X_4-F@Sx7S zQ=zQ-FZs<_=fXlq8_ztle;C6Ky*wUH>0lEv)=fC;z1Ay<2&IBMmul3i5IwEwCDAuW zU)EohRP<1;v$*0EI9*~!Cdy8Eh^d?ew=W;MB@C*@m7?xp!9TRu&qMe7egAPuh zgC6J)+>N=5a#R^X0Z#LjAmOc{lgEpeWYRI`#ARZb4< z+268JW;eTSc54$cb_G)-lx)~?;?ya}6in>E^wn-fK$*krb^=|Ou~B1BGW7U~v4F7J zFmux?C98^=L>$81$LO#T?)u(PcO?xw{{EsNZw2m_w7PrPT*F&;HMf?grWRj6yz=|z zzP@JcBmj3Wu2fVsl#uo-)8Pi|*B3UoHkZ+htRV@PovkJ>vppUXbbP-mGd;-vzF09zJ(L@s5pE zBnH7*Isc7{MOb!nK{A3^-Q@}E_`iQb9cEQNc*BkQEy-C{}Eegc!0F+_Svd!qX{oW*tJ3<~7@B+NFX9j&1QPfvtaMCLl(7ir` zp4?v@=S2iXJi^`XJs%!~wgc{NUcAb^@xUD3p&j_6Jw`6F}Vw;om6-Rb6H(LYMgG56JO4` zz$gXCa3@bcWkysoYj<4CBz4D;>Wno`{6Sz5!4Io=L}%##NCuqcMlsFcZGc~ z7YTR&yaRjazGm2o(fv;!B4VMCWt$$cENgA8*$mdL$(ER zEFDLkN01VP+iGj+=C03lqwd_{b4)vuayS+zijN*mkbuL|-9Gv~v=3*hqPc&+ertj| ziD_UOAO7S5YrFd@&Rhb`9j6tYfOr4CCZJM#e^aRYkzPc5Q{6G`WC!7~!@K8?pFe** zKhL})`KAX{RLgGVVSe6W9UVGqn;3RYM7SoL*8ca*E)1staRm%Jxez&{^xYfnJH9aV zV4dDS$SfgjTvg26@(#Eo^%MwO#G7c)XV`YTv1o=3KYS=-w&6`i<5qkAfp%YGsAp>y zw_e=3y2Kk>ZZ)N;zMJ91<;u}h3A{k%$;x%Hf0)9Z+qKp8ZP;g6A=5J2 zDXF@{l< z8<@-l?b6&#sX57^QcgK$t?nKuCcaKl!n|kAwkay!Ly!5jL?%k!Z!(LS-(ROkak7&+!m9x4VO5x2- zEzI4XK0lAOzk~416x`7%i909F+~s@$IndUzP|qgIC>3^+e1kSZ@v@_yKr!p7RZnwZqC6@Ehiq1OMs!%;HM{PAYZ9rA_gn0oL z5kBv|JJBJ#5Rh}QQ)z1(CaRI*qszQFND>v3qnI$oN4Z;CP}}vhTsCr%rHLGu%0YNf z4J^L5Y4xDW|0z5Fn5OeHj^m|3E3L|@_f1>-L+{#y2n|zfcBCPa$R(?H<1#7{^pB;P zhRZUh2lB(rDBKktQ~?bG!5~ZJ_z?s}M)(hIvXK~Aj+q#OiQ(e8ndwZ3Au8;aJ@4o9 z`Lsp+WB2X1ertbz69})qpU?BW-*U|S3z2tswzsXVL-ts<3DYii5MzI^Rv4$?p8U4n z`sShTZVtK==3&A(O!c#{+gNw|N#U1?`Mi^7o9SgYgcBIsqo5V#TC2#r6Ccol_vD_G z?(Y12xRJHu9lOh6F~-+xt!*82Y6EfecZZ|H=JC{%aNLB&zVo#@FFljXus1rkpXuZWY)=OE88UXVT3)IV1Rh+Sa{>{`x}tsW9*! z#H+jSzKdV9Tf1GKy1pD)u3cXKR^To~xT{in?i?R-&WYPn-9?YQe#dkhQm`9}1Ky7H zYN|XQcRw!hF0~rVU|NQQWRiwrV2{%Rmr1YJTxlWEWe++uZPH|zaqn|h?np+7GEo=n zrGp2)6bPoRhU8Tq_j1A*OE~lx04P`c^m>yiVb<>OxC;uL&i(tHl?4SBkISZ6a3Zij z2?l%2Sv`wAURb4I5zgjt(P_GPbk0imxX@W+VI!5GUE6o5PQ!|xBg2ZwKAX$ww9zZ9 z1k?2(fIBiJvvlCIRHjmTdbUQUikZ>oo9b)cw@V zPaZPxcr}mq37!={fMYoP|oQu6HN&q%A4V_Xe{Ani8ZM|%TLn1=-XFp~#m7ixezC31oKqv}qH zJ6U?v^VEqhHkxS=fe5lgQ-mKR>Qw##7dpjAi_9(;;!vlLH38Fu$c7TdOq@d#yvzm% zQNQGM#=cNLk`Qpd!bA+G%0=11D``3BKSI2sPPWD9bQ@w;nZhDE3cq)wz&osX$GB@1 zUST51mHtQYYlFuZEX6RBK##-4t9huk6~hXEV@E5($y+->BQ_A@1BsN9@qtwFnS%awEIT$jc#Su$_e7nsbNpC);SR+xX# zhwl=-DiU`bcn8nS@|EhV^W~3t%zU$PO}EN+Kze({(Q$|Lf?eOO~2x2(J&?ncmo zIarKHSNcVpOJgHUUYje<5&1vanf*<3Vi6C%nZ&s(X_-RVapF0uolT(=BR>W2ege>a z2Hxd;MrhkpM7Tp?6RNThQwYSi!oo(n6qYs?HY%5uqLJ9#yXvO7@VnnP%B8!;QzJ!k zm9A9GBb=Za~^SK-J16^9hE_ML#8oq+PAakD<&SDsfvXDf zEW@7V$*^cH&0*Fwr-ejT(Cl<%S)4AbX5D8gIC7*?tGhJ3BbiG~hGau5+(nltikQj# z2Td;G8KU-x$Q#$2CT9|K$`P2q=9>~HJ!Z|lZ@;I&z29Q-xHU(X%UtKqvYJV`k&IjH z>GAdiF_Yj$%7M-9EU*S04nmjJnxSz*pXPQ13+il|)84a)*}7m)&}-9dS$1BuBXZkV z{n_m>jz$}%bHByrum!ytR>Gp);dMCO)+~oD;gE1ufwJRdgimszhq$$*w(I8YmrIDA z3q#wnH|B1?)OBBB?2duQ;RL8^K3Q6rn!wr}W&ke3oVm6(7lZ3Oh)_$cyLI_i*WH`{ zd5%#8Z2tQbr;&IUUI>R57U)ATbsjG9hw(-v7EAmSQ{koQr73Ap?MHhGn`DA=UD((@ zlTddNhM8O+rRxHzyQ$NsABLYkc=6!z{ zRBlIUAi(t@FAn$!we4S&z`q@&Gn^`38Oe)<@*l;ROu|$JTZVj~IBo)+n<^0)D^L5v1+B;TE}F_=Y%##=BZ(bUgoE1{-As5@pd zPYRA*$<7QJqUXiJQCUOtF*)WD9qUwyT8Rmhl=k-e-rnZ+f%fKsA@=#=>Si-Li*Y3q z$KHB&UE#-j2e^}jU)9S`q(^ujU5=OwcQTT9>>qZweVecT>*(8X$LbDq2-^dr zi{<(AukKwXaTkdM0)gtzXRls&(v8t)DJe!H|JZ;qR590)$`ihssY%KgI+b=YW0#`m zxXT2+prSEQk)FOiQn_fJtd0b}4$Qb;PP4p2t+c}D+jTS!cbotObvOO0dQ{=gC~#+7 zi@QJel@>+4R7MV-7?Hl2=u{!P@DKn9QP5z@&1QF*Ew-SQM3g-vD`?FyTRp^~pn0~Aq*$eMHW?1l;wv{7 zs&vAHFgkB;hy?(Nq*pq8J)2-UVoFGiJ|BPSW{2G#r$^IlScTJUE(am4$EyK#R`_GW z3&rIz+r2?6e84h-Ju~_loFxH`&J%uNcAMF5y;NuQ&>06YQjQb^JQ+qSbhTNt%v#Vr zxsM3x@Y)?_YtU;Z@#pf8tn)Z8ReHh8ESn<%vW{@Kb0-YMa#b}GGXz~-7w=ENGn1uO zbi|GuQ+6Zc?pNFndF&X>pb0p~05D#=Q>`jxwP(1`^U$HP<7Imvd|q37>(;HiX!pD+ za0hcT|3t~|65{Fi3x2HNL7w?b9-cmM0LIk*iHX|_&zGilAMj83W$wYBE{X!CIviu; zOnTtvO+TjfNQe^d{ycU1>BX+vy?e{{9{+SNaQA-h@#E{?2kz9;n#eRy)+c-v8XwE= z?+fvyIp*vdxSSb6m9XX8ap=1St{656AR-}1;m)A4%k`pI$T5W?@J=ny>6QM2wn{W1 z-CUO#UCB${x>eM~4JMP$5YvImz$aPsWeU&C!8H;K{uLxr?)w`miU+ZPE ztMXnEewl~k=Ipk~N^Pt~MTy!qxFbph*5dBNH*r@zs?^;gaAzdk-CN8rfAsnu#9biZ z^QHSDHN%tD0bl3h=sKg31H6(^qg;oJLCHD*FO^v*#3TT>HA%WqB@Hi8k z?zx2b_RUr<67J>+clq=Gu2#7FOLXlHJ)wlV*Z-sJ`eT~D({MFKsKSO;uW7&cZL99< zFt$xMS92kT=uMYJo#ckcN#h?&JzdQ$1P(TV98tJ|oI2aXFl4_NL8ua}znI~Y%fTI7 zObj?-cZP+Iq3cw%$03ZNKL_t*FgP3Tv{`d>8CwY`v zR8!E=H`IsC9#kv{jP{59oU+*$hkO%qm7=1K>mpToZZl`65Y7?OJ-4~^1rWT_7fMS@ z8R{74N)__O%F88C3<)n~D%5kcRP)uv(ryX5M;)PEozktaL4nY%yiPm`Cdb3DvvQ!W zTvAms+1+A0vAem{Zql4&-cUyq>`Zz$7xNUa!^O#yRlHuy-zZ*$zC*(7gw}-x*ZjPy zV}@5aXQD~K)}I<_&Qh!NE;I{(G8x^{l>P4iVs{Rg%MJ0r4jZ-}3O_iE zR(93}!|Pf_%+VQiukwZ<05uYch$n$86dfh33Bw3^n;q_mk0203&GDRGCp481bP@>D zOVwOK-t-wLZ$;UU*4*q0deCSGp^DGpak;dT@n)2{YN4Rl5%g%fkG`A^Z=j@iqGZE< zXTS(LEETWpt9X6e8ai%Ux$nT5jRzVVtBxZrXBvd-=>0n|#6|0WElL$JAFNHEgYA8C zrI@M*@Dt@ zOg2YqW{P1%U}|SmwFf4{vsssADMNzku(PTVFPjPwv#IVxeEzYrVZk)$$=pD}BRAxk z?C$E-YlDMq{OfCJi(gNUj3oP{n21wfpc`##>O&ENSSW~@Hu<0Ui%zS~^}IY530qSh zgr`#LyV*u_h#MtDOk$!B0$dJfm;gi_a+rghIo68^+$rcZCD{wVR6YURfOEy<-iI^H zajb$-&I3lSQ>RooSSl|JPK5OD6d>~Dsr2nZBM<*xJ?t-DnpI~0)a)+vIdHeZSVT3%RZXH?k;5xGYRfi*M2=!Tn(iPW|=#<{%(CY_I8h^y64*$WGlGK zna5I;`3miF)oP9oPlMqtf-aLj>!^B53yKq$!D4P7ohp9NOz26E-WnMLy_*^<9(&lZ ze#i4K|B2ylw^C+4oMLwOsP@0bsbS{#q$`CX;TgN~)ST}(XXmB0EW{|c&3p>-*> zCeyoj{vahltYsN^gHVwpqH{CR4*R?GF!SDQce&(3BRlM#Im?_Da~&buMPYRc>G+$I zfV<1PxSJ<`<+X3x=ol{o#)Sp+4l%d;&ZKg)_IB#~@0iN<&$iw%_>Jc;U7Rt`{4ov$ z^G{vct-0NL(Bbwt3>|F&5ywPxD4+#BZbM5%oCG+D2}>&h%y{qk3X9cd>Fu>-D_H_v zo0vd2v`yKmOm=5Aq0gF!g^P?anM>eZFK@*K?m#^(WhbK@@tS@M zx0}9m=konq#Ic51ijtaRsxUFE?<7);n^yOJ9z7ZzPVepr+8R+Mz7twcLcGZ@)=f4x zPEOvwdG6bpyXWZe`CdAr4oDizqgz<5Ijl_PTLFRfaPmF4e|f7MI24m^vsc1h_R382 z6U0RCm2{Wr<@%#HZvpNu4sX4Eqp@P$x}7^KcCJ~u{rK^XD|fyQoB4dD4pC|nVt3hX z@3xIS;mUFegl^-RC4)vA$Q|49cykwTipR0o8$sV4XJ)FHuvC)|M%gy5ib}zqna`{c zO)@n_g{h>OR#VoQW5$k|T?kfjp$ps%4I(g;I2^qyj4hKWr6o8X!Z@>uYl`9{ac(l= z+Vvwx{PeywWU*%1c=Ig4yClPcU{3pfx+Jp|;iZ%=lh=1=+YuMmDOq8X<7Jlroauxq zy!~uvPy4BZhq1fEPvoYO@x>N%?gZ+8zx}MFWtzR`2fsW4!@BZ}o2GO+aS9n7i7xWp>J7`rNb}`)j z^Do7J|Hp2Qh;I1cD`t07U)MflxSOiYwSPZVW%*k>o?ULoFq6^5oM^HHQYME3XyZXw zLS^MUv6`B&pQv2npub~q1i@i7$#4O$=3qD=KGzW*8XANL9Re2CXL9li!CZ~MpeBq| z>7sBS=Y?U6Nrdi2hPW39`ha%Otdy3QZI0D_NQn2LKphVyl@0#2*J5>L%1JxrT29g1?d>@_(+Gpf!*aVTDovMd(FQMAF4T2!(&W3W9-8;)1QHJCc~4TCNn3Q zX|Vh9%YJ+QVh$2hNh(W!B-pVsQ}cy(AlF*Xs)BOVs?_#9p>1f!g;hYW|C_+2F7aT-RVwR?XEg+wJ6ybdkk^cebnZ^#$a z4UP*VB!(ef(?Z%N!$6^{hu|$@coK$>n4X(Ro!jYhM^=@Dv<)owbodONaGdV$40`pT zW!p~}>Qv+i4zjo-8a90Q=%4Q#MF7h1TJ)&0v76UtGS9^6?X9=jbO*etzeRlPqJ)Tj zd>*+ffIHDfZm*jBVjscXy)SOmpPQb!bLD5}-?(yxAa4snoXW{0Zg`nsZa6BDVo|U^ zHm=`_u7!$|gI%_W$L{U7#bths^Hlf@h(~t;+T9;}KyY{Ln1p6-+=hhcdGqquEX`kt zqfIPsS!!IEZOI;b_w?8d#Fx-ta+i6N$(RV{+D4uh z6MYr;^dJ0VXSK*NdstkFZ*j&#%TAV8cA5dvYBc&j*SS_B6|DV4bu}KQ6HicGeBwkg zKUaTeaWy|0uC$v^PM7iu5IDv!4U*3{Wm5XvbZ932B-388#8hSi?w)11BX-v%aF_c7 zaQE_TxU0Ps8WZ20z}@oI4G+g;bq7QO+`&Y?dcD2j@py{dSyU>j&1OD>UDcQKH+VvU zJ6oPLHO|blXeD&h34JEfTDzXGw0kr-L;MYJSN)$42<~3podI`UA3U0ZQf2DvhR4NY z1b1T}{V?1KyK5^i8!+dY<(V9B00PQ^%FQ5mm9a2d%&&(F;>nJtcp;+1in!3O(9a1g zm}vG5a=&X)I4rYH2v$VW0#;a4;Ey*URfWZvoBv1I)yFh>=kXdT6hYBLu4$k3X+?_| z9-E%}7|t{VZ;%)?vdLu9Ggek7#BE~lE+`^$!||rOi_-`OL&YIFMB`thPHrZ~ zH@G`uG%;*?j)cr)F?Z%(V($C-{(irw6)%fd+tNy3qv@xg@AvZpYFAVK)?0$rRoAR5 z$4?-yP0g5%u`;t-wU*a+%Dx}wb@j5vytAGGuK`lbPG)u{F8RG`vwW_RsErXr6x*aOYNxx!4j z!6R-rlr$pHPdVM}U%X`L(qugJ|Ih9S?o5qvjSyjvn>dwaxXn;O2%3H^*xJ9d6*0>N zRb^#W19>~q$tk4=j{H1T%&AVUmzkZ+xOc*PH{cY_ujs_<@5yjFdn~P|$3S!nJ&k4r zbdN{#2jjX0R73H&Pp6-z^+V5u$BKJywd#7PEF82)rsjvaF38y_L4F|gALyn9 zRcX-B<5d&hxIqxA8JccsL{fVuyk?-a)WCey40^nt5YoxqLAN7k%a)uX1$RXomqS|u zxSO23G&_Ab@py)yE>W?z_Qn@|m$)q)J>rBmE#P*a@7ncnx<$DH5=6!liN9A)Pt`Kq zHSKQN%H)oZb8ka?01lytQ8fo zD6mU#CMQ>?SFq==rGxdE9SC%3;UKXVZfDpFP?e)tV}OQw=lU`|aJvYfLcd>Brh(y*tRS zhF-Na;Bn##D>RAp(fO4Z(Z=E14yA$-?L&(e+2Rxo`{vua%+%hyw^Q5*A0z(j%=uoX zX$bc^PRHJJd}6)@+SPkzUpM`}mm{0;3qBFM>t*hSP_c8zhXf{lC~JcDbq~Rf*L!~G z__?8TcwjKQKSI{YcP1uzh8`V{03ONN)93IU$cF~&W2GolM`7qoF7W1xGDZ2rBH4)W zKHzTC`SD-6;nTC*0U z+vLubl{GKZW_J?nfQ-30oT~!3gJ3EvFGJ$p!aPUM2)&qrJ(e*}I10E6fWJMJa2JVe zJNnl@3Ao!2cK7{#%rfu)dH<8jn@=89-W*BKeJLF}xx33C z;b$0TZXdl@!Xym^2<>gFQH#K#nX8XAc3~q-^scdy1IBpZNmP(-5WTXRH7YRa5$P*z zY-@XWdogQN%4^oWSxw9iP`3``PEIp1!ix<<$S=ehK3m5 zBsTiVC%nd!DpN4L(ejw2d5S6$+A(G-Wu}T%5pcKVA8L~M^|LyHJpAF6-Lu&TSlT7I z9i!dpS%q|Sm>mNivpd4OB}*3X2D=-)cZkRx^EhHi&I|ietV0%D|HiS7+$~E?=BeM*{X+(KxtLL1Iu}6PeE&*D_?3d)5Hgo zBa_7`!dZ~SJQaG+8cq7haxTPYsbF@*@f-`s^Ie%E!Lrs7Uofm&MB|V{?YD@D`L%dF zu32$Q^RZoyps!%0%rs0b95Sm4kf{=e!qPB&1|n5}bwQo&32ec0dp#PooR;o(2Lt0( zmJ#-c=waMJ(;KjYrD%+;Dn+8Ek1#MCbQ8#i!@8p=XG;p&v51mR%}vddml88G7YXh* zqh_E2An1Sj(gRGW>dzPt=%;} z{qW)A>Bl#ppC-8b<^lKJZQQYi!=aZi7e)kzJEyFa%M@e6yJIgZ+r}VIVS?AjK66?2 zbMVtKPVRySyVJ6cz8Hpms+-ZVMNmeJ#bu7J<>`sg`)0}p!rc)43{O4LI zJI&=HFEd4%d3TWDj!o}fu#v;{evla{Caq_$acCwBG{N(DsU=T) z(j$5E_+MGJndhRxT&0NNq%u|V)i~3c|MP)O@a%4?p&C$Ow z+?^t@J9P?hNA&K|<&%#pzZ7tnJqPThGBY>#qy2-`)twv}RxX`)Qu+v+8&|7#b_%Hq z*c>;!!fgj02UA_)F<{zi_8&w!cm*m=nDI(B6aoMXHyn$ejiLpX?qAaNF0s2}WQNtO ztH$Q8y1p6-D%CaOGGGjdcSkwK1?2I}Qj9U#RoAI(g7oSde&5h3CZ40)w>QY@yK+17 zn$S(69P3uD)^rN)XB!c0s6)sK4>Ko~|KMLqa@Qvv2+w9y;cgb16nQRaolWfcKxz#`6SQ{@reoF+ly1&<*x^KIXN#&RpLS248gFbnQ`3+ z1>Kt4D7{r>X@)PZd*Rpix;@@K0}J!`i#hLQ92sdz^Ih`$w@vY60x-r<8Pa+##B~Ia z{S$f4R-eaH;5Pia<}(m_684#4&26FN2W|o%UEpj?kTu=wLFe6gYuxm>{m4ThRQ90; zLBo>H43`FUF10iLaLEVJ@BnWe-ILBd1s>frJ*8gFv^-wobKU}XFm4$BU z1-Bh~QJL2P@Q^~uaM#WS=j==vWUoS7JHZ{ZI|)p(eLPb^6#M4Y#0G#jL7T~gp{VM>+CQDn;iVo1ur}n zdT^OX8@qmyktu;z*vyq?V?M*(|0qVV0Z)W|5iW7-st66Ur;#GuODZLy%7mkCpR8pS zy$2{Z|5!$ii5>(y!&w*+5UpwVx=Ll{XDU!k=8hfMcR)RdIv|OYwJ&FsFq7c!myCBd z+{s$kpV(QMc&~KG$hO(tDU^#tg%Vkoos|N2xdQICeS37}enVu0y@O$<4RH-b`xcOcEgG?Qx&)OUTLW zt~w^Vi zN(c%|hH)i|ogTqmaYJ!2l$pft-ekbz1Be|X9!9FXZ>z7rdPk5l;GYnsfaw9OSg^@y z&&)i_YXFH%_F}S&9pH^==T#+H!Oo8Ecy+Y=YOK7bJ|_F_lA}zfKRKfC=W9jmCRvu* zC*z@++NIFDi%js;1XEEv8|=P1y;beiif+Ss_n+t?CU}j94v971kAR-KaP7NPa%Ypg ziN&^7W$A0uySd6t%PRvbK|5b2SP-$;2*E@qH^5S z%pVB%bQtGMx8LolDh=tS0W0JslnWa{-RBAGh9_*8anq;y0$#rlML++g?D}Jxyz_8x z0SaQ3iQJpE>krVkfmoV~SK|?5a97OI3MRsg5Rx+_Lrg9T!cug{amNbBiBiUofM7OF z2Ze>HC|mer1{fwwjGn+BoSBJX(IqC_Ow3|-x83P|p3nFDzAfICWreo1v~MArK0Ke# z^8@Nkw*ou54^vONp<1n$P?^W3Di(xs`pp)zqWV1+uUWwX6xHkWC|+WB95%xHyS>|l z523E5wWN2cYxdE~htD1f$dLfVWGc*~Ox~RdURR^hDRe^$Le<0 zHMAU?Y-ydm_2?Oa96b|@LAro46N#9jYAA@>xgw55-reml-7DDL=51S;-KC1Y9<7{?cvH^I%yi?lFxidwx8VXQ znfM?k2Z-xnr6X6qc5sSj_Y<%?qlo2V`+;T>XV)F0ok-D4lT*YrsmDx~Hq10VLq#NO z>Yc8_cc(Sx3A{9NBf>({d_Ol4^^XIAxpz_Wqr>PBEKhz;4 z%kqWw1FjobsSfx+>f?xSwPvBLB8=My6vhiO*c+rtJ0CVjaq zSdmM;+F`*v#UZg?`5!;xEcR;@q33j?LbLm^H!(4jq;^a=>*aHfn zJS%gf|@R~ zWv6SeLI}pGm_Alv0wHZS;)QmQI}{)|a6FqE*=T;hRk7P_sK#?R96m*{_(Sv?+U=^_ zj|x0hF%ZgalnGl~=zO$vf5YL!JBi#a-51JC33tn^`v;xk0cJQ1Rb)6=BlIf)krs_0 z6oxwpRamORSVspfJ>0sqb7xCy>;0vcmdRURe0P0$8QM%1S_YaQqvDm$2vrHg(9m2< zM))hMAz2T9-NUq1UZ@ zMGhZRwenP>;q{q?`Nqoewhj(d>F?wQbLjZnxB+iwyn{|KXA%z+EDXOH9k$r!Vi~Y880nhDQ!SQZg{i zp(Y4DVXY=2#gM8pB2rb}J~KRsfE7qKF9yUR)?M6C%WfP-Wk1n*`UB5;|!_1!8T6S0d4U{UMUN{A`11jZZ^K{abw>~Xh zCZeV%rDUedr07gKF02pY^aghX0hQ9_zyGhQvJ7eIEaFUgW%q7^&GIPk1PSk=JwArJ zC#WnhVB;O&u1_0gKE&?sDd6r)z}5RcQuYX21j{HoVx@p|2{P}NO`-Rm%WY>G8T0FAC6uWGl)>~5>QtjAnUq>gaUM__8Tsq_ok zEQFgjVyYHJbyyU$*;Q2Lceb~y4zJJVve?jjWRPIDjbkPE6D%FBYpH8(En&EW?+&U+ z=>M;3yE`6Zh9MRShl8R7o<+C-03ZNKL_t)CN0uY(fUbmhcb~8_v$c1rwSkRyM6i}t zzAb58y8m_8Wbdu3pMCc@02E~)3kI7Xpc`T3K{J$0Ne&3li`{` zZ5UsLu`>kl$sjSvGB4ihs?ipCbO^b-TfNv?hiU3j33nNsn7M9Uny6-y)C$D=^@WA* z#^;0(Gc)6zxyE92|Ih)JliI>SqTKj>hXqDaM zvMnK*eRi_>fD`6;P@d3io)*`z7-g-Z(eU?~{>o|23=J9HE*KK0$`>$`O6 zje@P2g52SuhMb&KQ#SrA!^{F^cjI^AM3AFQG0EgS6~Nqfs53$G2<{5^2;ZG>)#ZwQ1a{f+RxUrcd!~Dj zu$C9*&UZ9%_Z`|1rl*k>%_Ok!@|#oJcNG`!LWtO#yNW9g$#Eu|%1;Z|z}#JJ0;!kO z@#pcm$dMxqd0MB>+-c!Q0RN1fo;q=aYtpA8_2*7tiB}()yL)%;F0cH~onyc}uE&Y3 zg*y!qHkW8a%y?9$2n}1awp;ybRcz`KCYWC&EbTrQpP!fN`Cgk#LGM12)J`XNKfAjV zcz0zjxx?qpjGw@{pKo>*+sdff<#JfP9ybPo1bS8_W@-1^t=QYSoT|<5bXv>qEG~}b zB%4e|G-P7%WI#HFR!WVvh?f7$e9x#k&r@?oO-4;u$7e%olF=~gFDtTm95!djUv0*w z&K9#*r6y;Es@0fUjleIP>Q<|X_E|jEP?ag#Zm(9VtF19tRnnrxrnrflsW!LErK(m{ zC^daCwY|qiH|-8rh~UsyjpP*+=UKVhPI24ls;Ir)<*|f97CX^dyVK%S?L{sFPcdKR z_+$jD>_2*#S8O{8?z(2L&puk274t@z$(hI1bOpca!YostX7b%k?!@kHg)UdS(D1tS z^|89T($cy*!n~4W#O_*pdzX4Ak(>E!HaLr5753gOKY&bS1!9$2!o6m0KM05kXRsz9 zA+0NT@uEmlk?CO^Cx*o#?)`+Z7#@ZPj~_k3?yhUKtG9t(elIICGqz-8Y}v9d1MH5) zB1v()GyHa8q3!k)B26=(cktSQA9bSLj?j(ngA@~_N$6NOJ^Fu?U4KlI`59-k{OA=u zmFt?e>-!e8MRKL7ni@w8!AuhaMlTkOIT^6IBiP`gcp6}Zhw=8D^^re0KgMa$r^E{s)%@qRdcdQ$(KyY=6G7B;itnnFYUd$B)7AG5V+B!v|QVqG%%# zyi-wr8uTVez3cF~C`&r&Y+{5xU8f>lT=d!#Wl3lcy)a%9Ur>nBLoiW|G#~gxnuV0% zP61tF9fF}*NvYfvV|S|ncVJ~HmROy+z+HAuX7-5_t9oBD+||V4j@aD~Un3zpv*yMA zt^as>xx11_nXn5KL$8^hGXpD~Ofm`X7;7>zm#^CNhZBE!c<0WoTet4qy7Tbi!>cC< z@3J$*W{!V~7WSF4axBnrrdTp-GAxo6Jja&y~T@=p@p!FrH8)Lw0%V)bA(Jv2WbsVXZbN(kuIvQ}j+9e%JA`@3KBAagt6 z(st-Dh0uw80NIydcEa&S_wF5)n-yj$Yl3>$C10YfylrR2zP+FQZWNGt8Ys!Kmx?28 zF%acxDd28N;vjR$l9cxVcX6mwBh2|TOd~E+rkUTwxLr(+380g!zkc(T2ubk7%X+cA zi?ccd^O)P^fAN1WgNoWUMGUZ`Uvd^Nc`Lko_YK)fvD@p`gIZX#>wddG7@G2igCSd( z*pk<+`D`|?+ix%MhGrImsnMq*Mws-mk;Y<`v?LQzJyz1qciY8OD_L2_9Fsr8l9KzZ zNlDhmf~rZk(;nIo)HP2a81RK1yRBKf8^ccGYCe0w69@!@gj__zbbWHS^@2`ZO}9Bc zQ_0E4JeuF;=_ktO_Xm8s#!fvCT>Z~EDK|NcK)4*-)uY`>19EA}8493UPVC9*|C^;WBW;U(D18SRWHO(d*{@2<6V z?)&GhF_RTl<_lRa z%Jr`C@UVH81i^|SRAHscp#xB$uuMgTg&{<&Lo!x6M4Jf>C2m9d3eG`Z%3)Nxb~LLX z792GnIuJ9(86ueG4kJfJ1*gQMD@p~^+-$s5EK;m_o`kcR)1gJDk_d8dcD64PIchq? z-SUj(9|G>`P+gY@cj@B0BmS2C_0??(?wH*r?(P70b*}{OiiLm`*VOeAFy&;{O(OWx zZ2GyyY_eD|9Zm18S(nXl$7m;)JA!x1(=D+}pEWZ_GNLURbvX*|p!v)x?snf3t;*JG zFW-c_ZUuLbFLysyaF>yZ3^A(#d9u4x>~3s3q?ttK*ijzK%jD95wG}H_t}?o^4Y|<3 zIW9lQ-?bv|?%*yiZ53&Xgkp_><+1l}1sB3{o>({Jq! zva&zco+gTS<6Cx;$GgWR=CVwh4QfZ*j10_LrP-|IL`|`qx2Yce%kAK2s95Zxa1WA8wFmN|Z{h?^PN)xrthpo3ZI8u3<^C_L);n<|!?- zd!{PrD6qM)QgeEO-V2SP0+&#?v$cLlWo zdhsMg47~_0uC?q0lm-#pl@>jDFe~CvrOZr#yEJnw+Jv<#*T*_f zUTX)|4UDz4s5S(;reCM0iQBcbv=5;U54UqUP$vq2c$f(&!CuUqyinG=QgmRB%XGz` zVp8Hu=4>XFBQC>C6&PkVqE!vWN_@B}zPss$+=)(UBf`WI1aFYJ+}Ly{TLqy|;da*+ zma4QgoP2$yeL7XpP7O27$xQ4Fh+D8AAve=dXU1I!5)#eb zqt&^ueVZWY7bQ~{)!V0Zgp{ttHdiW?Ic?q0mwdan)wQL9p_SYpad zhPzE;Xi{xMh4|>GvX^fJy&K(#TDl6(awfq0?afR~0%}Xy!DCKs9HK)Vw z)}1z&he)O0LEz-_Il}~0x)!$EZ4O_*E#U3#^?H0hkI$(GG`~FL+kx2a*(2F1t2t8)ySwqy(nnh#m5%@YJm?*q z260y70{B=j)61?4gIzTYmKD*)udEl$JiY{{T&U2Hc4(Q|;~~#e_pC0VUT5 zT3+v;7yz&la|6~Pp@g{Iz!>-cU~Jevfo8d;h5?pCwlmxjyR%vu?ULh_11eF)04yW< z(yaP^2p5%YfoGrUza!<@G&1T8^^>7}!s_?>bi;}(a6|Ccu0gr%FukPr-Og|~(1|{F zF%J?}4}MP{OVeo!0e7mdf&1=^f`Fxq<|nNDGB(MSK7>zX!*;kMieY3=G}6@-!4$I# zqs8bj(!YA}YZUdXrw*Zs6^9WXPYy>S7ZH0*fb}ol-?OlUu5Sml$qDQosvQd?6|*9*Cg4X*lIYKqVaNADr$m zAu&39S)8efaa?mpIA=5$Pt@hI-{<@LJx^bfOWc6APg`s$8bAGfzn_;6R%<@qf`BGc*0(Th{sn zLyDTD0>J5g6?#@aCeU=JdVct%l9(K$a@F@}R+)7y3*r^%GNIwj;!8bBV9BbkK9-d= zQZcrq;`d_}Ym1614i74pgIS{amv%8paW^&q_Z~pFm?k4{(f^qQz6@Z-} zciISZDjw*jKl^f;q$-rFLZIY-Ny|HusYG+T?APaani-)`l$t01A8{%|s#1ewO1srz zlNbQN&t}RqS?#i7w#kag>QwB`fIBCrFt0hCaW)+dFw++$rWkdLr`Zu zWU)$;!{l-+9*e~+IZ>>TQ1;6*5Ip)?xia@1hIc!*-M_!fQO&vFwYq_1q5dR7^px_j+J%lY#RYKYq9a=6Qd%b|>#?^>>% zn|h3RGD0x|wAzP_wGWX(R$G9yOe(+Q&q;wQB0ek%^cc4kb?T)n4z-*B^#elAJ`9Hu zBF28;(k-bNA^<0~x*VvuVR+5fMr-g&*MVV`2q&h+Lmh(Juon99^8er`1 z=*@=3BdsJE9cZAWutA~3WGF|3jx6Y4pVVJ8aC!1^_Pxo!>y6wkp;mb;jHHZc^2{i8 zW>h$|u*U5^4hcyQuXk-bC8R1O&_v-J?r5t#b#~`aJEW5BXn3Vs*B+iD)(%NYM_Z+@ zy`PNc1WY_xW#{ozK6JXGx>cOoML1Y{CB>9M9)l}5K_??RV|}7MmR$@F`%azZQJg}T z8R>S77ORNN=!FZGZ;lKz8Ql3Z1l&=LI4LvNEMIf7E(-4IU_=PU!(AtAcQc*jjLXan zcHeo_o%x%f_iCsr!9W9O{7oB^4Zj`@h332c0`HC-*|>4y(gZ+nZf}FH&2zJ`y;9wI7KN>p zFh)mx=A>`A$35yvUtg|S z5Y%C&#p&sjC6~$OvYXr{o6}Kc30T|=fLvyVI*$6PtIc^;^;M@65{(E-(I*%;Br(WJ zs%lQuiSQJ?E@s05wii*B0|HZcgjfQ>oz7@XNbkLolUJCBuuQAdEEg8K7z*W?9eJap z7OUH?*z#mawg+U1F*%R@Mtz^#4Wbvwad<2qi`(vWSv`d}nsZD}N%7d-lFWds&*QcR zEDAexE0UsE>>jt2C%I*(Vp1d%dpJ8ga-m{@J)_-iM)!lv*j?El%gV}rrc%3=E7!08 z>#kjg?tgUq_MwLi?yf(7_;9xI%2Z<`Arlz^cue$}O%G~C!7FYpXna6St?`*JJaq+` zV<%cDuyPf$THeWBwv2cl1#<3cK?_j$968Ld2*3lx)i$E{fJ$EJoDAleW#S3wY1DpM zEDASazKP)KrKRUTFDN}n4h05yQ~)#!#Qb&_!QHCdT+N68z|#bi0`3xY2_H5zl=%8+ z-9^cmhX<+sfLDSK5M3G^oIKn>C_|B&dx_mK;v{&Jh^0&v?{I{Nj&nOYrVJGlvgo)A zfxGS0$moa{fd`7|)Wm7Mr~v}p?R~o%Y7~-<4)U-t%EugN*vH4OeD6q=eCRCddt6dI zxk1ktjNJIHr`?@K?^GE_d~{}%GLyl`?z24|L&Jz~?m64hPI48B^J*LJXzQVnW{QaJ zM{qPq8o#6CEQBkhF==lbf*aw}-738k4~<7=EO8iV(?|~Mpy)9Ki6M0iiWIOy9z#a= zw{fC}-q?;F2w{5KhWdLtj_>4kcUo2^dj)@r=62dT?-z{S#ljuR4OF<}J~+VLcy~P9 z^$NJFWN_!tOij%k|H}8plREylqLa}H>D24$6A!19$ z2=3U?AUWwEQMq@oq=cQITPdlormFJA$KKvIc?@lLl(~{sGLlWPE7{pp2y}0RR4xn( z8QhI+-FmR-AW9J+&HQtIJ_oyX>$=u4kS(SK+q7ETgVVI8!zAYcxfEz1S-zd8WeRSG zk!CT`yXmHn|I);*=;b_+g`*)yKls+yQ2|h#5dp)@3*TROp8~^v>|;uN(ZWt;c5&ox z8W-&BZ>yqUC@t;g%YpyI3o^4`AMZptf+|?~=9@UNO4{p8G9xdy#wsgT$!uaA$SR}b z)#J!vwg^_)(t&D3l(R>|i2s#}PK)~R(a2<1o;OB&F=NRo;gotYRZJdgCb5a{VQ zY|y9o_BvcSX36ZbN)Fj>%TpL8DYw#Z)H^J)%WkXB^T3KHV<*=a7_IW4^Ug|wODo#U zEzn~sZa2cDJrJmPkgAd+vHL7`yG2$!2*`9u4CL4!us9`a!0m9`?IuS53T+m}ZU+Je ztdhx+z{s6IyY(1rqVR6p{WD;9_iJavVGJ{;rmk#hBx+Y6;+e?{u%~eo!QBc3S1x5# zipmV8!oKj8yQQDQ1;EH1(K`U%j%7$F%jIQyE$6SDn|+Q(bC?{6-0>0wDrcn+02gqM zj0y#6P}p4}bi8?QX~CX7up_Xe((~6S^koIjN6{Gssmh9iLpxTf%R`Z>lEx#VMSgQ) z3VY8&t(xy$Zn#+a10*1a2Qj(iT7CdtL#vvfIXO9an5^B@w6l*7GchGh;)&Bq+MZ6Q zkN?+@m?9#=lGGzDaZWEoD?+=0NibXeMPS_*so z0J`}oNE1`oA0C%n-1`SiOpQs2=|;XfOj@X|?X(iOg^>(R4tk_*T>XI-6&=~%!PEUI z%-?FS4_%@FXy*ap`98oadT@DwholVki$pMytFjsdlIvh7oQ2kaqU7f|*S2VCl>X!W z(d9t=J~R-q+t>wmcf0YZs`G$5?Pz$Z$yN9(6n%FDcd5~ECw$(dQ@JIqH>EYuueaQB$8J0HQ_@;JDA%ATXIli+Ty zG8AN3m)V;c5y5Ee?!TO!eN2-F9>=*|d7Cu4L()9$`m*lINhnPfd)dgsFjoHnQnv%q zHvDmE9LKUdZVj9QH^OkhvB`$AaG)60r4s~s@fbS)IHL|17lY?pVhCFnZ|=6-WMpJ< z6G<-j`~H5<^R(!eoL7p_(x=oIpHIKPm+zNxS5bjU%peMw@Xy42<`z^m5i+nIvzd|Y z+anz#BcV`e-MZL7i0a9ut7r_{^3IhuF{dG(<}qy-hu z*5^&{KqA799g!{1v9d$Z9p?babz@LO8ETj)W?nwWS*+*O4{fZH=@sN4ykKMFR98YU z9`2y;Uw6)#4J>Rguxnhy^r|*T*arlSEgzYEeh#_@4x@xMQ!ikyOkp<>g9d; zeD6Mk%=cC-A%J)ve)w1MJW-Pi0{8Ci`%A(=X6Rw{-9*CX9bK|%C6#?vROx5P%1Sb}@9EAqXTze?>?i2i6@^~F`N4L-$85H{_Qbl)t|5!t?X$Si zlY^6)#p*_Rb7;tA@>(q>o6Tk_bA-ciR)Nu`%VIJ+6qC$aii(8rV>fsD~_HhK3=5?PvR!yjzr__ zX4ZBAtkA7J0a#|%DcP4tHr0HqrX)BWL)SnwxEN^OMaLrW(>JC~HW}f@U*&MS2 zH#&M?ZPr7tl167P7fTtQbQy%zNU_50$wuv zq9?p+;dh60&gm%Bj))It@{>NlRo8Tn>%*D4FK7bpHpFq4lbI679h+}u5|zF_`5^9w zbanU4FM+$cx{nxlxHKG|zq@?0yu6{ov90W91_a$1oyIJu)3_?Ov^Y0&YiU4uN6*cd z7v2T$zW)4h%kEf9c}9j48+gVno}EfF67H6qt8!Fz$BTD1R(C|(FE!kK#JD?rm~glB zC6KD(ZrfZ#<`bE*$=IrtMBMRGgWyh${o&cnpj0%W?g&h)B z8!DOWc!?Y~VFl|*%Z(873JM5xd3jFL?FKfX!m+#W%(lpA+! zAGTQn?>4frN)1L<2y~bAHFFYp=Pq+xU5!|z@FpGd8;D_DksVW#n^3{-V^Xl3dg*o4 zXrWXq7=(m8j!JLTikkPEh>5x*+-c>^+dslOnaL#Bk-AH4+Ff{W#SQ|aj{a;6Xd$e8 zO}RIGd-ncyyNBl8o>6Jtjed1BWi5tD*FJ39PA&~$k3yo!ZFds@6vbnAIP7*3dNw!7 zJCDm-6t)z3%)UKNXRMM=YZ6kejTy)ele}T(b7uT4y!;^VHI`0ggpY%KY zCY#CY4cnElcQV_i*!^aU!(q02T;@ydMcqXXuLVlZ<%T)5NAY4|!sRfNqVo+|T;Z_K zQqiaHS-WC45dcY~+UciWhs9UqH@RItb2gIO-So6! zN4CkE^sFZCN}=s?O9Q!qz>By4wzqljCsj?=oj=^^ByCqsKq6gtwgl#wJf*_UDqP8I z#g0I$@DBL}U(}JhI}wdmo!Hx~<1Tm8&e~kzp3G*Fyu0%SGR+|uZ}6r=@mcnUE+J-B z=Sz@lezCw#o&7AXf>HHF-gYPKm2@_h94kRULVh$V4?#I|SBc>6##>cUc>#eXuw&SvzDXatiI{57 zm9A&(q^gEHsR=oVx_u^7#~^9u>MpuIftkkds1k80S;De=_jB~_PC;l&{!d2-!tF4a z0#C7hoWPDP`C+<6Ox!=+vv1))q3J70XxxzqO~VQeUJ64l!71}c+%f?UDQ3!s+<}RS zL!)D(hw!C;f=5y03tYdBj*YFH8ixJcXF!@xPvlG%gRXV_Erk+ za>~mYcWzDHX}GHs+zkMC-z?uE+zkMCcY(W%Co)^+TjIFGUyfZ>2zQvwg(wa z6f)`C(5%>u(P6VazkU7p5iBE+wtJ2v%r|c)O}BAlMQ}Z~&MSDlg!2%HaWX}DPF4@j z{eeTy8@qzy6FRhJ zOT!)E0O}6B`>Dp=`vN<4R7IM0_qFaWe~4?My3?Y^|8Z}4MAvqO{}1oJN^n?_Q1i79 zV;%O`YJEH>x4}n%pc6k5cWfS;-Re+m1iG>^i%ap6T(aBz?GCfID3)3oGo~e{8M2a7 zMZBr+$dP}?p?9fzDJl_nNzTC}b$3|iSdxv&sdTnWN{gBIm_1&P&E#7g3TIC`{Qhje z-{kOQBiX=FHhHyO@gezuaAhIE=CdM(FYLK`)xMZrW(i}@V927Jj};BM{AI{NoiuisHBU+!nO!yjHK(ldT{T&!>1j9;?^m^Lou5CFxnHyHaSoQYmJZ z26nzpv}@iQCEQ)ObLZyGPSSQ##?)_9ya@jkVI8j3C1>-`(r0l#oHGHpv&FpV zcj96+8hwi}tGc+9$dpV~{;m_zDk*oO<=5176P}rLeRE_QTjy;f5+_IMF0YN#%n2p3 zawiN;HY9jo$_`z+9_gwXgBdgv7B(0&ChIE)>8WbFPUcGVStoH-Y0EpsQ3r%vbQkwe zkMs<8j9|D1pxY<5(7cWSg?grK;b7?0nN*k~7?GK7l+g4C2zOulPeS&OAn8x9_Sf}| z!7Hq(3$P2h0?Z?HZDvG$GiAL%`Mpue|QZn582pO}Zi;!AAC`o_d2`qhNYyEop! z3`}qT5!E^D2!0QKfkf;bInqCdc@<8~>w}TlI}-XsclGx1wU>^6=M7G2SZN}b=&BOW zu~kP^G~6}(1n%Db_BwF)Liv2Wch|kBsXNA9&h@#vkH5Y?0Nj=D9++QVT%6y%yXBI5 z+tMSe=s$mY?5eRc=FCdTX*qd!Zf0&-cn8@1Y6Gk@F}QO2^vQP{V)JE#sH0{j8=dq| zB)FS&53b6|$;c@e+?7k>-3-*-0C0EuW5!(rY`%Zs;eG45u*#i&YVPG-x}KuStt zHB-f1!x?ZQC=)7Bd4xOIZb9lH-8BLO=En718;N5>oJO7xb?=+70LJ_KxX@vCJra!o) zy&;8E1eX!h;H4N3Suuhha3p8Q23_2k#0iB-5EX}l2aI(zzES;c8NyXQQ+9Sd(G+7%5d~c)bE2j5u1tCoFoqRAhNB-Gr9aW}<9+Ny;0sM? zyxT(IRo@nk2(M;l0(WZ(cppCeA8R|gxVt*53^7NRm=6T$h;_GzhT>(UiZJ!9TJyZ` zKcO?sq_5uUUZqk2-th9(ZyK8UPDp;39A=^*PHRF3oRQ=c@+b`^t2y_G-k5CA83-4; zT#`(NlSLh!T+tF4+HEH`H8H=5jM9W`?xLdAA-;2!{0b+gQqTe#ylU>)Y&xCZV71vQ zbq1HsU^8fS7G1JI?{XM**4#U}KD*WqnQ1blSxqQxK+jLHzS8v6c2dtJ2v*rfC2Ddvsds>hc z4L=~LOl)S8d_6?zApHQi6XmYLa-kMARuPM|3kio_G$F_-CL0k%!Rx;7&-|sgjd2I! z0Aqq^j61=hGhMCp^2Heti_bPWObkR)aQEF`j|FMBPqr!?YXjrJw6->ZwYG1*IMc?B zuitU*7|uKCUFbt_mJ`Z2!t5L5WpdzV8>>eCbVEqDS#-OGwVA_rCvdk?!ClDePU3Fm z_U#`(nu}ipbvMWA4oIt#xXa0Ts^IQ#5_c(iRo9;R9x(3O33tm^s8uTU@Ni7$t)dkx z;_~Y5f_L^K;2ptEJ_PU1U8u?%p6kHgFfKAOPR&b^m}RPVN9T(85Ztxf=0``LgyZfX zr$3gsySqTN6}Wq}Ak|$c?v^R)EUSS42iLH=!-PHP<)rNhUxBl<^i%rmOG@5_Bv7-ToZ~ zJO1!#ACz5zn39(5l-7jX)n!W}q6Ie+N!=0fetbTwvCZZ+AhwksJXW3fHiNN;0CEQ|iiaIVg!C%tGlrMvhz##bW z*og3C#*@|D-+#)T@6OK7XDfZ|s@Pbrc!lpyL_`Y|ak)+r8t(@9vR{ORG4fhSHH~#E z2ahtRvZaToof3DEeB;mga*COc|9@u@VY1mk1|voybS`K@z@|froCKM_8aue&Vip)vNl~KY8TYXFf;7I zy>KD%nTx&VqVTS@^+0QHS8Ep<2zY9VIVHCu^qxhlK`%^oUA;Wwl%q)=M&fv-i%(VP z7tcx!77WgW6CHgAc#E?bN-Bd)9+C0@6b%bq!Ob52QT`!@oOt%O^&UFb+sYe3B^Y{h z_(f)TstWJ!zLcjbDJfg3^5R#@-Q6PYQnp`s^f)1??ihD@ggb&A5l`Z7;~a2ztsMnI z1hf|q{`uf>MMe9tHs>+DS(GZ~J3lVpCr z|ML^Fg1oF+?(VphRT*N63>EPreRoQdSOH&WVtKd!C<{Mi#uNxQ0)6|I?>><(_VbcL z1RgqKcSp!u``XjYi~OO8yQPmIsp}A^a`Cf^LpKtDIbpj)kl3mO+^RU0=UY7dVt*%3 zR=$Vz?oAEmukI?VeB7sMCH<64^2%V+nha(Ku>o5!U1g<_D52FS!)SiQR;0o?ie@n? zMXd=&W5|p4n7EJ&6|t!k8$OW|#g=4z-HN_N2qmOC%qC=iS+#mYnyXT8br9?aU{ceZX!w%@~n8V8$dUo&x+8nG;10XHc@D9f;creGHecqjiev_VT=Y}nnCMw z8SQCio8D^WGf%U@N>6=8u1;%mI4o9Iu3q*2uS>Jv!O{*kghU9vd^gnH-r7^=vw*u1 z(rnkSdaf_xZvEl&NK`3lCTP`6O*f0h-KCELzswL8r$z(Uvr76!n|UqTV)NY}Zh(X= zO{90hBh-V2_FCp#vgAOi^?} zi=AS(h>*L=2>yeM-5ud>4XZnGm>JR=5Q@9gTf%V{rtT!}=+{#6^6DPrRs^_f&tTj| z6YhpJw_e=(6}?|V$M77%YJRS+qC%k^@s8ykaF^GSuyW#hhCpKa zfOnaF{QY%&kXeqqOqdi1cM~@!3X=vVyxu~m-|u7}IcAuCwA`h8d;C2U=}tKECj5m- zqB#MUyCh=VL`#DgJ3M+c;#B3+9hsTiGS_WG{ZHz)%mO569{pfI(Q~1!tJzbES{+pq*n5OePj+a=03NAV@P5Vn9;B3{b{8G-tIMaA|DGPBw5Utmd3SKMT#>}wi}&aAdB5L^)8z9R6|;?`lg*@2%#OUdcaarBQ^)V49BYFAAzi_PRRx_nl%+oC9>;jC7~q@el0ZFRZ1 z*c_Fwh)f~LrW6;$Rbe(8jR-_T_p8-rb116Y>!gnqmr1p_y^7myHrt&!#daN5cS6;H zcL+Y&vX9l>vDTVhL)9-{RS%<2{Asm}@Z$MlSoqAJ9Ih!lJn^^&VVd;l#000Rym$te z`M!O^;f?69HS5;UtIxdrk$tC5^|ktmrPbp+oAmSGa_ufo3#&$c=F=w_Y3`ahe0YeP ze?*U~zYN)!0=40A>tlae+1fHr5v!?L?!Sx-v9)lf4-LP51v5lAe2mK&V4ugO$|uPj ztuoIrbCoGhrB?imEl)2kb_RWXd~|q$j1Ysm`>rl+Kza|bLPX(DZFg@iZ|ivA z+J*We5V?y)Y2Bc$@NIY97xA)CU0V9)>OTOwIr3J2Rzx)4t8lmq`dzDp-=MMcQfE~o zL>7~&Nl+ih zJ5Ldxs$g)*ySl4cC;423K1v7fKs$lE+QHhi-mY4L7&62#p-e-Z3M}WO-g*j^r?UO@I6K<3HrA}sDU2et+R=~qeL{qE}7$Ow}YqcO&$Z#Qt@ecXxr zNj_D9y!+St(vG9U#4s~j-3{LymJJBn$_~ozX8!s7e}5x}n90%?w_rgoZdF!CRUj<= ze|b01194X@+kjFQd~~LYm}W-4<2IjFRWploTuO#w@hWOCM>X3OdW<;abajyWQjAJw zd+MToTX4P(E97{cevtt^K058BAl~ra|J`IL#v&9V=;QS5c{&lUiOx?0a2J;vuWLuc ztHrGvy*amY2xBJ9B^_?JqA0fZ`I*)c)F0Sfu-Ro;tv(W-^hp`SFAhv0ra3Y!2vx!Q z(P{zOTqecmgCIm`n76{&;R^P(B56TkNRxBn>13D8Z47|ds{pN(H`g|@?4 zah-{POazAwiL4cRgkw2tWYbS; zE0-gnAECANF|a3%5#c*`!orY;!tMgTH#$1larbR)mrjfP zF^HNf{oJU$Ozl2%_PNT;81rk9B=+szs-}9l$>9da9jYTRJa>nyMnhOg@2O<3b~f?RuI*98Jas^?H0AxXSSYEj^8w zn%HLELSMf{Yzi;(flR1{p2mZrP9&;C%71ue_p1+?%(?F_J3BJWEG=4=B+|BKT2K@8 zz}?>;q$KV*o$Yzvfr=kOnx`$|yK4rYfV;nbf2K6o^Qouk%oI|*fV+`k;e!Om-R-fM zw&{_W7{lE8sh*=j_sf^hwdLL7#VM@r&Yiw~{Y*BZEEAIw=l8eakc};Su`x%V=O!}l zmI&OHR#;y?dZgiQ$;RtHL~wWT-1P^OQ&ZE;MH=psJcPTcQY}{{Hd@_{^0-fSAh4DL zc6gjwkY2z&tn^nwWp8=+((bNq9%-(|k>)D+%1a)?cekqK#6ZKgv^3IxC96vwme41$ zF%KXdAPiRaE`9iL^~2iHKkxqTt4*5&wqb-F>KD7Su)8CHw^<}; zYPrzjLQ{jCxJ_XNCk{66^WF~kD9B_vsYQle%?cFkKJ?Y*182{k4g7%(3istOGr-n9 ziMxf-w2MtnVBGP!3i0j($P?Q;EmP&@@Oy^4^PC=*PjB*B-N{@Pyj_s|NfL5Ze$tgn zMBnxFM5^yT(t0;b*J-#@;7v0Zn-!zOT#;!HW-4~OSG8JAB(scWlf~&(?ZN*3k$zjn zqBy+{u_`(-s)Q}UpqraIS34_+dnZyXF0Q>zXMny-&BM>v?PI!jYfuMIGyjSoQ@wV# z+2$Q-PrdE%nh;a%^SLcnwZoa3n%QBot2UF{YJtOCaj+x7rzj4aHN)bz`dpc|4l4;g zG`wcelg=E~VT4Byv&#&{?C8jGIvk`;vDC9>sKKCJHRojFRK;yInOr`_=yL`gF5Ty! zZ`mo1RY-mD-&^*bI!59S$(Go!J$p7xyqg#jD>OOH^b2{nef#z&6RnVUu%3%F%WA^i z_?^DKQ%8=-46$`<)`O-ifV=f8{<31lZzm=u!ZqWVW@2?0zKrncAr@!5mamnLyTgaK zBSlOu>~NO}$c4*lTEmaIjls{+o&L4F*Msw6`Aml9mDjJXjQ1TmA~IFhuUoT5;7;Ub z&XZwUBC8qN38MYpXxEFoa2g;prJNg75tK=4iLX_-V1P&i!oZDQUfzJqOjdW%d6}XK zNZQ2Z{7^3)cm{2*sU5b=6;~@_yB8e^t^GNQ(??am>PBlz8V1-0h&$14+^bY}_jY%8 zSDxUzOtkfZe1lMVqx8FearbTapl-$p!K*_0eSFA)5D5^|c6O}l_I@pb#5(IYw6w^R z%)$#YL@acnu)aw&(6xjL>w6mOIrWP^q9;P!1bZPQ-qW-6pEqiknW$nFmnv0N7YjGw z9%VzQ>B5G3dXGG^5TeBjFZ>5VVwW15xY4euUJEfpuo!pZiPAm#nwR#^7#y@q6}-`k z?tUH(KBTJ5;BI44uEd>00&I5@cS(ua2eWe@ykXpVJkt3l)!nkiDMiy7?mV8NYd;W) z9!+j6x^`6HP9HyitSu(5g4A8SJ|Q+GccjAQCY`9^4!gVT9caHJ+$|=a z001BWNkl;-9N#+-~8hpX?I@4WnKa6emwtvBDCOLKExg0soc!Ju`JTS zn!GR}Ibp%V(aN8`xcdO_`eTzn>L&C3UkgipHd0i`A+aEH+2JE- zHEOYnNdr8ur7g84baz{=>=TX0(`o~DXTTE}?MfR}HBZ3rQuK6hL~}-5qe0ALs;URp zVLrsOIuTS(Le4;bIpGaJAchH^Xj& zX(EQuy9JBp3d>B<7_-UvUCX*?^q+fLgocxe0zBgwJw@a|dN}MT!;QjK>=ehl0(YDd zj@Ld`t8~Fkimzuv<#PaDqKrPYNefjuo*AElH+#C*#;H*;!<88BFn&qAL(I=H>_gn> zCG;JO!q<~%7p04)Klqa9>0{H=`7Ke4sJP2Z0*z;nsl*4ozAO{KxrRx++xu&|iO>!= z=rb_)>WBCu^#%to5_$aypX2p|Gd=W0NVt24M>a{bvF#Tu!63%k>D${S?8I?UIJI0N ziN@+pD7@eGa~wK;0M2&i_dTus9EILbKMi?zoO=hA`3{S{OZdW`g9qb>RfM{G`B`x% zMp5T2-c}HYyX<8tF*D^@Kpf*P<%NRtz}-`cyCP0lHPv0lvVBkAd+(9JUH1Nohot;w z<1V?oJ1K2&d=6wk@vf_5YHF~)zF@QD9VS&?yNcMm^V!Q5r!4NSNUo^hT}=GD&!5{d zk;k~p1MV^ycTc3ci^CnOyK}FdJ2^2iH3i)5k+?%K^HIl==W&;p9pzFV0fPrJv2M;I znZ;|jAnR^5BPgr5>eM}WrR102tIHHVndN=u@Wx~l=(FXg!thUl$5=jZBINTnUR_^b zZ(VMFW^XvRynHyn@$|^nmwvqU7jMHG8oG{tR0&|0Rb0BWW+mNQyBhM2$0xZ6iT2zz zX^<%_G7;xmZD!#K+M$$rK+J9O_o0VJXW_2h7CP~9KG(a#L9C?u8sC{IW(p#(?+L5B zuXs3<4hG%jXV&ii!0*SnYd7|6zTI^;Q`rIF;r1=>-6gZQ!=2>h6&c5V`+pxdz&nAv z5AzoMFV)QNz}>?A5#oVjXo}D2HHamu+pBAKJC@&Ee%)E?_c&@@fuPGX+SS&LnFcd# zlgMWy{KWzgY;iBo%?VAoW7=Yan&L&9B`Fokj(5G1#?ympAein+Z}Zz7y2GP5-GhFg zrfHxq8B0k-#A-g<4=TXuJ zYXbyoUAOyu#6gV+ZWvzGO}`{@k4P7=+f|p{7q^H|R<;X8$1)(|O?Gm=0^GgNZkD3Q zhDzdnzVU$sAl7-@Kf{gyikZ_NPY!hc@dW8R*}vPcX4ludHk55->A33d-3MBy%buF=ThV(W~E#|$T5=;COI;Q%a+@>ug`f!#wEzjyGgzZ?%g3@ zmR(h1o|%glaVWyvB*`NVMo1FYr-|#Lx7D8wlQKer4rdOqRrJURTXgYfh?c|p9ahjA z!)IlhxoN5*^&O4`n{CEPMVgtIAX#aqBiID)rib}rE?i*l zvexXo(LzujAx>YofYR&82(KiZ8i|H)O_zsnTjX{GJ0h5Qcd<#u_+@i*IqiqiM2wda zWDqOnL)zIRY_x>ged)B({9UxXNG$ z;clO~i;!J#{^Td@5}l3C9pNr5&Ga7x?uPP~d&1!NyNK4 zF*G$LakqzYSO3l5hBBVTT?VPU=uBR~ap9V|HjA)VgLxIWL(@A%7&O8P3bT*nly zD!DS-H8(TdBBFt0nt49EN9W75*W5n(Z$nh<+xpV$B=26|S&UerEjTmb^z!QB($dnZ zEUsgUnavU)m=_GHk8^`gx?;jn;zpRqJG3QCY0WhU%f3B z5e`VhmIE*qtDZ3tGntT#X%e${2jInOnFm`!B{t-I@E*W<#=xplV#`cBj` zcVH1AHmX9nn?L`>6&K{<-SY3zw~Oc9XP275yYWT;aW>`O!fxThE{(9_G*nV^It(XC z{1~v3v}xcd%wIUuC1>V6n6r6_!*PZ6}|fY^LFpu)LSAs}s3~ z#iGud*nNk-Jq<77(g2e{Y`>aZL67e6s~%F3cHQICBEGiT5ZZ~VPa*nhDyo%4y%6a# z!$(N-g?yplpgrQGYjy^L4*E-U?nnxTB6W;g1m?j}Am2FzHi3Hvz!re{AU18z-tfP2HgmBl{dH;%;cgNnD zuv*CxMqaJan^pJqPF!@SyGpxrYv+dAA0< z720kAG7$)OHpv-NluH}2)8Rdl5#WtbH{1&&v2Y^>Rd@=d5u^U>Ds}@)0O8(VnRh49 z8Cxt6`3LcdzjC!a7L#F%%Rvw|Mr)#+(6r4o_7ZLJ$x?|z-GztG-oWG~yM(cw3Q`eT z!i^)mZ+8kwv?Clxi;vM8x2F4!t+Q}HF(E+;Px>=&-n{4HUD;+Ueh*K0tH{hdUwd19eJ~GcM|GuXlSTy zqGR7viMy1H#r5Aj94eZPJLID0<#l0f(WC?WWr{8y;U}vG~tsf`;xJmO^hiBG1dq&t|rSqw5-%*VQ%NKK+-SM|K{mBFYul6jv1& zmsS;5(NVKHt7>&s@s^U6h{I!h6%dz;l-I<@9o$q%*B!5sad@I8$+jy94wA5g0Vwa3 zuU#oVt3F;@dYl8(aeKGA8Y-{kr@xumyE6@zd3;ioGFjYxwejm&3+7_qPBiWwKOcY~ zcMzC&Ogwtq%IAC`S%9bZ_u-|_`rA_e`>@Kw?*!gH&Fyli z3WSuR5a>c0s)ivI5$5)Y-juE>E>}}+TU$^GRwRn%2g;PTMC+ZduEB0gOx0QA@>T_9XsH+{>ZLBFzp5BaSG@j!-|p320hdeBoL(j9QdQ02R8-BwiwAT? zE}b2rLn>Ad=;H83TF-5$T1cgbe7IzH#6KP+P&-Bllb_xqbvIDi`R48uh*p&*(P9YZm;Ykz`eT~R?{Iflgi_e)PSdna zThwd4#YzgK9wEla1_?t!2NYIDa;7;^GV~<3Ah2wlD9A(sshoNsS3?kV{3=@FAIq2r ziy{v2$-(&ib4> ze-PTt;h8J*Uk?*J4F~)o$=x^%I)#&=MJm{?%`hCW$sGH;=3$6b_}09ulvj9?0fq?X z=wx&{g1bi#3GE;Zl>^n)tJ8&2C05zp$#J4uA0Dk6n{2=O>>N)s>+8?g3p)Y|Uh7$_ z0?cDp2U7y5o?7n;$qAe9w9Z$SeXsqkvBg*)+S``4Et1%Z)rxpQ0NO zs&e4KhiI=G>gyQj8=|{`j{g1ugom-k4hdiV14ADk*E zVQ7%w?NbhsIEIIKnofJHA7{ezBDPGPn68BmSoV%7nVE}9?w+qTSMDR&U0#-$Wk%wi z9!7+$Gxxt;6~XS97EP$^ZlqwtPv58}xVwLwtMKwVA77>CKfRfpb+bzhGwHS>POqK5 zhQD#WR*&^v_5S_9JNDis(`z6jdiMV9&SX@NuM%)4*2?<0*b%^8HzA&YyUOY|dnCI{ zj%0WAL2x%Rx<3-`s-NDu`4YG**f7OSI)&1;4H1Z!<(QdJsX(8ZL|>uru6QGop{uZ$ z8*gt-1K4o~Q1M1COIY{jt?^=(YgX0cyC^f~*=E|truKD>=UO-3Dqh$0`O)$Uk4NN0 zBQCnUqN2!ygq6(Hq*RY5vyfmg)r-DYug8OCA2G9LzB`#CreCh7yOO~}KfvuP`bWU}%&!V!zsHKO$V$IxnAuBI%1WT(PPCeR zRs?amiB?%{8AOJhiFVuc*-Z(ie22ra@2n14Oe?g}yt`Y`);--jp^4HiR^TbccQO&0 zI9&{ZU5qyB>?#c#@G$C%Z=2Sxh+om*bDPW=2AkDnBqEmKG8)WQi^+zrR=3OKG$TmG zm_W47?zAwo1J>aNqoCC;q$*}EGBo_TGdxYcKIWWU*MS=z7htW*8j_ zRv7pUW?u%Dc}^$lT}61<@+Uc{=-C3m1K44hiJBg+S(PFcOb{97n9`-B$o*Xk6q4B? zU|2YO*eADqIuSM=DcIzwOx=R>E=Fk(R@fbEcE}NH5*@FsCFSem zaVGu*Sl+dt8+&&3S?icaw_uXs*N)P@D&F`N^TC2z}_@9vo0#l;c3%d1R&eG%LV z-(A*@#{_pfewVkQ0dQA|2G@cvmt=Pm?jB2aS6NWm`4lktmz#NK?sc_oy{G3DAliJo zrgwt&h2LRwcYpSiPiDXAWT!hsGEq7iHm6_m%E)NJ8v^cgsIfV_64R`d_t?~0!B29%5vg*xspKf9_SwV)2C$v-XG}E=Zj1T0yLH@P$&4^ljj@m=B{9Ohl9!8 z$5$4ZouqcWwUe8>-Mjw-xYP6UPNjKEq%;$w%(VX>?;LV}H#D*I`;2$$e)0vhTU-4v zx5aF5n-dIn3qgw0ZL#$B656@VwtR=lW^}qorj5M@hiPAa+o~9Z?8bAm2f4w=W^zWXmvZ zGMHTnkZ2NdblQmmx}8pUuO;6>T+!xoKO zN>u=NM_3`LCYV4H$p>OA4;~CO|A{zOARG#E5KDOO?h%%%)Yh^OAUBuMZcF;woG;d| z{ndkiJ}7%|Dn#XE%rZb^dUeKGpGM%!PyM-tNg0O*+7iRhc+Mhf|d7n3sT}nVg#J z-P*{g#xW|uJY;4zpM(s&v09rUV5W zb&3;D7hy5iDM4cLzfOR=)=Q@@fxzL4*c(U$^!)kO^B|m!oD>aR%{gA&(Whtg#f?)M zSxqEr7#RD0ys*_$(C)cvSB5*TD^P@)@4r9rTWC-qA(81}9ET}F#0D1N?xT||RS_X# zoSAuoz8oTKq=Vot!g;4w$0AH~h||G17n2XE_kb93eEXO2g}ok*<|a5svQ0*1SL zUDbqkm7}u?-2J<&Ve37`?WWh4bnBIz=D3v`B;IjSbk>gJx9{Ki+Z}?tO2z3OC8q#d zVt1pX1#4c;?w*6Y>iwOg%bhM(p+*4juzWrxMGq)veqhH>_%*>w?I+fD}WF~pw_p1TFLw%31+wEz&=#`?B z>({RfD8wF5+-kTmZx7SE7V(hFe>nHEP%yvKg5?!1f;++OmN2_h$(_KvD{^%wTFgQ3 zF3c@j*a<jiqB;V%UiZ=Y5So=KMcS7JNTWn-Yx&%tGm~hy`17z`w|>h zyU*E>Z*@Z>Li{d~Fv2%cGGVY8-3EitXEirWB={UAQ@0pEMT<8dttS3jx613HwNWvV zt*!z9>5T^J%$TzyZ4M;iiPAG7iShev4Q-A@Q{pC@(c!kkT<&mWpgf%zU82S8BnoCB zsIxeoHlKlz&Sy=uLuKjmu>}wHEY-KaOoRqAjWXtlOQqgXgEtYvK-vru*L;l)2e=raV z^92aBO<$dxD;9ULqW&i~mZy!1J&ymULL^-Ci?I*Jw~jM6f)oVK^7?jWOifKPoeQQr z@G0hZ0`63)3cKjE3uA5B;TqX}Cj_Cwj=OFyQ))zxDa;P zac6n!cs-)4xe=jBq-IV|g4vCYO*PHWUut|?qqaYA7m-d%RbE}J%@i@pilk9^?}YD; zaqlq9<-!TxPmf^q{x56i9~1R`#_>zT^*DjH%Gft|*ZZOM7-Z+=P%pKF5a^DCNT-G$ zcS6lZE-iDB!B{LP^S8)Qt0-y_3Pa)#z@aFfj6c?4rwtpS)W*ax;vZex*tAZvW@*%r zm_6_3`FxK9bz8C_AmVvwVto02p7-;9i$$d$Z(Nta))kmm5EzCc1C=?}OAolvdw@RP zg|<5-!lV%aySrYL;YlW$oR~|=K`#f0;a4L@Og`5xC+6*9j_ExrN9OG$?%t6SJ?6DK zx?42InfZbmYU;bY#KdG$cTeDsWm#fjcmAzd+{F$vi8oV2qVC28ceS^!1ofl|DTy1hii>MW-Q_FXeRKDtsgFe6F%U8Rp%>U(5)+1oY6W-c z#k1;e3K?ee)g8f(c?aCxpH+9K!{hg#hPxk}iv(36IERmvHb=_KYxoY2r;U4`H zQhB8r8KsOgz8uWsRw0!6g(^$(xMzsf-Hu>qFxYu#UN*EKguexfzXN?tu`NO?A$&J zX*0XW=|vxcjWF!TwAJq(uUVW=zbpzyA1%d0@?ROu1b`8nTeD?P9J!{@r!?VojEkLdsmhM3b&>tRr z30G#tsmo&(a)Gcy4iHrTkE&tdDmz&qu2z^+6|b!A_=JJzOVAq|quWh-P({cA4_XC*Yq7z44mzWIkHz%4lKNb@R>$yJW9h{C_mWo*>7ryS3HuJ}8Gn)u^ zN$kor8dWf*(P)mJzjfo_FzHW^Q`vGn#1u=Wto!2Ql$}Mdb&ah7QqIf~K14Xd<5WI2 zM^N;MebB_dWlkp~EIB8VQQ;QG))Ms;acG)r`KeZ91|Vs{r5n@Ux4^`^)#KIyFQM72+z!ecYXk-@ftpt49wNk%iv zJ04>a^>%c2-ueDq+(~J{!dcM$cvjx&z`Me{8@+et%4|U98KK-fWykM+!n-?YI=)3v z2jMB_fAjy+cm03;s{gBJnN^-K#EjY9xot!fcaPtzg>4uS_SmeN$L@#k(P_&eE`&c$ z84T3rXpI|8M!j>IakoS(G8@b$l?-i|$2u`=!rg)iCI*@Oj~#?SeB8(1`WsU{R-esc z_c^T&x6`G$io72vgi=~E6HY%4fTCy8q9>9KmU*f#{4Q_VL zg~H2~4%YR=qstg(es@hxGPz<7@b956cP>cdONF#n@Oyg~YSKsxm%$>KN)}bJY>To}KXvFv`Tc=i(*!3#3un zj(PXqMBnr@?3ercIEM13vq9{F@IjN=Xx4X&ggYZH*Pmfjd0~DVkbZN{EvyGnAFdRYXfe`P0}}T&z_oTD5CDaT z4fI2Z9hCySer_G_Z&$Sis-Z_U-Vr+S=z#&H3Fj%fUeK?%Uv=HFR_v9iYW_KHNk1x2 z8w+;#jGxGT(R;bCT0`crT>6UE)zv$(rUxO@r00DP*r>4I6$F1F2j63{i$&9US3ghnYQQT$8-QA#ra5v4k zgH@#?26xAQEwtNOdw+atW@emmS3EQRXzN_u;U#P71kC%;L5v5*KEWM3R#4Rw$*4JB zc5yNrG~+QQrk8A5VV4Tp4q(k>+Ckcp@@qpQ0yp7hW@n@19J{+M2SXTP0(Yz6z{Cd` zFsW2l;!`D|>PmcA1N9eaE`Mzk3JvNb`)jxcp`F`y3cCcqf%%zfw z#_rCQ!IknySt%v5Sl#`oD%iP0;cj6p>}(i<#ltVJg#7`RX1DnKHjT8M+wZYxb*bOFnRI@<+U>X}_krEs#yZlcEkzEu*9OQs zogl4E3%Hy%!>jApum9N(8F+#`rV-BW6z)d1tJR(OR0Ri&>FuP}T->p|J6lrC;cK!DdlZbTq;$9;Fa8`FErQ7ot8DT#bh_e#79 z88gM{U3PM9r~D((c9K}Yf0sx)A{{r@s*Ljtq0M7@V31B`3O% z4-veZA74BnDQCvGB)lc1FqhRGn%-p2I9L>WZLUXVzWFOvdxtyoE*zC}#@G7;xGP79 znEn|~{_5|&An9SPT#bjtURP^ZS1Z56gL-#GYkFd=nS1+Bo^9=KJ^Q{~tII0zEC z0^v#e+b?j>57*+g@@+ZamvcfHdWlYUwX5fq#ofz#?~lN5(V}_n2ur(T)SciiIhJ=a z%#7mhp&Djp6%RVbpTONM;BIU8{V6rf6x=P2Ht#G+C)}wgC&J?F4V~t-%L@w??j8=+ zrl+SQ@Lz-^ZpR=IPJ)new^gaTbhOJl9?0@;F79}D*Kz!K4DL2h-4J#6aQEVQxFg~c z?)btj*o>_gc6WT8&HV_Hse-iVypkhm^#Rc`hfnO|!aJT^W@b-jJ zc;EFlw4KwB+VBZ<;t?R54n=AXRflTe!em89)XQ6!$KS78SBg8lN*v{tW>l7Mdi}Ni zAliq``>P{WO4PAcGstZ~A2R!zBREOGF0T`h>1@u(!-9^@H@HcF%M9*aKxC#Dakl? zJrP8AIDfPw!zqXJ7odeGpaun;8t;#dn|LuXDi{*Oxy!{3leu}A+aLaz=9c@sKi}`u z7Ia?jR%}7~OQh-Rd4Ip}_xqzeGIeP~Lm#RlKas`wwfGQa)FYe?Raz`H!_C7sYO!<{QlL2%pS@cPoyyl$`RO)IeF*-Kmn zPLE1@$cA`Z8m7Y?Gw5?zSPF1j++>O#g5&*5$DN#cX6!zeYQk~7!6X*4_|U(*$CjFU zvn2rd*j%=7i{dU&G`r?iNORepey7c^`g>HL$I(+#k%tCrmB$&Vq}L$c6u4E~0mY@b z-I}U8NzoAnot_@0&fOCblkfrol7a+H#YPg*(Q>oS)pGr;*Y63q9hzHpV}31Nuef|R zLs4et2FwfKGcP(WEaI&@5i>_;CU6I>wF|u}>(77r)mM8EE#I|ERIDJAfbr(9FYdoe z>JEE%SGhrK|VlXVa@osZX^GOt9LOz{DE%bga<-+nHY%08p$uR`(%skBM&M0FCqLVCvMV{1S zTn;W?5+<&z6gzxU*NM!X$my{#w3y?EOG(=4TX>AStOrBbyJJ&klgQnL&?B~=-75&g zGw9${85z0wVDqUFgOQ`<5O$b7MA$PYOpDG4z~w9Y+@Du;M~Cdpd~3O!cL&_{agi4x zC~~NV9V0N49Ad8~e3%$ghu+LC%)9Gq*GhNqs_AMw2v=sYu&W%ZVV7uxo;6b3)%!s#z`p#gXs-%; zZhjLt)tt57v=`wnDTX_-cekSbXj0NApRLGfc{0&1)!k&=)JE*(yFyZbs z9d|R5Q>M?HPB@LqyNulV^H(B=Q2S0h=MW~^S7uZs∈_`*;VjSdy(~!rcVnZe@K` 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 z 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%0DG73=t(YGVpH_+7`OeSvNII*gaymr)%Ee~V4 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

  • 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 Date: Sun, 6 Sep 2026 15:09:16 +0800 Subject: [PATCH 18/62] Default native routes to TCP --- src/main.rs | 10 +++++----- src/vector/config.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index dc134b5..069b287 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,9 +74,9 @@ Portal parameters: Example: shared-key@host/tcp:2006/udp6:2017. Omit or use none to disable. Mutually exclusive with socks. up=tcp|udp|mix Native upstream upload carrier. Mix chooses per flow. - Defaults to the only declared carrier, or UDP. + Defaults to the only declared carrier, or TCP. down=tcp|udp|mix Native upstream download carrier. Mix chooses per flow. - Defaults to the only declared carrier, or UDP. + Defaults to the only declared carrier, or TCP. mux=0|1 Use TLS Mux when the native route can select TCP. Default: 0. sni= Native upstream certificate DNS name. Default: none. pin= Native upstream certificate fingerprint. Default: none. @@ -84,8 +84,8 @@ Portal parameters: log= none, debug, info, warn, error, event. Default: info. Vector parameters: - up=tcp|udp|mix Upload carrier. Defaults to the only declared carrier, or UDP. - down=tcp|udp|mix Download carrier. Defaults to the only declared carrier, or UDP. + up=tcp|udp|mix Upload carrier. Defaults to the only declared carrier, or TCP. + down=tcp|udp|mix Download carrier. Defaults to the only declared carrier, or TCP. mux=0|1 Use TLS Mux when either direction can select TCP. Default: 0. sni= Verify the certificate for a DNS name. Empty, omitted, or none disables certificate validation. Default: none. @@ -121,7 +121,7 @@ Portal binding: Vector and next dialing: * is invalid. DNS results are filtered independently for each carrier family. - A single carrier is the default for both directions; with both, UDP is the + A single carrier is the default for both directions; with both, TCP is the default. Explicit up/down must exist, and mix requires both carriers. Transport capabilities: diff --git a/src/vector/config.rs b/src/vector/config.rs index b69685b..71ce082 100644 --- a/src/vector/config.rs +++ b/src/vector/config.rs @@ -97,8 +97,8 @@ impl PortalClientConfig { ) -> Result { let remote = ServiceEndpoint::parse(url, false, context)?; let default = match (remote.has_tcp(), remote.has_udp()) { - (true, false) => CarrierMode::Tcp, - (false, true) | (true, true) => CarrierMode::Udp, + (true, false) | (true, true) => CarrierMode::Tcp, + (false, true) => CarrierMode::Udp, (false, false) => unreachable!(), }; let up = CarrierMode::parse(query.get("up").map(String::as_str), "up", default, context)?; From 53be4f5bbe42e3ccb72051b246aa853440946829 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 15:12:11 +0800 Subject: [PATCH 19/62] Cover TCP route defaults --- src/tests/main.rs | 2 ++ src/tests/portal.rs | 4 ++-- src/tests/vector/config.rs | 18 ++++++++++++------ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/tests/main.rs b/src/tests/main.rs index fe93ab1..20ccdcf 100644 --- a/src/tests/main.rs +++ b/src/tests/main.rs @@ -26,6 +26,8 @@ fn help_text_documents_usage_and_configuration_surface() { "mux=0|1", "up=tcp|udp|mix", "down=tcp|udp|mix", + "Defaults to the only declared carrier, or TCP.", + "with both, TCP is the", "Use TLS Mux when the native route can select TCP.", "UDP ASSOCIATE", "rate=", diff --git a/src/tests/portal.rs b/src/tests/portal.rs index ff5195d..e00bcd4 100644 --- a/src/tests/portal.rs +++ b/src/tests/portal.rs @@ -143,7 +143,7 @@ fn socks_configuration_is_validated_and_redacted_in_effective_url() { } #[test] -fn native_next_defaults_to_quic_and_redacts_the_shared_key() { +fn native_next_defaults_to_tcp_without_mux_and_redacts_the_shared_key() { let portal = Portal::new( Url::parse("portal://relay-key@127.0.0.1:2000?next=upstream%40key@relay.example:2080") .unwrap(), @@ -154,7 +154,7 @@ fn native_next_defaults_to_quic_and_redacts_the_shared_key() { assert_eq!(portal.inner.outbound.next_endpoint(), "relay.example:2080"); assert_eq!( portal.inner.outbound.next_transport().as_deref(), - Some("up=udp down=udp mux=0 sni=none pin=none") + Some("up=tcp down=tcp mux=0 sni=none pin=none") ); let effective = portal.effective_url(); assert!(effective.contains("next=relay.example:2080")); diff --git a/src/tests/vector/config.rs b/src/tests/vector/config.rs index b407e0a..06bd30c 100644 --- a/src/tests/vector/config.rs +++ b/src/tests/vector/config.rs @@ -5,10 +5,10 @@ fn parse(raw: &str) -> Result { } #[test] -fn defaults_to_quic_both_directions() { +fn dual_carrier_endpoint_defaults_to_tcp_without_mux() { let config = parse("vector://secret@example.com:2000?socks=:1080").unwrap(); - assert_eq!(config.up, CarrierMode::Udp); - assert_eq!(config.down, CarrierMode::Udp); + assert_eq!(config.up, CarrierMode::Tcp); + assert_eq!(config.down, CarrierMode::Tcp); assert_eq!(config.mux, MuxMode::Disabled); assert_eq!(config.sni, None); assert_eq!(config.pin, None); @@ -23,12 +23,18 @@ fn explicit_endpoints_select_ports_families_and_single_carrier_defaults() { assert_eq!(tcp.down, CarrierMode::Tcp); assert_eq!(tcp.portal_endpoint(), "example.com/tcp4:2006"); - let mixed = - parse("vector://secret@example.com/udp6:2017/tcp4:2006?socks=:1080&up=tcp&down=udp") - .unwrap(); + let udp = parse("vector://secret@example.com/udp6:2017?socks=:1080").unwrap(); + assert_eq!(udp.up, CarrierMode::Udp); + assert_eq!(udp.down, CarrierMode::Udp); + assert_eq!(udp.mux, MuxMode::Disabled); + + let mixed = parse("vector://secret@example.com/udp6:2017/tcp4:2006?socks=:1080").unwrap(); assert_eq!(mixed.portal_endpoint(), "example.com/tcp4:2006/udp6:2017"); assert_eq!(mixed.remote.tcp.unwrap().port, 2006); assert_eq!(mixed.remote.udp.unwrap().port, 2017); + assert_eq!(mixed.up, CarrierMode::Tcp); + assert_eq!(mixed.down, CarrierMode::Tcp); + assert_eq!(mixed.mux, MuxMode::Disabled); } #[test] From bcafd52d9a947d59b039021e5ca8d8e302b1c960 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 15:14:11 +0800 Subject: [PATCH 20/62] Document TCP route defaults --- README.md | 4 +++- docs/configuration.md | 8 ++++---- docs/quick-start.md | 9 +++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index cecf9ef..3a1735c 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,9 @@ rules, DNS behavior, family availability, and canonical output. ### One flow, two transport decisions -Vector's `up` and `down` parameters accept `tcp`, `udp`, or `mix`: +Vector's `up` and `down` parameters accept `tcp`, `udp`, or `mix`. When both +carriers are available, omitted directions select TCP. Mux remains disabled +unless `mux=1` is set: | `up` ↓ / `down` → | `tcp` | `udp` | `mix` | |---|---|---|---| diff --git a/docs/configuration.md b/docs/configuration.md index a0bf23c..841ea1d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -105,7 +105,7 @@ sockets instead of relying on an operating-system dual-stack default. | `dial` | `auto` or local IP | `auto` | | `socks` | outbound SOCKS5 configuration | disabled | | `next` | `shared-key@host:port` or explicit carrier endpoint | disabled | -| `up`, `down` | native next-hop policy: `tcp`, `udp`, or `mix` | only carrier, otherwise `udp` | +| `up`, `down` | native next-hop policy: `tcp`, `udp`, or `mix` | only carrier, otherwise `tcp` | | `mux` | native next-hop TLS: `0` dedicated lanes, `1` Mux when TCP is possible | `0` | | `sni` | native next-hop verified DNS name, or `none` | `none` | | `pin` | native next-hop certificate SHA-256 pin, or `none` | `none` | @@ -132,14 +132,14 @@ IPv4. If no resolved address matches the selected family, dialing fails with a configuration-specific address error. When an endpoint declares one carrier, omitted `up` and `down` both select that -carrier. When both carriers exist, each omitted direction selects UDP. An +carrier. When both carriers exist, each omitted direction selects TCP. An explicit direction may select only a declared carrier, and `mix` requires both TCP and UDP. These checks run before the SOCKS listener begins accepting -traffic. +traffic. The transport default does not enable Mux; omitted `mux` remains `0`. | Query | Values | Default | |---|---|---| -| `up`, `down` | `tcp`, `udp`, or `mix` | only carrier, otherwise `udp` | +| `up`, `down` | `tcp`, `udp`, or `mix` | only carrier, otherwise `tcp` | | `mux` | `0` dedicated TLS lanes, `1` TLS Mux | `0` | | `sni` | verified DNS name, or `none` | `none` | | `pin` | certificate SHA-256 pin, or `none` | `none` | diff --git a/docs/quick-start.md b/docs/quick-start.md index 09cf895..2e68710 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -60,10 +60,10 @@ operating system does not support one address family. ## 2. Start Vector -Dedicated TLS lanes in both directions: +Dedicated TLS lanes in both directions use the compact endpoint defaults: ```text -nowhere 'vector://secret@127.0.0.1:2000?up=tcp&down=tcp&socks=127.0.0.1:1080' +nowhere 'vector://secret@127.0.0.1:2000?socks=127.0.0.1:1080' ``` QUIC in both directions: @@ -79,8 +79,9 @@ nowhere 'vector://secret@127.0.0.1/tcp:2006/udp:2017?up=tcp&down=udp&socks=127.0 ``` The carrier path describes what can be dialed. `up` and `down` choose from -those carriers for each logical direction. A single-carrier endpoint needs no -explicit direction policy: +those carriers for each logical direction. Compact and explicit dual-carrier +endpoints default both directions to TCP with Mux disabled. A single-carrier +endpoint needs no explicit direction policy: ```text nowhere 'vector://secret@127.0.0.1/tcp4:2006?socks=127.0.0.1:1080' From 2ba26a5f03682b2bf5fa892dc3a2c438805a4b36 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Sun, 6 Sep 2026 15:22:44 +0800 Subject: [PATCH 21/62] Cover independent route defaults and explicit Mux --- src/tests/portal.rs | 32 ++++++++++++++++++++++++++++++++ src/tests/vector/config.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/tests/portal.rs b/src/tests/portal.rs index e00bcd4..c742d0b 100644 --- a/src/tests/portal.rs +++ b/src/tests/portal.rs @@ -162,6 +162,38 @@ fn native_next_defaults_to_tcp_without_mux_and_redacts_the_shared_key() { assert_eq!(portal.inner.outbound.ping_ms(), 0); } +#[test] +fn native_next_omitted_directions_keep_independent_tcp_defaults_and_explicit_mux() { + for endpoint in ["origin.example:2000", "origin.example/udp6:2017/tcp4:2006"] { + for (query, up, down) in [ + ("", "tcp", "tcp"), + ("&up=udp", "udp", "tcp"), + ("&down=udp", "tcp", "udp"), + ("&up=mix", "mix", "tcp"), + ("&down=mix", "tcp", "mix"), + ] { + for (mux_query, mux) in [("", 0), ("&mux=1", 1)] { + let raw = format!( + "portal://relay-key@*/udp4:2017?next=upstream-key@{endpoint}{query}{mux_query}" + ); + let portal = Portal::new(Url::parse(&raw).unwrap(), test_logger()).unwrap(); + assert_eq!(portal.inner.network_mode, NetworkMode::Udp); + assert_eq!( + portal.inner.outbound.next_transport().as_deref(), + Some(format!("up={up} down={down} mux={mux} sni=none pin=none").as_str()), + "{raw}" + ); + assert!( + portal + .effective_url() + .contains(&format!("&up={up}&down={down}&mux={mux}&")), + "{raw}" + ); + } + } + } +} + #[test] fn native_next_uses_shared_endpoint_grammar_and_single_carrier_defaults() { let portal = Portal::new( diff --git a/src/tests/vector/config.rs b/src/tests/vector/config.rs index 06bd30c..138682a 100644 --- a/src/tests/vector/config.rs +++ b/src/tests/vector/config.rs @@ -37,6 +37,33 @@ fn explicit_endpoints_select_ports_families_and_single_carrier_defaults() { assert_eq!(mixed.mux, MuxMode::Disabled); } +#[test] +fn omitted_directions_keep_independent_tcp_defaults_and_explicit_mux() { + for endpoint in ["example.com:2000", "example.com/udp6:2017/tcp4:2006"] { + for (query, up, down) in [ + ("", "tcp", "tcp"), + ("&up=udp", "udp", "tcp"), + ("&down=udp", "tcp", "udp"), + ("&up=mix", "mix", "tcp"), + ("&down=mix", "tcp", "mix"), + ] { + for (mux_query, mux) in [("", MuxMode::Disabled), ("&mux=1", MuxMode::Enabled)] { + let raw = format!("vector://secret@{endpoint}?socks=:1080{query}{mux_query}"); + let config = parse(&raw).unwrap(); + assert_eq!(config.up.to_string(), up, "{raw}"); + assert_eq!(config.down.to_string(), down, "{raw}"); + assert_eq!(config.mux, mux, "{raw}"); + assert!( + config + .effective_url() + .contains(&format!("up={up}&down={down}&mux={mux}&")), + "{raw}" + ); + } + } + } +} + #[test] fn policy_must_use_declared_carriers() { for raw in [ From 99068f20b36bce22eac4d937f539a8fba2adeb4c Mon Sep 17 00:00:00 2001 From: yosebyte Date: Mon, 7 Sep 2026 20:17:53 +0800 Subject: [PATCH 22/62] Redesign TLS Mux flow control and relay path --- docs/compatibility.md | 5 +- docs/configuration.md | 16 ++- docs/mux-performance.md | 45 ++++++ docs/operations.md | 7 +- docs/protocol.md | 30 ++-- docs/security.md | 14 +- scripts/bench-mux-local.sh | 56 ++++++++ scripts/mux-bench.py | 241 ++++++++++++++++++++++++++++++++ src/main.rs | 2 +- src/mux/driver.rs | 54 ++++--- src/mux/handle.rs | 45 ++++-- src/mux/mod.rs | 177 ++++++++++++++--------- src/mux/stream.rs | 99 ++++++++++--- src/mux/wire.rs | 15 +- src/portal/conn.rs | 2 +- src/portal/conn/relay_stream.rs | 32 ++--- src/portal/conn/relay_tcp.rs | 4 - src/portal/conn/tcp/flow.rs | 4 +- src/portal/listener.rs | 4 +- src/portal/pairing/state.rs | 5 +- src/tests/main.rs | 2 +- src/tests/mux/runtime.rs | 52 ++++++- src/tests/mux/wire.rs | 16 +-- src/tests/portal/conn/tcp.rs | 4 + src/tests/transport/quic.rs | 12 +- src/tests/vector/session.rs | 4 +- src/tests/vector/session/tls.rs | 22 +++ src/transport/mod.rs | 6 +- src/transport/owned_io.rs | 143 +++++++++++++++++++ src/transport/quic.rs | 24 ++-- src/vector/flow.rs | 6 +- src/vector/flow/tcp.rs | 19 ++- src/vector/session.rs | 3 +- src/vector/session/quic.rs | 2 +- src/vector/session/tls.rs | 14 +- 35 files changed, 948 insertions(+), 238 deletions(-) create mode 100644 docs/mux-performance.md create mode 100755 scripts/bench-mux-local.sh create mode 100755 scripts/mux-bench.py create mode 100644 src/transport/owned_io.rs diff --git a/docs/compatibility.md b/docs/compatibility.md index c772136..9d2b287 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -104,8 +104,9 @@ and never appears on a dedicated lane. ## Runtime contract -Mux Shards open lazily at 4 active flows, select the least-loaded live Shard, -and close after 30 seconds fully idle. Dedicated lanes and Mux streams use the +Mux Shards open lazily at 4 active flows up to a 4-Shard directional pool, +select the least-loaded live Shard, and close after 30 seconds fully idle. +Dedicated lanes and Mux streams use the same authentication, FlowHeader, Target, setup result, pairing and limits. QUIC behavior is independent from the client Mux setting. diff --git a/docs/configuration.md b/docs/configuration.md index 841ea1d..870df92 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -223,7 +223,8 @@ carrier. With `mux=1`, Shards open lazily according to active flow pressure. New flows use the least-loaded shard; a shard carries 4 active flows before another -opens and closes after 30 seconds fully idle. With `mux=0`, every TLS-carried +opens, with at most 4 shards per direction. A shard closes after 30 seconds +fully idle. With `mux=0`, every TLS-carried Flow owns one on-demand lane that closes with the Flow. Mux applies when at least one direction is `tcp` or `mix`. `udp/udp&mux=1` canonicalizes to `mux=0`. @@ -287,7 +288,7 @@ Durations use humantime syntax such as `250ms`, `15s`, `2m`, or `1h`. | `NOW_MAX_TCP_FLOWS` | `1024` | TCP flows per authenticated client session | | `NOW_MAX_UDP_FLOWS` | `256` | UDP flows per authenticated client session | | `NOW_QUIC_UDP_QUEUE_BYTES` | `4 MiB` | QUIC datagram and reassembly byte budget | -| `NOW_QUIC_MEMORY_PROFILE` | `throughput` | QUIC profile: `memory`, `balanced`, or `throughput` | +| `NOW_TRANSPORT_MEMORY_PROFILE` | `throughput` | QUIC and TLS Mux profile: `memory`, `balanced`, or `throughput` | | `NOW_MAX_PENDING_PAIRS` | `1024` | Pending split-flow pairs per Portal session | | `NOW_FLOW_PAIR_TIMEOUT` | `15s` | Portal split-flow pairing deadline | | `NOW_FLOW_SETUP_TIMEOUT` | `20s` | Client wait for `SetupResult` | @@ -305,11 +306,12 @@ Durations use humantime syntax such as `250ms`, `15s`, `2m`, or `1h`. | `NOW_SHUTDOWN_TIMEOUT` | `5s` | Graceful shutdown deadline | | `NOW_RELOAD_INTERVAL` | `1h` | Supplied-certificate reload interval | -Mux limits are library defaults with strict validation: 512 KiB per stream and -connection, 256 active streams per Mux, and 512 queued frame slots. Payload in -the queue is also charged against the 512 KiB connection window, so slot capacity -does not multiply the byte bound. The application uses a 4-flow shard density -and retires fully idle shards after 30 seconds. `NOW_MAX_TCP_FLOWS` is the hard +TLS Mux shares the transport profile's 4/8, 8/16, or 16/32 MiB stream/connection +receive windows with QUIC. A Mux carrier permits 256 active streams and 512 +queued frame slots; queued payload remains charged against the connection +window. The application opens another shard after 4 active flows, caps each +direction at 4 shards, reuses the least-loaded shard after that, and retires +fully idle shards after 30 seconds. `NOW_MAX_TCP_FLOWS` is the hard per-session logical TCP limit shared by TLS and QUIC. `NOW_MAX_UDP_FLOWS` is the corresponding UDP limit shared by UoT and QUIC DATAGRAM. Excess flows fail without waiting for capacity. QUIC internally admits the sum of both limits as diff --git a/docs/mux-performance.md b/docs/mux-performance.md new file mode 100644 index 0000000..0dfb6eb --- /dev/null +++ b/docs/mux-performance.md @@ -0,0 +1,45 @@ +# TLS Mux performance baseline + +The local benchmark runs a real Vector, Toxiproxy-delayed TCP/TLS carrier, +Portal, and TCP target. It synchronizes the application transfers, reports +payload throughput, and samples peak RSS for both Nowhere processes every +100 ms. + +```sh +cargo build --release --locked +scripts/bench-mux-local.sh 100 1 64 1 +``` + +Arguments are RTT in milliseconds, concurrent flows, MiB per flow, `mux=0|1`, +and an optional binary path. Run samples serially so their fixed local ports do +not overlap. Apple Container's default kernel does not provide the netem qdisc, +so the local runner uses bidirectional Toxiproxy latency on the real carrier. + +## 2026-09-07 development baseline + +Host: Apple silicon macOS development host, Apple Container Toxiproxy 2.12.0, +`throughput` profile, fixed delay without artificial bandwidth or loss. These +numbers compare implementations on this host; they are not production capacity +claims. + +| Implementation | RTT | Flows | Payload | Throughput | Portal peak RSS | Vector peak RSS | +|---|---:|---:|---:|---:|---:|---:| +| Previous 512 KiB Mux | 100 ms | 1 | 64 MiB | 37.86 Mbps | 9.77 MiB | 8.95 MiB | +| Profile-window Mux | 100 ms | 1 | 64 MiB | 959.71 Mbps | 17.56 MiB | 10.52 MiB | +| Previous 512 KiB Mux | 300 ms | 1 | 32 MiB | 13.08 Mbps | 9.86 MiB | 9.16 MiB | +| Profile-window Mux | 300 ms | 1 | 32 MiB | 288.04 Mbps | 19.38 MiB | 10.27 MiB | +| One Mux Shard | 100 ms | 16 | 128 MiB | 1.81 Gbps | 26.83 MiB | 10.38 MiB | +| Four Mux Shards | 100 ms | 16 | 128 MiB | 4.38 Gbps | 71.66 MiB | 11.86 MiB | +| One Mux Shard | 300 ms | 16 | 128 MiB | 670.71 Mbps | 26.72 MiB | 10.19 MiB | +| Four Mux Shards | 300 ms | 16 | 128 MiB | 1.69 Gbps | 61.61 MiB | 11.88 MiB | + +The larger profile removes the dominant high-RTT collapse. Four Shards improve +aggregate 16-flow throughput by about 2.5 times at both measured RTTs, while +raising peak RSS substantially. The runtime therefore opens Shards lazily at +four active flows and caps the directional pool at four. A single flow remains +on one Shard. + +At 300 ms, a 16 MiB stream window has a theoretical ceiling near 447 Mbps. The +measured 288.04 Mbps is about 64% of that ceiling, leaving room to improve credit +return timing, writer batching, allocation reuse, and TLS scheduling before +increasing the profile budgets. diff --git a/docs/operations.md b/docs/operations.md index 7fc52ae..7b5ec18 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -54,12 +54,13 @@ the other family. ## Capacity The important memory bounds are the 1,024 concurrent TCP flows and 256 UDP flows -per authenticated client session, the 512 KiB per-stream and per-Mux receive +per authenticated client session, the selected 4/8, 8/16, or 16/32 MiB +per-stream/per-Mux receive windows, 256 streams per Mux, bounded reusable relay-buffer caches, and QUIC UDP queue/reassembly limits. UoT and QUIC DATAGRAM share the UDP flow limit. TLS shards originated with `mux=1` by Vector or a Portal `next` client target 4 -active flows, use least-loaded placement, and close after 30 seconds fully -idle. Frame queue slots do not bypass byte credit. Windows are granted as +active flows, stop at 4 shards per direction, use least-loaded placement, and +close after 30 seconds fully idle. Frame queue slots do not bypass byte credit. Windows are granted as permits and payload is admitted incrementally. At a session flow limit, TCP setup returns a failure immediately. A SOCKS5 UDP diff --git a/docs/protocol.md b/docs/protocol.md index 74b9c7d..806bec6 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -213,8 +213,8 @@ Mux frame. ### MuxHeader -Every Mux frame starts with an 8-byte header. STREAM and DATAGRAM frames carry -exactly `value` payload bytes; WINDOW carries no payload. +Every Mux frame starts with an 8-byte header. A DATA STREAM frame carries +exactly `value` payload bytes; control frames carry no payload. ```text MuxHeader - 8 bytes @@ -228,13 +228,8 @@ MuxHeader - 8 bytes | `kind` | Name | `value` | `flow_id` | |---:|---|---|---| -| `0x01` | STREAM | payload length | nonzero | -| `0x02` | WINDOW | returned byte credit | `0` for connection, nonzero for stream | -| `0x03` | DATAGRAM | payload length | nonzero | - -The runtime implements STREAM and WINDOW. DATAGRAM headers are recognized by -the codec but are not registered as a runtime plane; receiving one closes the -Mux carrier as unsupported. +| `0x01` | STREAM | DATA length, or initial window extension with SYN | nonzero | +| `0x02` | WINDOW | returned credit in 1 KiB units | `0` for connection, nonzero for stream | For STREAM, the low three flag bits are: @@ -247,12 +242,14 @@ flags byte +---------------------+-----+-----+-----+ ``` -- `SYN=0x01` creates the logical stream before optional payload is delivered. +- `SYN=0x01` creates the logical stream. It is isolated, carries no payload, + and `value` extends the peer's 4 MiB initial stream window in 1 KiB units. - `FIN=0x02` half-closes the sender after optional payload is delivered. - `RST=0x04` resets the stream. It MUST be the only flag and `value` MUST be 0. - All other flag bits MUST be zero. -WINDOW uses `flags=0`, carries no payload, and requires nonzero credit. A +WINDOW uses `flags=0`, carries no payload, and requires nonzero credit in 1 KiB +units. A WINDOW with `flow_id=0` replenishes connection credit; a nonzero ID replenishes that logical stream. Credit that would exceed the configured window closes the carrier. A late stream-local WINDOW for an already closed stream is ignored. @@ -261,9 +258,11 @@ STREAM data for an unknown flow is a carrier error. Late FIN or RST processing is idempotent. Closing the physical Mux carrier fails every logical stream on that carrier. -The runtime emits at most 32 KiB of data per STREAM frame. Default Mux bounds -are 512 KiB per-stream receive credit, 512 KiB connection-wide receive credit, -256 active streams, and 512 queued outbound frame slots. Payload must obtain +The runtime emits at most 32 KiB of data per STREAM frame. Mux uses an initial +4 MiB stream window and 8 MiB connection window. Each side sends one WINDOW to +extend its connection window and includes its stream-window extension in SYN. +The selected transport profile sets final windows to 4/8, 8/16, or 16/32 MiB. +Each shard permits 256 active streams and 512 queued outbound frame slots. Payload must obtain both stream and connection credit before it enters the outbound queue. ```text @@ -283,7 +282,8 @@ payload beyond either advertised receive window. Client-side Shards open lazily in separate uplink and downlink sets. A new flow uses the least-loaded live Shard for its TLS direction; a new Shard opens when -all live Shards in that set have 4 active flows. A symmetric `tcp/tcp` flow +all live Shards in that set have 4 active flows, up to 4 live Shards per +direction. Once the pool is full, new flows reuse its least-loaded Shard. A symmetric `tcp/tcp` flow uses one duplex stream from the uplink set. A fully idle Shard closes after 30 seconds. Portal applies the same timeout to an authenticated Mux carrier with no active streams. Sharding is runtime placement and does not add wire fields. diff --git a/docs/security.md b/docs/security.md index b6c946c..c133b6e 100644 --- a/docs/security.md +++ b/docs/security.md @@ -70,17 +70,19 @@ The fixed maximum frame payload is 65,535 bytes and the runtime emits at most and DATA for unknown streams close the carrier. Late terminal and credit frames for a terminal stream are idempotent. -Default limits are 512 KiB per stream and per Mux connection and 256 active -streams per Mux. With client `mux=1`, Vector or Portal `next` places at most 4 -active flows on a shard before opening another, distributes new flows to the +The transport memory profile bounds Mux stream/connection windows at 4/8, +8/16, or 16/32 MiB, and each Mux allows 256 active streams. With client `mux=1`, +Vector or Portal `next` places at most 4 active flows on a shard before opening +another, caps each direction at 4 shards, distributes new flows to the least-loaded shard, and closes a fully idle shard after 30 seconds. One authenticated inbound Mux carrier is subject to the same fully idle timeout. One authenticated client session admits at most 1,024 concurrent logical TCP flows and 256 logical UDP flows across all of its carriers. UoT and QUIC DATAGRAM flows share the UDP limit. -Local fair credit prevents one stream from monopolizing a shared window. The -finite frame queue has 512 slots, but payload admission is capped by the -512 KiB byte window; empty SYN/FIN/WINDOW frames cannot turn those slots into +Per-stream and connection credit plus bounded channel admission limit how much +one stream can occupy. The finite frame queue has 512 slots, but +payload admission is capped by the selected connection window; empty +SYN/FIN/WINDOW frames cannot turn those slots into retained application payload. These are credit ceilings rather than eagerly allocated payload buffers. diff --git a/scripts/bench-mux-local.sh b/scripts/bench-mux-local.sh new file mode 100755 index 0000000..3e063d0 --- /dev/null +++ b/scripts/bench-mux-local.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# Copyright (C) 2026 NodePassProject +# SPDX-License-Identifier: GPL-3.0-only + +set -eu + +rtt_ms=${1:-100} +flows=${2:-1} +mib_per_flow=${3:-64} +mux=${4:-1} +binary=${5:-target/release/nowhere} +container_name=nowhere-toxiproxy + +if ! container list | awk '{print $1}' | grep -Fqx "$container_name"; then + container run --rm -d --name "$container_name" \ + ghcr.io/shopify/toxiproxy:2.12.0 >/dev/null +fi + +proxy_ip=$(container list | awk -v name="$container_name" ' + NR == 1 { + for (column = 1; column <= NF; column++) { + if ($column == "IP") ip_column = column + } + next + } + $1 == name && ip_column != 0 { + value = $ip_column + sub("/24", "", value) + print value + } +') +if [ -z "$proxy_ip" ]; then + echo "failed to discover Toxiproxy address" >&2 + exit 1 +fi + +if container exec "$container_name" /toxiproxy-cli list | grep -q '^nowhere'; then + container exec "$container_name" /toxiproxy-cli delete nowhere >/dev/null +fi +container exec "$container_name" /toxiproxy-cli create \ + -l 0.0.0.0:2001 -u 192.168.64.1:2000 nowhere >/dev/null + +one_way_ms=$((rtt_ms / 2)) +container exec "$container_name" /toxiproxy-cli toxic add \ + -n latency_upstream -t latency -a "latency=$one_way_ms" -u nowhere >/dev/null +container exec "$container_name" /toxiproxy-cli toxic add \ + -n latency_downstream -t latency -a "latency=$one_way_ms" -d nowhere >/dev/null + +python3 scripts/mux-bench.py \ + --binary "$binary" \ + --rtt-ms "$rtt_ms" \ + --flows "$flows" \ + --mib-per-flow "$mib_per_flow" \ + --mux "$mux" \ + --proxy-host "$proxy_ip" \ + --external-proxy diff --git a/scripts/mux-bench.py b/scripts/mux-bench.py new file mode 100755 index 0000000..aead6da --- /dev/null +++ b/scripts/mux-bench.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 +"""Run an end-to-end TLS Mux throughput/RSS sample through Toxiproxy.""" + +import argparse +import json +import os +import signal +import socket +import struct +import subprocess +import threading +import time +import urllib.error +import urllib.request + + +def api(method, path, body=None): + data = None if body is None else json.dumps(body).encode() + request = urllib.request.Request( + f"http://127.0.0.1:8474{path}", data=data, method=method, + headers={"Content-Type": "application/json"}, + ) + with urllib.request.urlopen(request) as response: + return response.read() + + +def receive_exact(sock, size): + received = 0 + while received < size: + block = sock.recv(min(256 * 1024, size - received)) + if not block: + raise RuntimeError(f"early EOF after {received} bytes") + received += len(block) + return received + + +def target_server(stop, listener): + listener.settimeout(0.2) + + def serve(conn): + try: + request = b"" + while len(request) < 8: + part = conn.recv(8 - len(request)) + if not part: + return + request += part + remaining = struct.unpack("!Q", request)[0] + payload = bytes(256 * 1024) + while remaining: + count = min(remaining, len(payload)) + conn.sendall(payload[:count]) + remaining -= count + finally: + conn.close() + + try: + while not stop.is_set(): + try: + conn, _ = listener.accept() + except socket.timeout: + continue + threading.Thread(target=serve, args=(conn,), daemon=True).start() + finally: + listener.close() + + +def run_flow(port, target_port, byte_count, barrier, results, index): + try: + sock = socket.create_connection(("127.0.0.1", port), timeout=60) + sock.sendall(b"\x05\x01\x00") + if sock.recv(2) != b"\x05\x00": + raise RuntimeError("SOCKS authentication failed") + sock.sendall(b"\x05\x01\x00\x01\x7f\x00\x00\x01" + struct.pack("!H", target_port)) + reply = receive_reply(sock) + if reply != 0: + raise RuntimeError(f"SOCKS CONNECT failed: {reply}") + barrier.wait(timeout=60) + started = time.monotonic() + sock.sendall(struct.pack("!Q", byte_count)) + receive_exact(sock, byte_count) + results[index] = time.monotonic() - started + sock.close() + except Exception as error: + results[index] = str(error) + barrier.abort() + + +def receive_reply(sock): + head = receive_bytes(sock, 4) + if head[3] == 1: + address_len = 4 + elif head[3] == 3: + address_len = receive_bytes(sock, 1)[0] + elif head[3] == 4: + address_len = 16 + else: + raise RuntimeError(f"unknown SOCKS address type: {head[3]}") + receive_bytes(sock, address_len + 2) + return head[1] + + +def receive_bytes(sock, size): + chunks = bytearray() + while len(chunks) < size: + chunk = sock.recv(size - len(chunks)) + if not chunk: + raise RuntimeError("early EOF") + chunks.extend(chunk) + return bytes(chunks) + + +def rss_kib(pid): + output = subprocess.check_output(["ps", "-o", "rss=", "-p", str(pid)], text=True) + return int(output.strip()) + + +def wait_port(port, host="127.0.0.1", deadline=10): + until = time.monotonic() + deadline + while time.monotonic() < until: + try: + with socket.create_connection((host, port), timeout=0.5): + return + except OSError: + time.sleep(0.05) + raise RuntimeError(f"port {host}:{port} did not open") + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--binary", default="target/release/nowhere") + parser.add_argument("--rtt-ms", type=int, required=True) + parser.add_argument("--flows", type=int, required=True) + parser.add_argument("--mib-per-flow", type=int, default=64) + parser.add_argument("--mux", choices=("0", "1"), default="1") + parser.add_argument("--profile", default="throughput") + parser.add_argument("--proxy-host", default="127.0.0.1") + parser.add_argument("--external-proxy", action="store_true") + parser.add_argument("--debug", action="store_true") + args = parser.parse_args() + + env = os.environ.copy() + env.update({"NOW_TRANSPORT_MEMORY_PROFILE": args.profile, "RUST_LOG": "off"}) + log = "debug" if args.debug else "none" + output = None if args.debug else subprocess.DEVNULL + portal = subprocess.Popen( + [args.binary, f"portal://secret@:2000?log={log}"], env=env, + stdout=output, stderr=output, + ) + vector = None + stop = threading.Event() + target_listener = socket.socket() + target_listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + target_listener.bind(("127.0.0.1", 0)) + target_listener.listen() + target_port = target_listener.getsockname()[1] + server = threading.Thread( + target=target_server, args=(stop, target_listener), daemon=True, + ) + server.start() + try: + wait_port(2000) + if not args.external_proxy: + api("DELETE", "/proxies/nowhere") + except urllib.error.HTTPError as error: + if error.code != 404: + raise + try: + if not args.external_proxy: + api("POST", "/proxies", { + "name": "nowhere", "listen": "0.0.0.0:2001", "upstream": "192.168.64.1:2000", + }) + latency = args.rtt_ms // 2 + for stream in ("upstream", "downstream"): + api("POST", f"/proxies/nowhere/toxics", { + "name": f"latency-{stream}", "type": "latency", "stream": stream, + "toxicity": 1.0, "attributes": {"latency": latency, "jitter": 0}, + }) + wait_port(2001, args.proxy_host) + # Let a just-updated proxy path complete one RTT before the measured + # client opens its first carrier. Transfer timing starts later. + time.sleep(max(args.rtt_ms * 2 / 1000, 1.0)) + vector = subprocess.Popen( + [args.binary, f"vector://secret@{args.proxy_host}:2001?mux={args.mux}&socks=127.0.0.1:1080&log={log}"], + env=env, stdout=output, stderr=output, + ) + wait_port(1080) + barrier = threading.Barrier(args.flows) + durations = [0.0] * args.flows + byte_count = args.mib_per_flow * 1024 * 1024 + peak = {"portal": rss_kib(portal.pid), "vector": rss_kib(vector.pid)} + sampling = True + + def sample(): + while sampling: + peak["portal"] = max(peak["portal"], rss_kib(portal.pid)) + peak["vector"] = max(peak["vector"], rss_kib(vector.pid)) + time.sleep(0.1) + + monitor = threading.Thread(target=sample, daemon=True) + monitor.start() + threads = [threading.Thread( + target=run_flow, + args=(1080, target_port, byte_count, barrier, durations, index), + daemon=True, + ) for index in range(args.flows)] + wall_start = time.monotonic() + for thread in threads: + thread.start() + for thread in threads: + thread.join() + setup_and_transfer = time.monotonic() - wall_start + sampling = False + monitor.join() + errors = [result for result in durations if not isinstance(result, float)] + if errors: + raise RuntimeError(f"flow failures: {errors}") + total = byte_count * args.flows + transfer = max(durations) + print(json.dumps({ + "rtt_ms": args.rtt_ms, "flows": args.flows, "mux": int(args.mux), + "profile": args.profile, "mib": total / 1024 / 1024, + "seconds": round(transfer, 3), "mbps": round(total * 8 / transfer / 1_000_000, 2), + "setup_and_transfer_seconds": round(setup_and_transfer, 3), + "flow_max_seconds": round(max(durations), 3), + "portal_peak_rss_mib": round(peak["portal"] / 1024, 2), + "vector_peak_rss_mib": round(peak["vector"] / 1024, 2), + }, sort_keys=True)) + finally: + stop.set() + for process in (vector, portal): + if process is not None and process.poll() is None: + process.send_signal(signal.SIGTERM) + try: + process.wait(timeout=3) + except subprocess.TimeoutExpired: + process.kill() + + +if __name__ == "__main__": + main() diff --git a/src/main.rs b/src/main.rs index 069b287..81b803f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -142,7 +142,7 @@ Environment: NOW_MAX_TCP_FLOWS TCP flows per authenticated client session. NOW_MAX_UDP_FLOWS UDP flows per authenticated client session. NOW_QUIC_UDP_QUEUE_BYTES Maximum queued/reassembling UDP bytes per QUIC connection. - NOW_QUIC_MEMORY_PROFILE memory, balanced, or throughput. Default: throughput. + NOW_TRANSPORT_MEMORY_PROFILE memory, balanced, or throughput. Default: throughput. NOW_MAX_PENDING_PAIRS Maximum pending logical-flow IDs per session. NOW_FLOW_PAIR_TIMEOUT Timeout for completing a split logical flow. NOW_FLOW_SETUP_TIMEOUT Timeout for waiting for a logical flow to become ready. diff --git a/src/mux/driver.rs b/src/mux/driver.rs index b38f76c..223bdc7 100644 --- a/src/mux/driver.rs +++ b/src/mux/driver.rs @@ -13,19 +13,15 @@ use bytes::Bytes; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio::sync::mpsc; -use super::{Inbound, Outbound, Shared}; +use super::{Inbound, MuxChunk, Outbound, Shared}; pub(super) async fn send_data( shared: Arc, flow_id: FlowId, - payload: Bytes, + payload: MuxChunk, ) -> io::Result<()> { let charge = frame_charge(payload.len()); - let (flow_credit, fair_credit) = shared.send_credits(flow_id)?; - let fair = fair_credit - .acquire_many_owned(charge as u32) - .await - .map_err(|_| closed())?; + let flow_credit = shared.send_credit(flow_id)?; let flow = flow_credit .acquire_many_owned(charge as u32) .await @@ -45,7 +41,6 @@ pub(super) async fn send_data( }) .await .map_err(|_| closed())?; - fair.forget(); flow.forget(); connection.forget(); Ok(()) @@ -61,8 +56,8 @@ pub(super) async fn run_reader(mut reader: R, shared: Arc< } let header = decode_header(&encoded).map_err(invalid)?; let payload_len = match header.kind { - FrameKind::Stream | FrameKind::Datagram => header.value as usize, - FrameKind::Window => 0, + FrameKind::Stream if header.flags & FLAG_SYN == 0 => header.value as usize, + FrameKind::Stream | FrameKind::Window => 0, }; let mut payload = vec![0; payload_len]; if !payload.is_empty() { @@ -74,12 +69,6 @@ pub(super) async fn run_reader(mut reader: R, shared: Arc< match header.kind { FrameKind::Stream => receive_stream(&shared, header, Bytes::from(payload)).await?, FrameKind::Window => receive_window(&shared, header)?, - FrameKind::Datagram => { - return Err(io::Error::new( - io::ErrorKind::Unsupported, - "mux datagram is not registered", - )); - } } } } @@ -96,7 +85,20 @@ async fn receive_stream( ) -> io::Result<()> { if header.flags & FLAG_SYN != 0 { let terminal_permit = shared.reserve_terminal().await?; - let stream = shared.insert_flow(header.flow_id, terminal_permit)?; + let stream = shared.insert_flow(header.flow_id, terminal_permit, true)?; + let extra_credit = header.value as usize; + if extra_credit != 0 { + let credit = shared.send_credit(header.flow_id)?; + if credit.available_permits().saturating_add(extra_credit) + > super::credit_units(super::MAX_STREAM_WINDOW_BYTES) + { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "stream window overflow", + )); + } + credit.add_permits(extra_credit); + } shared .incoming_tx .send(stream) @@ -139,7 +141,7 @@ fn receive_window(shared: &Shared, header: FrameHeader) -> io::Result<()> { .connection_send_credit .available_permits() .saturating_add(credit) - > shared.config.connection_window_bytes + > super::credit_units(super::MAX_CONNECTION_WINDOW_BYTES) { return Err(io::Error::new( io::ErrorKind::InvalidData, @@ -157,7 +159,7 @@ fn receive_window(shared: &Shared, header: FrameHeader) -> io::Result<()> { return Ok(()); }; if flow.send_credit.available_permits().saturating_add(credit) - > shared.config.stream_window_bytes + > super::credit_units(super::MAX_STREAM_WINDOW_BYTES) { return Err(io::Error::new( io::ErrorKind::InvalidData, @@ -165,7 +167,6 @@ fn receive_window(shared: &Shared, header: FrameHeader) -> io::Result<()> { )); } flow.send_credit.add_permits(credit); - Shared::return_fair_credit(flow, credit); Ok(()) } @@ -183,7 +184,7 @@ pub(super) async fn run_terminals(shared: Arc, mut terminal_rx: mpsc::Re _ = shared.closed_notify.notified() => return, sent = shared.data_tx.send(Outbound { header, - payload: Bytes::new(), + payload: MuxChunk::empty(), flushed: None, }) => sent, }; @@ -237,10 +238,11 @@ pub(super) async fn run_writer( } } writer.write_all(&headers).await?; + writer.flush().await?; continue; } let header = encode_header(item.header).map_err(invalid)?; - write_frame_vectored(&mut writer, &header, &item.payload).await?; + write_frame_vectored(&mut writer, &header, item.payload.as_ref()).await?; } } .await; @@ -316,6 +318,7 @@ async fn write_pending_windows( } if !encoded.is_empty() { writer.write_all(encoded).await?; + writer.flush().await?; } Ok(()) } @@ -331,7 +334,12 @@ fn append_windows(encoded: &mut Vec, flow_id: FlowId, mut credit: usize) -> } fn frame_charge(payload: usize) -> usize { - payload + super::credit_units(payload) +} + +pub(super) fn frame_open(flow_id: FlowId, receive_window_bytes: usize) -> io::Result { + let extra = receive_window_bytes.saturating_sub(super::BASE_STREAM_WINDOW_BYTES); + FrameHeader::stream(flow_id, FLAG_SYN, super::credit_units(extra)).map_err(invalid) } pub(super) fn frame_stream(flow_id: FlowId, flags: u8, length: usize) -> io::Result { diff --git a/src/mux/handle.rs b/src/mux/handle.rs index 4faffa3..295affd 100644 --- a/src/mux/handle.rs +++ b/src/mux/handle.rs @@ -9,12 +9,10 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::time::Duration; -use bytes::Bytes; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::sync::{Notify, Semaphore, mpsc, watch}; -use super::driver::{closed, frame_stream, run_reader, run_terminals, run_writer}; -use super::wire::FLAG_SYN; +use super::driver::{closed, frame_open, run_reader, run_terminals, run_writer}; use super::{Incoming, MuxConfig, MuxHandle, MuxStream, Outbound, Shared}; impl MuxHandle { @@ -30,9 +28,17 @@ impl MuxHandle { let shared = Arc::new(Shared { config, flows: Mutex::new(HashMap::new()), - connection_send_credit: Arc::new(Semaphore::new(config.connection_window_bytes)), - connection_receive_credit: Mutex::new(config.connection_window_bytes), - pending_connection_credit: AtomicUsize::new(0), + connection_send_credit: Arc::new(Semaphore::new(super::credit_units( + super::BASE_CONNECTION_WINDOW_BYTES, + ))), + connection_receive_credit: Mutex::new(super::credit_units( + config.connection_window_bytes, + )), + pending_connection_credit: AtomicUsize::new(super::credit_units( + config + .connection_window_bytes + .saturating_sub(super::BASE_CONNECTION_WINDOW_BYTES), + )), ready_flows: Mutex::new(VecDeque::with_capacity(config.max_streams)), data_tx, terminal_tx, @@ -41,11 +47,16 @@ impl MuxHandle { active_streams_tx, closed: AtomicBool::new(false), closed_notify: Notify::new(), + #[cfg(test)] + borrowed_write_copies: AtomicUsize::new(0), }); let (reader, writer) = tokio::io::split(io); tokio::spawn(run_reader(reader, shared.clone())); tokio::spawn(run_writer(writer, shared.clone(), data_rx)); tokio::spawn(run_terminals(shared.clone(), terminal_rx)); + if config.connection_window_bytes > super::BASE_CONNECTION_WINDOW_BYTES { + shared.control_notify.notify_one(); + } Ok(( Self { shared }, Incoming { @@ -56,12 +67,12 @@ impl MuxHandle { pub(crate) async fn open_stream(&self, flow_id: super::FlowId) -> io::Result { let terminal_permit = self.shared.reserve_terminal().await?; - let stream = self.shared.insert_flow(flow_id, terminal_permit)?; + let stream = self.shared.insert_flow(flow_id, terminal_permit, false)?; self.shared .data_tx .send(Outbound { - header: frame_stream(flow_id, FLAG_SYN, 0)?, - payload: Bytes::new(), + header: frame_open(flow_id, self.shared.config.stream_window_bytes)?, + payload: super::MuxChunk::empty(), flushed: None, }) .await @@ -77,6 +88,22 @@ impl MuxHandle { self.shared.flows.lock().expect("mux flow lock").len() } + pub(crate) fn has_stream_capacity(&self) -> bool { + self.active_streams() < self.shared.config.max_streams + } + + #[cfg(test)] + pub(crate) fn borrowed_write_copies(&self) -> usize { + self.shared.borrowed_write_copies.load(Ordering::Relaxed) + } + + #[cfg(test)] + pub(crate) fn reset_borrowed_write_copies(&self) { + self.shared + .borrowed_write_copies + .store(0, Ordering::Relaxed); + } + pub(crate) fn close(&self) { self.shared.close(); } diff --git a/src/mux/mod.rs b/src/mux/mod.rs index c54424e..f4aa16f 100644 --- a/src/mux/mod.rs +++ b/src/mux/mod.rs @@ -20,13 +20,17 @@ mod handle; mod stream; mod wire; -const FRAME_BYTES: usize = 32 * 1024; -const WINDOW_UPDATE_BYTES: usize = 4 * 1024; +pub(crate) const FRAME_BYTES: usize = 32 * 1024; +const MIB: usize = 1024 * 1024; +const BASE_STREAM_WINDOW_BYTES: usize = 4 * MIB; +const BASE_CONNECTION_WINDOW_BYTES: usize = 8 * MIB; +const MAX_STREAM_WINDOW_BYTES: usize = 16 * MIB; +const MAX_CONNECTION_WINDOW_BYTES: usize = 32 * MIB; +const CREDIT_UNIT_BYTES: usize = 1024; // Frame count is separate from the byte window: UoT carries many small // packets, so the frame queue absorbs scheduling bursts while the byte window // remains the hard payload bound. const FLOW_CHANNEL_FRAMES: usize = 512; -const MIN_FAIR_CREDIT_BYTES: usize = 256 * 1024; pub(crate) const MUX_IDLE_TIMEOUT: Duration = Duration::from_secs(30); #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -39,16 +43,25 @@ pub(crate) struct MuxConfig { impl Default for MuxConfig { fn default() -> Self { + Self::from_flow_control(crate::transport::transport_flow_control().unwrap_or( + crate::transport::TransportFlowControl { + stream_receive_window: MAX_STREAM_WINDOW_BYTES as u32, + connection_receive_window: MAX_CONNECTION_WINDOW_BYTES as u32, + send_window: MAX_CONNECTION_WINDOW_BYTES as u64, + }, + )) + } +} + +impl MuxConfig { + pub(crate) fn from_flow_control(profile: crate::transport::TransportFlowControl) -> Self { Self { - stream_window_bytes: 512 * 1024, - connection_window_bytes: 512 * 1024, + stream_window_bytes: profile.stream_receive_window as usize, + connection_window_bytes: profile.connection_receive_window as usize, max_streams: 256, outbound_frames: 512, } } -} - -impl MuxConfig { fn validate(self) -> io::Result { if self.stream_window_bytes < FRAME_BYTES || self.connection_window_bytes < self.stream_window_bytes @@ -70,6 +83,17 @@ pub(crate) struct MuxStream { writer: FlowWriter, } +pub(crate) struct MuxChunk { + payload: Bytes, + _credit: Option, +} + +struct ReceiveCredit { + shared: Arc, + flow_id: FlowId, + charge: usize, +} + pub(crate) struct FlowReader { shared: Arc, flow_id: FlowId, @@ -113,14 +137,13 @@ struct Shared { active_streams_tx: watch::Sender, closed: AtomicBool, closed_notify: Notify, + #[cfg(test)] + borrowed_write_copies: AtomicUsize, } struct FlowState { inbound: mpsc::Sender, send_credit: Arc, - fair_send_credit: Arc, - fair_limit: usize, - fair_debt: usize, receive_credit: usize, pending_receive_credit: usize, window_queued: bool, @@ -135,10 +158,54 @@ enum Inbound { struct Outbound { header: FrameHeader, - payload: Bytes, + payload: MuxChunk, flushed: Option>>, } +impl MuxChunk { + pub(crate) fn from_bytes(payload: Bytes) -> Self { + Self { + payload, + _credit: None, + } + } + + fn received(payload: Bytes, shared: Arc, flow_id: FlowId, charge: usize) -> Self { + Self { + payload, + _credit: Some(ReceiveCredit { + shared, + flow_id, + charge, + }), + } + } + + fn empty() -> Self { + Self::from_bytes(Bytes::new()) + } + + pub(crate) fn len(&self) -> usize { + self.payload.len() + } + + pub(crate) fn is_empty(&self) -> bool { + self.payload.is_empty() + } +} + +impl AsRef<[u8]> for MuxChunk { + fn as_ref(&self) -> &[u8] { + &self.payload + } +} + +impl Drop for ReceiveCredit { + fn drop(&mut self) { + self.shared.release_receive(self.flow_id, self.charge); + } +} + impl Shared { async fn reserve_terminal(&self) -> io::Result> { tokio::select! { @@ -151,6 +218,7 @@ impl Shared { self: &Arc, flow_id: FlowId, terminal_permit: mpsc::OwnedPermit, + advertise_window: bool, ) -> io::Result { if flow_id == 0 || self.closed.load(Ordering::Acquire) { return Err(closed()); @@ -169,25 +237,36 @@ impl Shared { "mux flow already exists", )); } - let send_credit = Arc::new(Semaphore::new(self.config.stream_window_bytes)); - let fair_send_credit = Arc::new(Semaphore::new(self.config.stream_window_bytes)); + let send_credit = Arc::new(Semaphore::new(credit_units(BASE_STREAM_WINDOW_BYTES))); + let initial_credit = if advertise_window { + credit_units( + self.config + .stream_window_bytes + .saturating_sub(BASE_STREAM_WINDOW_BYTES), + ) + } else { + 0 + }; flows.insert( flow_id, FlowState { inbound: sender, send_credit, - fair_send_credit, - fair_limit: self.config.stream_window_bytes, - fair_debt: 0, - receive_credit: self.config.stream_window_bytes, - pending_receive_credit: 0, - window_queued: false, + receive_credit: credit_units(self.config.stream_window_bytes), + pending_receive_credit: initial_credit, + window_queued: advertise_window && initial_credit != 0, local_parts: 2, }, ); - Self::rebalance_fair_credits(&mut flows, self.config); let active_streams = flows.len(); drop(flows); + if advertise_window && initial_credit != 0 { + self.ready_flows + .lock() + .expect("mux ready-flow lock") + .push_back(flow_id); + self.control_notify.notify_one(); + } self.active_streams_tx.send_replace(active_streams); Ok(MuxStream { reader: FlowReader { @@ -217,53 +296,18 @@ impl Shared { self.closed_notify.notify_waiters(); } - fn send_credits(&self, flow_id: FlowId) -> io::Result<(Arc, Arc)> { + fn send_credit(&self, flow_id: FlowId) -> io::Result> { self.flows .lock() .expect("mux flow lock") .get(&flow_id) - .map(|flow| (flow.send_credit.clone(), flow.fair_send_credit.clone())) + .map(|flow| flow.send_credit.clone()) .ok_or_else(closed) } - fn rebalance_fair_credits(flows: &mut HashMap, config: MuxConfig) { - if flows.is_empty() { - return; - } - let fair_limit = (config.connection_window_bytes / flows.len()) - .max(MIN_FAIR_CREDIT_BYTES) - .min(config.stream_window_bytes); - for flow in flows.values_mut() { - if fair_limit < flow.fair_limit { - let reduction = flow.fair_limit - fair_limit; - let removed = flow.fair_send_credit.forget_permits(reduction); - flow.fair_debt = flow.fair_debt.saturating_add(reduction - removed); - } else if fair_limit > flow.fair_limit { - let increase = fair_limit - flow.fair_limit; - let debt_repaid = increase.min(flow.fair_debt); - flow.fair_debt -= debt_repaid; - flow.fair_send_credit.add_permits(increase - debt_repaid); - } - flow.fair_limit = fair_limit; - } - } - - fn return_fair_credit(flow: &mut FlowState, credit: usize) { - let debt_repaid = credit.min(flow.fair_debt); - flow.fair_debt -= debt_repaid; - let returned = credit - debt_repaid; - let room = flow - .fair_limit - .saturating_sub(flow.fair_send_credit.available_permits()); - flow.fair_send_credit.add_permits(returned.min(room)); - } - fn remove_flow(&self, flow_id: FlowId) -> Option { let mut flows = self.flows.lock().expect("mux flow lock"); let removed = flows.remove(&flow_id); - if removed.is_some() { - Self::rebalance_fair_credits(&mut flows, self.config); - } let active_streams = flows.len(); drop(flows); self.active_streams_tx.send_replace(active_streams); @@ -301,12 +345,12 @@ impl Shared { .expect("mux credit lock"); *connection = connection .saturating_add(charge) - .min(self.config.connection_window_bytes); + .min(credit_units(self.config.connection_window_bytes)); if let Some(flow) = self.flows.lock().expect("mux flow lock").get_mut(&flow_id) { flow.receive_credit = flow .receive_credit .saturating_add(charge) - .min(self.config.stream_window_bytes); + .min(credit_units(self.config.stream_window_bytes)); flow.pending_receive_credit = flow.pending_receive_credit.saturating_add(charge); let ready = if flow.window_queued { false @@ -314,7 +358,9 @@ impl Shared { flow.window_queued = true; true }; - (ready, flow.pending_receive_credit >= WINDOW_UPDATE_BYTES) + let threshold = + credit_units(self.config.stream_window_bytes / 8).min(u16::MAX as usize); + (ready, flow.pending_receive_credit >= threshold) } else { return; } @@ -328,7 +374,9 @@ impl Shared { let previous = self .pending_connection_credit .fetch_add(charge, Ordering::AcqRel); - if flow_notify || previous.saturating_add(charge) >= WINDOW_UPDATE_BYTES { + let threshold = + credit_units(self.config.connection_window_bytes / 8).min(u16::MAX as usize); + if flow_notify || previous.saturating_add(charge) >= threshold { self.control_notify.notify_one(); } } @@ -342,7 +390,6 @@ impl Shared { flow.local_parts = flow.local_parts.saturating_sub(1); if flow.local_parts == 0 { flows.remove(&flow_id); - Self::rebalance_fair_credits(&mut flows, self.config); } let active_streams = flows.len(); drop(flows); @@ -353,6 +400,10 @@ impl Shared { } } +fn credit_units(bytes: usize) -> usize { + bytes.div_ceil(CREDIT_UNIT_BYTES) +} + #[cfg(test)] #[path = "../tests/mux/runtime.rs"] mod tests; diff --git a/src/mux/stream.rs b/src/mux/stream.rs index 31cf420..52c91d8 100644 --- a/src/mux/stream.rs +++ b/src/mux/stream.rs @@ -12,7 +12,11 @@ use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tokio::sync::oneshot; use super::driver::{closed, frame_stream, send_data}; -use super::{FRAME_BYTES, FlowReader, FlowWriter, Inbound, MuxStream, Outbound}; +use super::{FRAME_BYTES, FlowReader, FlowWriter, Inbound, MuxChunk, MuxStream, Outbound}; + +fn copy_payload(payload: &[u8]) -> Bytes { + Bytes::copy_from_slice(payload) +} impl MuxStream { pub fn into_split(self) -> (FlowReader, FlowWriter) { @@ -106,6 +110,41 @@ impl AsyncRead for FlowReader { } } +impl FlowReader { + pub(crate) async fn recv_chunk(&mut self) -> io::Result> { + if let Some((payload, offset, charge)) = self.current.take() { + return Ok(Some(MuxChunk::received( + payload.slice(offset..), + self.shared.clone(), + self.flow_id, + charge, + ))); + } + if self.eof { + return Ok(None); + } + match self.receiver.recv().await { + Some(Inbound::Data { payload, charge }) => Ok(Some(MuxChunk::received( + payload, + self.shared.clone(), + self.flow_id, + charge, + ))), + Some(Inbound::Fin) | None => { + self.eof = true; + Ok(None) + } + Some(Inbound::Reset) => { + self.eof = true; + Err(io::Error::new( + io::ErrorKind::ConnectionReset, + "mux flow reset", + )) + } + } + } +} + impl Drop for FlowReader { fn drop(&mut self) { if let Some((_, _, charge)) = self.current.take() { @@ -136,7 +175,11 @@ impl AsyncWrite for FlowWriter { return Poll::Ready(Ok(0)); } let length = buf.len().min(FRAME_BYTES); - let payload = Bytes::copy_from_slice(&buf[..length]); + #[cfg(test)] + self.shared + .borrowed_write_copies + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let payload = MuxChunk::from_bytes(copy_payload(&buf[..length])); self.start_write(cx, payload, length) } @@ -151,23 +194,18 @@ impl AsyncWrite for FlowWriter { if let Some(result) = self.poll_pending(cx) { return result; } - let length = bufs - .iter() - .map(|buffer| buffer.len()) - .sum::() - .min(FRAME_BYTES); - if length == 0 { - return Poll::Ready(Ok(0)); - } - let mut payload = Vec::with_capacity(length); for buffer in bufs { - let remaining = length - payload.len(); - if remaining == 0 { - break; + if !buffer.is_empty() { + let length = buffer.len().min(FRAME_BYTES); + #[cfg(test)] + self.shared + .borrowed_write_copies + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let payload = MuxChunk::from_bytes(copy_payload(&buffer[..length])); + return self.start_write(cx, payload, length); } - payload.extend_from_slice(&buffer[..buffer.len().min(remaining)]); } - self.start_write(cx, Bytes::from(payload), length) + Poll::Ready(Ok(0)) } fn is_write_vectored(&self) -> bool { @@ -189,7 +227,7 @@ impl AsyncWrite for FlowWriter { .data_tx .send(Outbound { header: frame_stream(flow_id, 0, 0)?, - payload: Bytes::new(), + payload: MuxChunk::empty(), flushed: Some(tx), }) .await @@ -217,7 +255,7 @@ impl AsyncWrite for FlowWriter { .data_tx .send(Outbound { header: frame_stream(flow_id, FLAG_FIN, 0)?, - payload: Bytes::new(), + payload: MuxChunk::empty(), flushed: None, }) .await @@ -239,7 +277,7 @@ impl FlowWriter { fn start_write( &mut self, cx: &mut Context<'_>, - payload: Bytes, + payload: MuxChunk, length: usize, ) -> Poll> { let shared = self.shared.clone(); @@ -272,6 +310,29 @@ impl FlowWriter { } } +impl FlowWriter { + pub(crate) async fn send_chunk(&mut self, chunk: MuxChunk) -> io::Result<()> { + if self.closed + || self + .shared + .closed + .load(std::sync::atomic::Ordering::Acquire) + { + return Err(closed()); + } + if chunk.is_empty() { + return Ok(()); + } + if chunk.len() > FRAME_BYTES { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "mux chunk exceeds frame size", + )); + } + super::driver::send_data(self.shared.clone(), self.flow_id, chunk).await + } +} + impl Drop for FlowWriter { fn drop(&mut self) { if !self.closed { diff --git a/src/mux/wire.rs b/src/mux/wire.rs index 8030370..e204a31 100644 --- a/src/mux/wire.rs +++ b/src/mux/wire.rs @@ -16,7 +16,6 @@ pub(super) const FLAG_RST: u8 = 0x04; pub(super) enum FrameKind { Stream = 0x01, Window = 0x02, - Datagram = 0x03, } impl TryFrom for FrameKind { @@ -26,7 +25,6 @@ impl TryFrom for FrameKind { match value { 0x01 => Ok(Self::Stream), 0x02 => Ok(Self::Window), - 0x03 => Ok(Self::Datagram), _ => Err(WireError::UnknownKind(value)), } } @@ -73,6 +71,9 @@ impl FrameHeader { if self.flags & FLAG_RST != 0 && (self.flags != FLAG_RST || self.value != 0) { return Err(WireError::InvalidReset); } + if self.flags & FLAG_SYN != 0 && self.flags != FLAG_SYN { + return Err(WireError::InvalidOpen); + } } FrameKind::Window => { if self.flags != 0 { @@ -82,14 +83,6 @@ impl FrameHeader { return Err(WireError::InvalidWindow); } } - FrameKind::Datagram => { - if self.flow_id == 0 { - return Err(WireError::InvalidFlowId); - } - if self.flags != 0 { - return Err(WireError::ReservedFlags); - } - } } Ok(()) } @@ -128,6 +121,7 @@ pub(super) enum WireError { InvalidFlowId, InvalidWindow, InvalidReset, + InvalidOpen, } impl fmt::Display for WireError { @@ -144,6 +138,7 @@ impl fmt::Display for WireError { Self::InvalidReset => { formatter.write_str("RST must be the only flag and carry no data") } + Self::InvalidOpen => formatter.write_str("SYN must be an isolated open frame"), } } } diff --git a/src/portal/conn.rs b/src/portal/conn.rs index 667644c..0df0651 100644 --- a/src/portal/conn.rs +++ b/src/portal/conn.rs @@ -119,7 +119,7 @@ async fn handle_connection( } // Once auth succeeds, expand the conservative pre-auth limits to the normal // data-plane limits and release the admission slot. - let flow_control = match crate::transport::quic_flow_control() { + let flow_control = match crate::transport::transport_flow_control() { Ok(value) => value, Err(err) => { portal.logger.error(format_args!( diff --git a/src/portal/conn/relay_stream.rs b/src/portal/conn/relay_stream.rs index 9af318e..3ec1e8b 100644 --- a/src/portal/conn/relay_stream.rs +++ b/src/portal/conn/relay_stream.rs @@ -6,40 +6,38 @@ use std::sync::Arc; use std::sync::atomic::Ordering; -use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; use tokio::time::timeout; use crate::portal::PortalInner; use crate::protocol::Carrier; use crate::telemetry::AccessSpan; -use crate::transport::BufferLease; +use crate::transport::{ + AsyncReadAny, AsyncWriteAny, read_owned, read_owned_from, write_owned, write_owned_to, +}; /// Relays both directions until one side closes or either direction errors. -pub(in crate::portal) async fn relay_stream( +pub(in crate::portal) async fn relay_stream( portal: Arc, - client_read: &mut R, - client_write: &mut W, + client_read: &mut std::pin::Pin>, + client_write: &mut std::pin::Pin>, target: (TR, TW), - buffers: (BufferLease, BufferLease), carriers: Option<(Carrier, Carrier)>, access: &AccessSpan, ) -> anyhow::Result<()> where - R: AsyncRead + Unpin, - W: AsyncWrite + Unpin, TR: AsyncRead + Unpin, TW: AsyncWrite + Unpin, { - let (mut buffer1, mut buffer2) = buffers; let (mut target_read, mut target_write) = target; let client_to_target = async { loop { - let n = client_read.read(&mut buffer1).await?; - if n == 0 { + let Some(chunk) = read_owned(client_read).await? else { target_write.shutdown().await?; return Ok::<(), anyhow::Error>(()); - } + }; + let n = chunk.len(); access.add_upload(n as u64); portal.stats.tcp_rx.fetch_add(n as u64, Ordering::Relaxed); if let Some((uplink, _)) = carriers { @@ -52,21 +50,21 @@ where if let Some(limiter) = &portal.rate_limiter { limiter.wait_read(n as i64).await; } - target_write.write_all(&buffer1[..n]).await?; + write_owned_to(&mut target_write, chunk).await?; } }; let target_to_client = async { loop { - let n = target_read.read(&mut buffer2).await?; - if n == 0 { + let Some(chunk) = read_owned_from(&mut target_read).await? else { client_write.shutdown().await?; return Ok::<(), anyhow::Error>(()); - } + }; + let n = chunk.len(); if let Some(limiter) = &portal.rate_limiter { limiter.wait_write(n as i64).await; } - client_write.write_all(&buffer2[..n]).await?; + write_owned(client_write, chunk).await?; if carriers.is_some_and(|(uplink, downlink)| { uplink == Carrier::TlsTcp && downlink == Carrier::Quic }) { diff --git a/src/portal/conn/relay_tcp.rs b/src/portal/conn/relay_tcp.rs index 969a345..c1a5fbd 100644 --- a/src/portal/conn/relay_tcp.rs +++ b/src/portal/conn/relay_tcp.rs @@ -154,10 +154,6 @@ pub(in crate::portal) async fn relay_paired_tcp(portal: Arc, paired &mut client_read, &mut client_write, (target_read, target_write), - ( - portal.buffers.get_tcp_buffer(), - portal.buffers.get_tcp_buffer(), - ), Some((uplink, downlink)), &access, ); diff --git a/src/portal/conn/tcp/flow.rs b/src/portal/conn/tcp/flow.rs index 1a40fa0..feff7da 100644 --- a/src/portal/conn/tcp/flow.rs +++ b/src/portal/conn/tcp/flow.rs @@ -36,7 +36,9 @@ pub(super) async fn process_flow( R: AsyncRead + Send + Unpin + 'static, W: AsyncWrite + Send + Unpin + 'static, { - let mut recv = BufReader::new(recv); + // A one-byte staging buffer keeps exact handshake reads from prefetching + // application payload out of an owned Mux chunk. + let mut recv = BufReader::with_capacity(1, recv); let header = match tokio::select! { result = timeout(flow_timeout, read_flow_header(&mut recv)) => Some(result), _ = shutdown.cancelled() => None, diff --git a/src/portal/listener.rs b/src/portal/listener.rs index 79a8eb4..c28d4e9 100644 --- a/src/portal/listener.rs +++ b/src/portal/listener.rs @@ -14,7 +14,7 @@ use tokio::time::{Duration, sleep}; use tokio_util::sync::CancellationToken; use crate::telemetry::{RuntimeEvent, RuntimeKind, RuntimeLevel}; -use crate::transport::quic_flow_control; +use crate::transport::transport_flow_control; use super::{PortalInner, conn}; @@ -184,7 +184,7 @@ pub(super) fn configure_transport( udp_idle_timeout: Duration, keep_alive_interval: Option, ) -> Result<()> { - let flow_control = quic_flow_control()?; + let flow_control = transport_flow_control()?; let transport = Arc::get_mut(&mut server_config.transport).ok_or_else(|| { anyhow::anyhow!("portal::configure_transport: server transport already shared") })?; diff --git a/src/portal/pairing/state.rs b/src/portal/pairing/state.rs index 1d36612..8e200eb 100644 --- a/src/portal/pairing/state.rs +++ b/src/portal/pairing/state.rs @@ -8,14 +8,13 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use quinn::Connection; -use tokio::io::{AsyncRead, AsyncWrite}; use tokio::sync::{OwnedSemaphorePermit, Semaphore, mpsc, oneshot}; use tokio_util::sync::CancellationToken; use crate::protocol::{FlowKind, ProtocolVersion, SessionId, Target}; -pub(in crate::portal) type BoxReader = Pin>; -pub(in crate::portal) type BoxWriter = Pin>; +pub(in crate::portal) type BoxReader = Pin>; +pub(in crate::portal) type BoxWriter = Pin>; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub(in crate::portal) struct SessionKey { diff --git a/src/tests/main.rs b/src/tests/main.rs index 20ccdcf..a0d7cb6 100644 --- a/src/tests/main.rs +++ b/src/tests/main.rs @@ -36,7 +36,7 @@ fn help_text_documents_usage_and_configuration_surface() { "NOW_MAX_TCP_FLOWS", "NOW_MAX_UDP_FLOWS", "NOW_QUIC_UDP_QUEUE_BYTES", - "NOW_QUIC_MEMORY_PROFILE", + "NOW_TRANSPORT_MEMORY_PROFILE", "NOW_MAX_PENDING_PAIRS", "NOW_FLOW_PAIR_TIMEOUT", "NOW_FLOW_SETUP_TIMEOUT", diff --git a/src/tests/mux/runtime.rs b/src/tests/mux/runtime.rs index 69b47cf..2a56ecd 100644 --- a/src/tests/mux/runtime.rs +++ b/src/tests/mux/runtime.rs @@ -69,6 +69,42 @@ async fn many_small_writes_cross_the_credit_window() { assert!(received.iter().all(|byte| *byte == 0x5a)); } +#[tokio::test] +async fn peers_with_different_profiles_exchange_beyond_the_base_window() { + let memory = MuxConfig { + stream_window_bytes: 4 * MIB, + connection_window_bytes: 8 * MIB, + ..MuxConfig::default() + }; + let throughput = MuxConfig { + stream_window_bytes: 16 * MIB, + connection_window_bytes: 32 * MIB, + ..MuxConfig::default() + }; + let (left, right) = tokio::io::duplex(1 << 20); + let (client, _) = MuxHandle::start(left, memory).unwrap(); + let (_server, mut incoming) = MuxHandle::start(right, throughput).unwrap(); + let mut outgoing = client.open_stream(81).await.unwrap(); + let mut accepted = incoming.accept().await.unwrap().unwrap(); + let sender = tokio::spawn(async move { + let payload = vec![0x81; 12 * MIB]; + outgoing.write_all(&payload).await.unwrap(); + outgoing.shutdown().await.unwrap(); + }); + let mut received = 0; + let mut buffer = vec![0; FRAME_BYTES]; + loop { + let count = accepted.read(&mut buffer).await.unwrap(); + if count == 0 { + break; + } + assert!(buffer[..count].iter().all(|byte| *byte == 0x81)); + received += count; + } + sender.await.unwrap(); + assert_eq!(received, 12 * MIB); +} + #[tokio::test] async fn carrier_close_fails_every_flow() { let (left, right) = tokio::io::duplex(1024); @@ -127,7 +163,7 @@ async fn dropping_unused_writer_preserves_incoming_half() { } #[tokio::test] -async fn fair_credit_tracks_active_stream_count() { +async fn stream_credit_does_not_shrink_with_active_stream_count() { let (left, right) = tokio::io::duplex(1 << 20); let (client, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); let (_server, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); @@ -139,7 +175,10 @@ async fn fair_credit_tracks_active_stream_count() { } { let flows = client.shared.flows.lock().unwrap(); - assert!(flows.values().all(|flow| flow.fair_limit == 256 * 1024)); + assert!(flows.values().all(|flow| { + flow.send_credit.available_permits() + == credit_units(MuxConfig::default().stream_window_bytes) + })); } let retained = streams.pop().unwrap(); @@ -148,8 +187,13 @@ async fn fair_credit_tracks_active_stream_count() { let flows = client.shared.flows.lock().unwrap(); assert_eq!(flows.len(), 1); assert_eq!( - flows.values().next().unwrap().fair_limit, - MuxConfig::default().stream_window_bytes + flows + .values() + .next() + .unwrap() + .send_credit + .available_permits(), + credit_units(MuxConfig::default().stream_window_bytes) ); } drop(retained); diff --git a/src/tests/mux/wire.rs b/src/tests/mux/wire.rs index 7cf3f5d..e7d7e0a 100644 --- a/src/tests/mux/wire.rs +++ b/src/tests/mux/wire.rs @@ -12,16 +12,6 @@ fn header_has_stable_eight_byte_vector() { fn only_connection_window_accepts_zero_flow_id() { assert!(FrameHeader::window(0, 1).is_ok()); assert!(FrameHeader::stream(0, 0, 1).is_err()); - assert_eq!( - FrameHeader { - kind: FrameKind::Datagram, - flags: 0, - value: 1, - flow_id: 0, - } - .validate(), - Err(WireError::InvalidFlowId) - ); } #[test] @@ -30,3 +20,9 @@ fn reset_is_exclusive_and_empty() { assert!(FrameHeader::stream(1, FLAG_RST | FLAG_FIN, 0).is_err()); assert!(FrameHeader::stream(1, FLAG_RST, 1).is_err()); } + +#[test] +fn open_uses_value_for_initial_window_credit() { + assert!(FrameHeader::stream(1, FLAG_SYN, 12 * 1024).is_ok()); + assert!(FrameHeader::stream(1, FLAG_SYN | FLAG_FIN, 0).is_err()); +} diff --git a/src/tests/portal/conn/tcp.rs b/src/tests/portal/conn/tcp.rs index 4d954ff..294f304 100644 --- a/src/tests/portal/conn/tcp.rs +++ b/src/tests/portal/conn/tcp.rs @@ -381,6 +381,10 @@ async fn tls_mux_carrier_closes_after_becoming_fully_idle() { .await .unwrap() .unwrap(); + let mut window = [0_u8; 8]; + tls.read_exact(&mut window).await.unwrap(); + assert_eq!(window[0], 0x02); + assert_eq!(&window[4..], &[0, 0, 0, 0]); let mut byte = [0_u8; 1]; let read = tls.read(&mut byte).await; assert!( diff --git a/src/tests/transport/quic.rs b/src/tests/transport/quic.rs index 56e81ab..28607a2 100644 --- a/src/tests/transport/quic.rs +++ b/src/tests/transport/quic.rs @@ -6,9 +6,9 @@ use super::*; #[test] fn profiles_keep_streams_within_connection_limits() { for profile in [ - parse_quic_profile(Some("memory")).unwrap(), - parse_quic_profile(Some("balanced")).unwrap(), - parse_quic_profile(Some("throughput")).unwrap(), + parse_transport_profile(Some("memory")).unwrap(), + parse_transport_profile(Some("balanced")).unwrap(), + parse_transport_profile(Some("throughput")).unwrap(), ] { assert!(profile.stream_receive_window <= profile.connection_receive_window); assert!(u64::from(profile.connection_receive_window) <= profile.send_window * 2); @@ -18,12 +18,12 @@ fn profiles_keep_streams_within_connection_limits() { #[test] fn throughput_is_the_default_profile() { assert_eq!( - parse_quic_profile(None).unwrap(), - QuicFlowControl::THROUGHPUT + parse_transport_profile(None).unwrap(), + TransportFlowControl::THROUGHPUT ); } #[test] fn rejects_unknown_profiles() { - assert!(parse_quic_profile(Some("tiny")).is_err()); + assert!(parse_transport_profile(Some("tiny")).is_err()); } diff --git a/src/tests/vector/session.rs b/src/tests/vector/session.rs index 1f8bc64..10a0cb9 100644 --- a/src/tests/vector/session.rs +++ b/src/tests/vector/session.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: GPL-3.0-only #[test] -fn quic_flow_control_matches_authenticated_portal_capacity() { - let flow_control = crate::transport::quic_flow_control().unwrap(); +fn transport_flow_control_matches_authenticated_portal_capacity() { + let flow_control = crate::transport::transport_flow_control().unwrap(); assert!(flow_control.stream_receive_window <= flow_control.connection_receive_window); assert!(flow_control.send_window >= u64::from(flow_control.stream_receive_window)); } diff --git a/src/tests/vector/session/tls.rs b/src/tests/vector/session/tls.rs index 7368900..5888bbb 100644 --- a/src/tests/vector/session/tls.rs +++ b/src/tests/vector/session/tls.rs @@ -61,6 +61,28 @@ async fn shard_selection_uses_the_least_loaded_carrier() { assert!(selected.handle.same_carrier(&handle_b)); } +#[tokio::test] +async fn full_pool_reuses_the_least_loaded_shard() { + let mut shards = Vec::new(); + let mut peer_handles = Vec::new(); + let mut streams = Vec::new(); + let mut peers = Vec::new(); + for shard_index in 0..TLS_MUX_MAX_SHARDS_PER_DIRECTION { + let (left, right) = tokio::io::duplex(1 << 20); + let (handle, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + let (peer_handle, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); + for index in 0..TLS_MUX_FLOWS_PER_SHARD { + let flow_id = (shard_index * TLS_MUX_FLOWS_PER_SHARD + index + 1) as u32; + streams.push(handle.open_stream(flow_id).await.unwrap()); + peers.push(incoming.accept().await.unwrap().unwrap()); + } + shards.push(shard(handle)); + peer_handles.push(peer_handle); + } + assert!(select_available_mux(&shards).is_some()); + drop((peer_handles, streams, peers)); +} + #[tokio::test] async fn closing_one_shard_does_not_affect_another() { let (left_a, right_a) = tokio::io::duplex(1 << 20); diff --git a/src/transport/mod.rs b/src/transport/mod.rs index 812f06a..d681c4c 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -4,11 +4,15 @@ //! Transport support primitives for buffers, rate limits, and counters. mod buffers; +mod owned_io; mod quic; mod rate; mod stats; pub use buffers::{BufferLease, Buffers}; -pub(crate) use quic::quic_flow_control; +pub(crate) use owned_io::{ + AsyncReadAny, AsyncWriteAny, read_owned, read_owned_from, write_owned, write_owned_to, +}; +pub(crate) use quic::{TransportFlowControl, transport_flow_control}; pub use rate::{RateLimiter, TokenBucket}; pub use stats::Stats; diff --git a/src/transport/owned_io.rs b/src/transport/owned_io.rs new file mode 100644 index 0000000..64ab99e --- /dev/null +++ b/src/transport/owned_io.rs @@ -0,0 +1,143 @@ +// Copyright (C) 2026 NodePassProject +// SPDX-License-Identifier: GPL-3.0-only + +//! Owned payload handoff for relay paths that terminate in TLS Mux. + +use std::any::Any; +use std::io; +use std::pin::Pin; + +use bytes::{Bytes, BytesMut}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, BufReader}; + +use crate::mux::{FRAME_BYTES, FlowReader, FlowWriter, MuxChunk}; + +pub(crate) trait AsyncReadAny: AsyncRead + Send + Unpin { + fn as_any_mut(&mut self) -> &mut dyn Any; +} + +impl AsyncReadAny for T { + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } +} + +pub(crate) trait AsyncWriteAny: AsyncWrite + Send + Unpin { + fn as_any_mut(&mut self) -> &mut dyn Any; +} + +impl AsyncWriteAny for T { + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } +} + +pub(crate) enum RelayChunk { + Mux(MuxChunk), + Bytes(Bytes), +} + +impl RelayChunk { + pub(crate) fn len(&self) -> usize { + self.as_ref().len() + } +} + +impl AsRef<[u8]> for RelayChunk { + fn as_ref(&self) -> &[u8] { + match self { + Self::Mux(chunk) => chunk.as_ref(), + Self::Bytes(bytes) => bytes, + } + } +} + +pub(crate) async fn read_owned( + reader: &mut Pin>, +) -> io::Result> { + let any = reader.as_mut().get_mut().as_any_mut(); + if let Some(reader) = any.downcast_mut::() { + return reader + .recv_chunk() + .await + .map(|chunk| chunk.map(RelayChunk::Mux)); + } + if let Some(reader) = any.downcast_mut::>() + && reader.buffer().is_empty() + { + return reader + .get_mut() + .recv_chunk() + .await + .map(|chunk| chunk.map(RelayChunk::Mux)); + } + read_owned_from(reader).await +} + +pub(crate) async fn read_owned_from( + reader: &mut R, +) -> io::Result> { + let mut payload = BytesMut::with_capacity(FRAME_BYTES); + let count = reader.read_buf(&mut payload).await?; + if count == 0 { + Ok(None) + } else { + Ok(Some(RelayChunk::Bytes(payload.freeze()))) + } +} + +pub(crate) async fn write_owned( + writer: &mut Pin>, + chunk: RelayChunk, +) -> io::Result<()> { + if let Some(writer) = writer + .as_mut() + .get_mut() + .as_any_mut() + .downcast_mut::() + { + let chunk = match chunk { + RelayChunk::Mux(chunk) => chunk, + RelayChunk::Bytes(bytes) => MuxChunk::from_bytes(bytes), + }; + return writer.send_chunk(chunk).await; + } + writer.write_all(chunk.as_ref()).await +} + +pub(crate) async fn write_owned_to( + writer: &mut W, + chunk: RelayChunk, +) -> io::Result<()> { + writer.write_all(chunk.as_ref()).await +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mux::{MuxConfig, MuxHandle}; + + #[tokio::test] + async fn owned_bytes_enter_mux_without_a_borrowed_payload_copy() { + let (left, right) = tokio::io::duplex(1 << 20); + let (client, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + let (_server, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); + let stream = client.open_stream(901).await.unwrap(); + let (_reader, writer) = stream.into_split(); + let mut writer: Pin> = Box::pin(writer); + let mut peer = incoming.accept().await.unwrap().unwrap(); + client.reset_borrowed_write_copies(); + + write_owned( + &mut writer, + RelayChunk::Bytes(Bytes::from_static(b"owned payload")), + ) + .await + .unwrap(); + let mut received = [0; 13]; + peer.read_exact(&mut received).await.unwrap(); + + assert_eq!(&received, b"owned payload"); + assert_eq!(client.borrowed_write_copies(), 0); + } +} diff --git a/src/transport/quic.rs b/src/transport/quic.rs index 7c8d8a6..b6675a8 100644 --- a/src/transport/quic.rs +++ b/src/transport/quic.rs @@ -1,20 +1,20 @@ // Copyright (C) 2026 NodePassProject // SPDX-License-Identifier: GPL-3.0-only -//! Shared QUIC flow-control budgets for every protocol version and Mux setting. +//! Shared transport flow-control budgets for QUIC and TLS Mux. use anyhow::{Result, bail}; const MIB: u32 = 1024 * 1024; #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub(crate) struct QuicFlowControl { +pub(crate) struct TransportFlowControl { pub(crate) stream_receive_window: u32, pub(crate) connection_receive_window: u32, pub(crate) send_window: u64, } -impl QuicFlowControl { +impl TransportFlowControl { const MEMORY: Self = Self::new(4, 8, 8); const BALANCED: Self = Self::new(8, 16, 16); const THROUGHPUT: Self = Self::new(16, 32, 32); @@ -32,16 +32,20 @@ impl QuicFlowControl { /// /// The throughput profile preserves the established high-BDP values; memory /// and balanced remain available through the environment override. -pub(crate) fn quic_flow_control() -> Result { - parse_quic_profile(std::env::var("NOW_QUIC_MEMORY_PROFILE").ok().as_deref()) +pub(crate) fn transport_flow_control() -> Result { + parse_transport_profile( + std::env::var("NOW_TRANSPORT_MEMORY_PROFILE") + .ok() + .as_deref(), + ) } -fn parse_quic_profile(value: Option<&str>) -> Result { +fn parse_transport_profile(value: Option<&str>) -> Result { match value.unwrap_or("throughput") { - "memory" => Ok(QuicFlowControl::MEMORY), - "balanced" => Ok(QuicFlowControl::BALANCED), - "throughput" => Ok(QuicFlowControl::THROUGHPUT), - _ => bail!("NOW_QUIC_MEMORY_PROFILE must be memory, balanced, or throughput"), + "memory" => Ok(TransportFlowControl::MEMORY), + "balanced" => Ok(TransportFlowControl::BALANCED), + "throughput" => Ok(TransportFlowControl::THROUGHPUT), + _ => bail!("NOW_TRANSPORT_MEMORY_PROFILE must be memory, balanced, or throughput"), } } diff --git a/src/vector/flow.rs b/src/vector/flow.rs index 94e3271..8ab8c53 100644 --- a/src/vector/flow.rs +++ b/src/vector/flow.rs @@ -11,7 +11,7 @@ use std::task::{Context as TaskContext, Poll}; use std::time::Duration; use anyhow::{Context, Result, anyhow}; -use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; use tokio::net::TcpStream; use tokio::sync::OwnedSemaphorePermit; use tokio::time::timeout; @@ -40,8 +40,8 @@ mod tcp; pub(crate) use self::tcp::{TcpTunnel, TcpTunnelGuard}; pub(super) use self::tcp::{open_tcp, relay_tcp}; -pub(crate) type BoxReader = Pin>; -pub(crate) type BoxWriter = Pin>; +pub(crate) type BoxReader = Pin>; +pub(crate) type BoxWriter = Pin>; pub(super) struct PhysicalLane { pub(super) reader: Option, diff --git a/src/vector/flow/tcp.rs b/src/vector/flow/tcp.rs index a8645c4..bdfd05b 100644 --- a/src/vector/flow/tcp.rs +++ b/src/vector/flow/tcp.rs @@ -4,6 +4,7 @@ //! TCP tunnel setup and bidirectional relay. use super::*; +use crate::transport::{read_owned, read_owned_from, write_owned, write_owned_to}; pub(crate) struct TcpTunnel { reader: BoxReader, @@ -231,21 +232,19 @@ pub(in crate::vector) async fn relay_tcp( let result = { let (mut client_read, mut client_write) = client.into_split(); - let mut up_buffer = vector.buffers.get_tcp_buffer(); - let mut down_buffer = vector.buffers.get_tcp_buffer(); let uplink = tunnel.uplink; let downlink = tunnel.downlink; let client_to_portal = async { loop { - let read = client_read.read(&mut up_buffer).await?; - if read == 0 { + let Some(chunk) = read_owned_from(&mut client_read).await? else { tunnel.writer.shutdown().await?; return Ok::<(), anyhow::Error>(()); - } + }; + let read = chunk.len(); if let Some(rate) = &vector.rate_limiter { rate.wait_read(read as i64).await; } - tunnel.writer.write_all(&up_buffer[..read]).await?; + write_owned(&mut tunnel.writer, chunk).await?; if uplink == Carrier::Quic && downlink == Carrier::TlsTcp { // A continuously writable QUIC stream can otherwise keep // this relay hot long enough to delay the opposite Mux @@ -262,15 +261,15 @@ pub(in crate::vector) async fn relay_tcp( }; let portal_to_client = async { loop { - let read = tunnel.reader.read(&mut down_buffer).await?; - if read == 0 { + let Some(chunk) = read_owned(&mut tunnel.reader).await? else { client_write.shutdown().await?; return Ok::<(), anyhow::Error>(()); - } + }; + let read = chunk.len(); if let Some(rate) = &vector.rate_limiter { rate.wait_write(read as i64).await; } - client_write.write_all(&down_buffer[..read]).await?; + write_owned_to(&mut client_write, chunk).await?; access.add_download(read as u64); vector .stats diff --git a/src/vector/session.rs b/src/vector/session.rs index 4bda3f8..c1455fc 100644 --- a/src/vector/session.rs +++ b/src/vector/session.rs @@ -31,13 +31,14 @@ use crate::protocol::{ decode_udp_frame_owned, encode_auth_frame, encode_udp_close, }; use crate::telemetry::{RuntimeEvent, RuntimeKind, RuntimeLevel, TelemetryHub}; -use crate::transport::{Stats, quic_flow_control}; +use crate::transport::{Stats, transport_flow_control}; use super::config::PortalClientConfig; use super::tls::{ClientTls, EXPORTER_LABEL, quic_protocol_version}; const QUIC_DATAGRAM_BUFFER_SIZE: usize = 4 * 1024 * 1024; const TLS_MUX_FLOWS_PER_SHARD: usize = 4; +const TLS_MUX_MAX_SHARDS_PER_DIRECTION: usize = 4; #[derive(Clone)] pub(super) struct ClientSignals { diff --git a/src/vector/session/quic.rs b/src/vector/session/quic.rs index 0ad83be..27b13e0 100644 --- a/src/vector/session/quic.rs +++ b/src/vector/session/quic.rs @@ -375,7 +375,7 @@ fn configure_quic_transport( idle_timeout: Duration, keepalive_interval: Duration, ) -> Result<()> { - let flow_control = quic_flow_control()?; + let flow_control = transport_flow_control()?; let mut transport = quinn::TransportConfig::default(); transport.datagram_receive_buffer_size(Some(QUIC_DATAGRAM_BUFFER_SIZE)); transport.datagram_send_buffer_size(QUIC_DATAGRAM_BUFFER_SIZE); diff --git a/src/vector/session/tls.rs b/src/vector/session/tls.rs index 16242a3..728d9bd 100644 --- a/src/vector/session/tls.rs +++ b/src/vector/session/tls.rs @@ -206,12 +206,20 @@ impl TlsManager { } fn select_available_mux(muxes: &[TlsMux]) -> Option { - muxes + let available = muxes .iter() .filter(|shard| !shard.handle.is_closed()) .min_by_key(|shard| shard.handle.active_streams()) - .filter(|shard| shard.handle.active_streams() < TLS_MUX_FLOWS_PER_SHARD) - .cloned() + .cloned()?; + let active = available.handle.active_streams(); + if !available.handle.has_stream_capacity() { + return None; + } + if active < TLS_MUX_FLOWS_PER_SHARD || muxes.len() >= TLS_MUX_MAX_SHARDS_PER_DIRECTION { + Some(available) + } else { + None + } } #[cfg(test)] From 525a5c3280c1d72b0aec0c6f76bb9499b6ae34b2 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Tue, 8 Sep 2026 06:06:55 +0800 Subject: [PATCH 23/62] Adapt TLS Mux scheduling and buffer reuse --- docs/compatibility.md | 5 +-- docs/configuration.md | 11 ++++--- docs/mux-performance.md | 45 +++++++++++++++++++++++++-- docs/operations.md | 5 +-- docs/protocol.md | 8 +++-- docs/security.md | 6 ++-- scripts/bench-mux-local.sh | 13 +++++--- scripts/mux-bench.py | 26 ++++++++++++++-- src/mux/driver.rs | 21 +++++++++++++ src/mux/handle.rs | 10 ++++++ src/mux/mod.rs | 9 ++++-- src/portal/conn/relay_stream.rs | 4 +-- src/tests/mux/runtime.rs | 54 ++++++++++++++++++++++++++++++++- src/tests/portal/conn/vector.rs | 9 ++++-- src/tests/vector/session/tls.rs | 24 +++++++++------ src/transport/buffers.rs | 6 ++++ src/transport/owned_io.rs | 33 +++++++++++++++++--- src/vector/flow/tcp.rs | 4 +-- src/vector/session.rs | 1 - src/vector/session/tls.rs | 21 +++++++++++-- 20 files changed, 262 insertions(+), 53 deletions(-) diff --git a/docs/compatibility.md b/docs/compatibility.md index 9d2b287..0960687 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -104,8 +104,9 @@ and never appears on a dedicated lane. ## Runtime contract -Mux Shards open lazily at 4 active flows up to a 4-Shard directional pool, -select the least-loaded live Shard, and close after 30 seconds fully idle. +Mux Shards open lazily using TLS setup latency and carrier pressure, up to a +4-Shard directional pool. They select the least-loaded live Shard and close +after 30 seconds fully idle. Dedicated lanes and Mux streams use the same authentication, FlowHeader, Target, setup result, pairing and limits. QUIC behavior is independent from the client Mux setting. diff --git a/docs/configuration.md b/docs/configuration.md index 870df92..f08f03b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -222,8 +222,10 @@ has no health score or circuit breaker. Both carriers must be declared for carrier. With `mux=1`, Shards open lazily according to active flow pressure. New flows -use the least-loaded shard; a shard carries 4 active flows before another -opens, with at most 4 shards per direction. A shard closes after 30 seconds +use the least-loaded shard. TLS setup latency selects a target density of 16, +8, 4, or 2 flows at 30, 75, and 200 ms, while connection-credit or queue +pressure can expand earlier. +Each direction has at most 4 shards. A shard closes after 30 seconds fully idle. With `mux=0`, every TLS-carried Flow owns one on-demand lane that closes with the Flow. Mux applies when at least one direction is `tcp` or `mix`. `udp/udp&mux=1` canonicalizes to @@ -309,8 +311,9 @@ Durations use humantime syntax such as `250ms`, `15s`, `2m`, or `1h`. TLS Mux shares the transport profile's 4/8, 8/16, or 16/32 MiB stream/connection receive windows with QUIC. A Mux carrier permits 256 active streams and 512 queued frame slots; queued payload remains charged against the connection -window. The application opens another shard after 4 active flows, caps each -direction at 4 shards, reuses the least-loaded shard after that, and retires +window. The application adapts shard density to measured TLS setup latency and +live carrier pressure, caps each direction at 4 shards, reuses the least-loaded +shard after that, and retires fully idle shards after 30 seconds. `NOW_MAX_TCP_FLOWS` is the hard per-session logical TCP limit shared by TLS and QUIC. `NOW_MAX_UDP_FLOWS` is the corresponding UDP limit shared by UoT and QUIC DATAGRAM. Excess flows fail diff --git a/docs/mux-performance.md b/docs/mux-performance.md index 0dfb6eb..036bdf7 100644 --- a/docs/mux-performance.md +++ b/docs/mux-performance.md @@ -35,11 +35,50 @@ claims. The larger profile removes the dominant high-RTT collapse. Four Shards improve aggregate 16-flow throughput by about 2.5 times at both measured RTTs, while -raising peak RSS substantially. The runtime therefore opens Shards lazily at -four active flows and caps the directional pool at four. A single flow remains -on one Shard. +raising peak RSS substantially. The runtime therefore adapts target density to +TLS setup latency and live carrier pressure, and caps the directional pool at +four. A single flow remains on one Shard. At 300 ms, a 16 MiB stream window has a theoretical ceiling near 447 Mbps. The measured 288.04 Mbps is about 64% of that ceiling, leaving room to improve credit return timing, writer batching, allocation reuse, and TLS scheduling before increasing the profile budgets. + +## 2026-09-08 adaptive pass + +This pass retains the minimal 8-byte STREAM/WINDOW wire format and the 1/8-window +credit-return threshold. A 1/16 threshold did not improve throughput and would +send more control frames. Generic TCP relay reads now transfer a pooled buffer +lease into `Bytes`, so the allocation returns to the existing transport pool +when the last payload owner drops. Mux-to-relay reads continue to hand off their +owned frame payload directly. + +| RTT | Flows | Payload | TLS carriers | Throughput | Portal peak RSS | Vector peak RSS | +|---:|---:|---:|---:|---:|---:|---:| +| 10 ms | 16 | 48 MiB | 1 | 6.77 Gbps | 26.77 MiB | 10.84 MiB | +| 100 ms | 1 | 64 MiB | 1 | 937.75 Mbps | 20.08 MiB | 10.47 MiB | +| 300 ms | 1 | 32 MiB | 1 | 283.82 Mbps | 20.36 MiB | 12.56 MiB | +| 100 ms | 16 | 128 MiB | 4 | 4.74 Gbps | 57.48 MiB | 12.78 MiB | +| 300 ms | 16 | 128 MiB | 4 | 1.66 Gbps | 75.64 MiB | 26.30 MiB | + +The adaptive policy keeps 16 simultaneous low-RTT flows on one carrier, while +100 and 300 ms samples expand to four. The high-RTT aggregate results match or +slightly exceed the fixed-four baseline within normal local-run variation, so +the four-carrier cap remains useful under high BDP without imposing four TLS +connections on low-latency workloads. Single-flow throughput is unchanged. + +Multi-DATA vectored batching was tested and rejected. Tokio-rustls can complete +a vectored write after consuming only part of a header/payload pair; extending +that state machine across several frames caused boundary corruption under +concurrency. The retained writer issues one frame at a time but already sends +its 8-byte header and owned payload through one vectored-write path without +coalescing them into a copy buffer. Reader and writer loops now also observe a +closed flag before waiting, and the reader yields after each 32 DATA frames so +WINDOW production cannot be starved by a continuously ready TLS carrier. + +The Apple Container Toxiproxy path became unreliable for sustained 10 ms bursts +above 48 MiB in this run, while 48 MiB crossed the 32 MiB connection window and +completed. The 100 and 300 ms sustained samples were run from fresh proxy +containers; the benchmark now records carrier topology alongside throughput and +RSS so future comparisons can distinguish pool-size changes from data-path +changes. diff --git a/docs/operations.md b/docs/operations.md index 7b5ec18..672598d 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -58,8 +58,9 @@ per authenticated client session, the selected 4/8, 8/16, or 16/32 MiB per-stream/per-Mux receive windows, 256 streams per Mux, bounded reusable relay-buffer caches, and QUIC UDP queue/reassembly limits. UoT and QUIC DATAGRAM share the UDP flow limit. TLS -shards originated with `mux=1` by Vector or a Portal `next` client target 4 -active flows, stop at 4 shards per direction, use least-loaded placement, and +shards originated with `mux=1` by Vector or a Portal `next` client adapt their +target density to TLS setup latency and carrier pressure, stop at 4 shards per +direction, use least-loaded placement, and close after 30 seconds fully idle. Frame queue slots do not bypass byte credit. Windows are granted as permits and payload is admitted incrementally. diff --git a/docs/protocol.md b/docs/protocol.md index 806bec6..e5ca9bd 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -281,9 +281,11 @@ Both credit checks precede queue admission. A stream therefore cannot reserve payload beyond either advertised receive window. Client-side Shards open lazily in separate uplink and downlink sets. A new flow -uses the least-loaded live Shard for its TLS direction; a new Shard opens when -all live Shards in that set have 4 active flows, up to 4 live Shards per -direction. Once the pool is full, new flows reuse its least-loaded Shard. A symmetric `tcp/tcp` flow +uses the least-loaded live Shard for its TLS direction. Its target density is +derived from measured TLS setup latency: 16, 8, 4, or 2 active flows as setup +latency crosses 30, 75, and 200 ms. Exhausting three quarters of connection +credit or frame-queue capacity can open the next Shard earlier, up to 4 live +Shards per direction. Once the pool is full, new flows reuse its least-loaded Shard. A symmetric `tcp/tcp` flow uses one duplex stream from the uplink set. A fully idle Shard closes after 30 seconds. Portal applies the same timeout to an authenticated Mux carrier with no active streams. Sharding is runtime placement and does not add wire fields. diff --git a/docs/security.md b/docs/security.md index c133b6e..3566a67 100644 --- a/docs/security.md +++ b/docs/security.md @@ -72,8 +72,8 @@ for a terminal stream are idempotent. The transport memory profile bounds Mux stream/connection windows at 4/8, 8/16, or 16/32 MiB, and each Mux allows 256 active streams. With client `mux=1`, -Vector or Portal `next` places at most 4 active flows on a shard before opening -another, caps each direction at 4 shards, distributes new flows to the +Vector or Portal `next` adapts a shard's target density to TLS setup latency and +live connection pressure, caps each direction at 4 shards, distributes new flows to the least-loaded shard, and closes a fully idle shard after 30 seconds. One authenticated inbound Mux carrier is subject to the same fully idle timeout. One authenticated client session admits at most 1,024 concurrent logical TCP @@ -92,7 +92,7 @@ authenticated client session +-- TCP budget: 1,024 active flows | | | +-- dedicated TLS lane - | +-- Mux stream --> TLS Shard, target density 4 + | +-- Mux stream --> adaptive TLS Shard | +-- QUIC reliable stream | +-- UDP budget: 256 active flows diff --git a/scripts/bench-mux-local.sh b/scripts/bench-mux-local.sh index 3e063d0..1a8f31e 100755 --- a/scripts/bench-mux-local.sh +++ b/scripts/bench-mux-local.sh @@ -11,10 +11,15 @@ mux=${4:-1} binary=${5:-target/release/nowhere} container_name=nowhere-toxiproxy -if ! container list | awk '{print $1}' | grep -Fqx "$container_name"; then - container run --rm -d --name "$container_name" \ - ghcr.io/shopify/toxiproxy:2.12.0 >/dev/null -fi +# A timed-out high-rate sample can leave Toxiproxy links draining long after +# both Nowhere processes exit. Use a fresh proxy process so the next sample is +# independent instead of blocking while the old proxy is reconfigured. +container delete -f "$container_name" >/dev/null 2>&1 || true +while container list | awk 'NR > 1 { print $1 }' | grep -Fqx "$container_name"; do + sleep 0.1 +done +container run --rm -d --name "$container_name" \ + ghcr.io/shopify/toxiproxy:2.12.0 >/dev/null proxy_ip=$(container list | awk -v name="$container_name" ' NR == 1 { diff --git a/scripts/mux-bench.py b/scripts/mux-bench.py index aead6da..a72c772 100755 --- a/scripts/mux-bench.py +++ b/scripts/mux-bench.py @@ -115,6 +115,17 @@ def rss_kib(pid): return int(output.strip()) +def established_tcp(pid): + try: + output = subprocess.check_output( + ["lsof", "-nP", "-a", "-p", str(pid), "-iTCP", "-sTCP:ESTABLISHED"], + text=True, stderr=subprocess.DEVNULL, + ) + except (FileNotFoundError, subprocess.CalledProcessError): + return None + return max(0, len(output.splitlines()) - 1) + + def wait_port(port, host="127.0.0.1", deadline=10): until = time.monotonic() + deadline while time.monotonic() < until: @@ -185,7 +196,14 @@ def main(): env=env, stdout=output, stderr=output, ) wait_port(1080) - barrier = threading.Barrier(args.flows) + topology = {} + + def capture_topology(): + connections = established_tcp(vector.pid) + if connections is not None: + topology["tls_carriers"] = max(0, connections - args.flows) + + barrier = threading.Barrier(args.flows, action=capture_topology) durations = [0.0] * args.flows byte_count = args.mib_per_flow * 1024 * 1024 peak = {"portal": rss_kib(portal.pid), "vector": rss_kib(vector.pid)} @@ -217,7 +235,7 @@ def sample(): raise RuntimeError(f"flow failures: {errors}") total = byte_count * args.flows transfer = max(durations) - print(json.dumps({ + result = { "rtt_ms": args.rtt_ms, "flows": args.flows, "mux": int(args.mux), "profile": args.profile, "mib": total / 1024 / 1024, "seconds": round(transfer, 3), "mbps": round(total * 8 / transfer / 1_000_000, 2), @@ -225,7 +243,9 @@ def sample(): "flow_max_seconds": round(max(durations), 3), "portal_peak_rss_mib": round(peak["portal"] / 1024, 2), "vector_peak_rss_mib": round(peak["vector"] / 1024, 2), - }, sort_keys=True)) + } + result.update(topology) + print(json.dumps(result, sort_keys=True)) finally: stop.set() for process in (vector, portal): diff --git a/src/mux/driver.rs b/src/mux/driver.rs index 223bdc7..a32f5d9 100644 --- a/src/mux/driver.rs +++ b/src/mux/driver.rs @@ -48,7 +48,11 @@ pub(super) async fn send_data( pub(super) async fn run_reader(mut reader: R, shared: Arc) { let result: io::Result<()> = async { + let mut data_frames = 0_u8; loop { + if shared.closed.load(std::sync::atomic::Ordering::Acquire) { + return Ok(()); + } let mut encoded = [0; HEADER_LEN]; tokio::select! { _ = shared.closed_notify.notified() => return Ok(()), @@ -70,6 +74,13 @@ pub(super) async fn run_reader(mut reader: R, shared: Arc< FrameKind::Stream => receive_stream(&shared, header, Bytes::from(payload)).await?, FrameKind::Window => receive_window(&shared, header)?, } + if payload_len != 0 { + data_frames = data_frames.wrapping_add(1); + if data_frames == 32 { + data_frames = 0; + tokio::task::yield_now().await; + } + } } } .await; @@ -149,6 +160,10 @@ fn receive_window(shared: &Shared, header: FrameHeader) -> io::Result<()> { )); } shared.connection_send_credit.add_permits(credit); + shared.connection_send_peak.fetch_max( + shared.connection_send_credit.available_permits(), + std::sync::atomic::Ordering::Relaxed, + ); return Ok(()); } let mut flows = shared.flows.lock().expect("mux flow lock"); @@ -172,6 +187,9 @@ fn receive_window(shared: &Shared, header: FrameHeader) -> io::Result<()> { pub(super) async fn run_terminals(shared: Arc, mut terminal_rx: mpsc::Receiver) { loop { + if shared.closed.load(std::sync::atomic::Ordering::Acquire) { + return; + } let flow_id = tokio::select! { _ = shared.closed_notify.notified() => return, flow_id = terminal_rx.recv() => flow_id, @@ -204,6 +222,9 @@ pub(super) async fn run_writer( let mut pending_item = None; let result: io::Result<()> = async { loop { + if shared.closed.load(std::sync::atomic::Ordering::Acquire) { + return Ok(()); + } let item = if let Some(item) = pending_item.take() { Some(item) } else { diff --git a/src/mux/handle.rs b/src/mux/handle.rs index 295affd..6147c13 100644 --- a/src/mux/handle.rs +++ b/src/mux/handle.rs @@ -31,6 +31,9 @@ impl MuxHandle { connection_send_credit: Arc::new(Semaphore::new(super::credit_units( super::BASE_CONNECTION_WINDOW_BYTES, ))), + connection_send_peak: AtomicUsize::new(super::credit_units( + super::BASE_CONNECTION_WINDOW_BYTES, + )), connection_receive_credit: Mutex::new(super::credit_units( config.connection_window_bytes, )), @@ -92,6 +95,13 @@ impl MuxHandle { self.active_streams() < self.shared.config.max_streams } + pub(crate) fn is_send_congested(&self) -> bool { + let available = self.shared.connection_send_credit.available_permits(); + let peak = self.shared.connection_send_peak.load(Ordering::Relaxed); + available.saturating_mul(4) < peak + || self.shared.data_tx.capacity().saturating_mul(4) < self.shared.config.outbound_frames + } + #[cfg(test)] pub(crate) fn borrowed_write_copies(&self) -> usize { self.shared.borrowed_write_copies.load(Ordering::Relaxed) diff --git a/src/mux/mod.rs b/src/mux/mod.rs index f4aa16f..c7316d7 100644 --- a/src/mux/mod.rs +++ b/src/mux/mod.rs @@ -27,6 +27,7 @@ const BASE_CONNECTION_WINDOW_BYTES: usize = 8 * MIB; const MAX_STREAM_WINDOW_BYTES: usize = 16 * MIB; const MAX_CONNECTION_WINDOW_BYTES: usize = 32 * MIB; const CREDIT_UNIT_BYTES: usize = 1024; +const WINDOW_UPDATE_DIVISOR: usize = 8; // Frame count is separate from the byte window: UoT carries many small // packets, so the frame queue absorbs scheduling bursts while the byte window // remains the hard payload bound. @@ -127,6 +128,7 @@ struct Shared { config: MuxConfig, flows: Mutex>, connection_send_credit: Arc, + connection_send_peak: AtomicUsize, connection_receive_credit: Mutex, pending_connection_credit: AtomicUsize, ready_flows: Mutex>, @@ -359,7 +361,8 @@ impl Shared { true }; let threshold = - credit_units(self.config.stream_window_bytes / 8).min(u16::MAX as usize); + credit_units(self.config.stream_window_bytes / WINDOW_UPDATE_DIVISOR) + .min(u16::MAX as usize); (ready, flow.pending_receive_credit >= threshold) } else { return; @@ -374,8 +377,8 @@ impl Shared { let previous = self .pending_connection_credit .fetch_add(charge, Ordering::AcqRel); - let threshold = - credit_units(self.config.connection_window_bytes / 8).min(u16::MAX as usize); + let threshold = credit_units(self.config.connection_window_bytes / WINDOW_UPDATE_DIVISOR) + .min(u16::MAX as usize); if flow_notify || previous.saturating_add(charge) >= threshold { self.control_notify.notify_one(); } diff --git a/src/portal/conn/relay_stream.rs b/src/portal/conn/relay_stream.rs index 3ec1e8b..e3de4e8 100644 --- a/src/portal/conn/relay_stream.rs +++ b/src/portal/conn/relay_stream.rs @@ -33,7 +33,7 @@ where let client_to_target = async { loop { - let Some(chunk) = read_owned(client_read).await? else { + let Some(chunk) = read_owned(client_read, &portal.buffers).await? else { target_write.shutdown().await?; return Ok::<(), anyhow::Error>(()); }; @@ -56,7 +56,7 @@ where let target_to_client = async { loop { - let Some(chunk) = read_owned_from(&mut target_read).await? else { + let Some(chunk) = read_owned_from(&mut target_read, &portal.buffers).await? else { client_write.shutdown().await?; return Ok::<(), anyhow::Error>(()); }; diff --git a/src/tests/mux/runtime.rs b/src/tests/mux/runtime.rs index 2a56ecd..95567be 100644 --- a/src/tests/mux/runtime.rs +++ b/src/tests/mux/runtime.rs @@ -168,11 +168,26 @@ async fn stream_credit_does_not_shrink_with_active_stream_count() { let (client, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); let (_server, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); let mut streams = Vec::new(); + let mut peers = Vec::new(); for flow_id in 1..=128 { streams.push(client.open_stream(flow_id).await.unwrap()); - let _ = incoming.accept().await.unwrap().unwrap(); + peers.push(incoming.accept().await.unwrap().unwrap()); } + tokio::time::timeout(Duration::from_secs(1), async { + loop { + let all_ready = client.shared.flows.lock().unwrap().values().all(|flow| { + flow.send_credit.available_permits() + == credit_units(MuxConfig::default().stream_window_bytes) + }); + if all_ready { + break; + } + tokio::task::yield_now().await; + } + }) + .await + .unwrap(); { let flows = client.shared.flows.lock().unwrap(); assert!(flows.values().all(|flow| { @@ -197,4 +212,41 @@ async fn stream_credit_does_not_shrink_with_active_stream_count() { ); } drop(retained); + drop(peers); +} + +#[tokio::test] +async fn concurrent_streams_cross_the_connection_window() { + const FLOWS: u32 = 16; + const BYTES_PER_FLOW: usize = 3 * MIB; + let (left, right) = tokio::io::duplex(1 << 20); + let (client, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + let (_server, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); + let mut writers = Vec::new(); + let mut readers = Vec::new(); + for flow_id in 1..=FLOWS { + writers.push(client.open_stream(flow_id).await.unwrap()); + readers.push(incoming.accept().await.unwrap().unwrap()); + } + + let mut tasks = writers + .into_iter() + .map(|mut stream| { + tokio::spawn(async move { + stream.write_all(&vec![0x5a; BYTES_PER_FLOW]).await.unwrap(); + stream.shutdown().await.unwrap(); + }) + }) + .collect::>(); + tasks.extend(readers.into_iter().map(|mut stream| { + tokio::spawn(async move { + let mut received = Vec::new(); + stream.read_to_end(&mut received).await.unwrap(); + assert_eq!(received.len(), BYTES_PER_FLOW); + assert!(received.iter().all(|byte| *byte == 0x5a)); + }) + })); + for task in tasks { + task.await.unwrap(); + } } diff --git a/src/tests/portal/conn/vector.rs b/src/tests/portal/conn/vector.rs index 5ba8de0..4d677f8 100644 --- a/src/tests/portal/conn/vector.rs +++ b/src/tests/portal/conn/vector.rs @@ -281,8 +281,8 @@ async fn mux_full_duplex_tcp_exceeds_each_direction_credit_window() { } #[tokio::test] -async fn mux_fifth_active_tcp_flow_opens_a_second_shard() { - const FLOW_COUNT: usize = 5; +async fn mux_seventeenth_active_tcp_flow_opens_a_second_low_latency_shard() { + const FLOW_COUNT: usize = 17; let target = TcpListener::bind("127.0.0.1:0").await.unwrap(); let target_address = target.local_addr().unwrap(); @@ -318,7 +318,10 @@ async fn mux_fifth_active_tcp_flow_opens_a_second_shard() { .unwrap(); assert_eq!(runtime.portal_stats.link_tcp.load(Ordering::Relaxed), 2); - assert_eq!(runtime.portal_stats.tcp_active.load(Ordering::Relaxed), 5); + assert_eq!( + runtime.portal_stats.tcp_active.load(Ordering::Relaxed), + FLOW_COUNT as i32 + ); drop(flows); target_shutdown.cancel(); target_task.await.unwrap(); diff --git a/src/tests/vector/session/tls.rs b/src/tests/vector/session/tls.rs index 5888bbb..fcd58b0 100644 --- a/src/tests/vector/session/tls.rs +++ b/src/tests/vector/session/tls.rs @@ -8,18 +8,19 @@ fn shard(handle: MuxHandle) -> TlsMux { TlsMux { handle, version: ProtocolVersion::V2, + target_density: 4, } } #[tokio::test] -async fn shard_selection_stops_at_four_active_flows() { +async fn shard_selection_stops_at_its_target_density() { let (left, right) = tokio::io::duplex(1 << 20); let (handle, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); let (_peer, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); let mut streams = Vec::new(); let mut peers = Vec::new(); - for flow_id in 1..TLS_MUX_FLOWS_PER_SHARD as u32 { + for flow_id in 1..4 { streams.push(handle.open_stream(flow_id).await.unwrap()); peers.push(incoming.accept().await.unwrap().unwrap()); } @@ -31,12 +32,7 @@ async fn shard_selection_stops_at_four_active_flows() { .same_carrier(&handle) ); - streams.push( - handle - .open_stream(TLS_MUX_FLOWS_PER_SHARD as u32) - .await - .unwrap(), - ); + streams.push(handle.open_stream(4).await.unwrap()); peers.push(incoming.accept().await.unwrap().unwrap()); assert!(select_available_mux(std::slice::from_ref(&shard)).is_none()); } @@ -71,8 +67,8 @@ async fn full_pool_reuses_the_least_loaded_shard() { let (left, right) = tokio::io::duplex(1 << 20); let (handle, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); let (peer_handle, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); - for index in 0..TLS_MUX_FLOWS_PER_SHARD { - let flow_id = (shard_index * TLS_MUX_FLOWS_PER_SHARD + index + 1) as u32; + for index in 0..4 { + let flow_id = (shard_index * 4 + index + 1) as u32; streams.push(handle.open_stream(flow_id).await.unwrap()); peers.push(incoming.accept().await.unwrap().unwrap()); } @@ -83,6 +79,14 @@ async fn full_pool_reuses_the_least_loaded_shard() { drop((peer_handles, streams, peers)); } +#[test] +fn setup_latency_adapts_target_density() { + assert_eq!(mux_target_density(Duration::from_millis(10)), 16); + assert_eq!(mux_target_density(Duration::from_millis(50)), 8); + assert_eq!(mux_target_density(Duration::from_millis(100)), 4); + assert_eq!(mux_target_density(Duration::from_millis(600)), 2); +} + #[tokio::test] async fn closing_one_shard_does_not_affect_another() { let (left_a, right_a) = tokio::io::duplex(1 << 20); diff --git a/src/transport/buffers.rs b/src/transport/buffers.rs index 827bc31..9f19f7e 100644 --- a/src/transport/buffers.rs +++ b/src/transport/buffers.rs @@ -94,6 +94,12 @@ impl AsMut<[u8]> for BufferLease { } } +impl AsRef<[u8]> for BufferLease { + fn as_ref(&self) -> &[u8] { + self.as_slice() + } +} + impl Drop for BufferLease { fn drop(&mut self) { let Some(buffer) = self.buffer.take() else { diff --git a/src/transport/owned_io.rs b/src/transport/owned_io.rs index 64ab99e..3d039ed 100644 --- a/src/transport/owned_io.rs +++ b/src/transport/owned_io.rs @@ -7,11 +7,13 @@ use std::any::Any; use std::io; use std::pin::Pin; -use bytes::{Bytes, BytesMut}; +use bytes::Bytes; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, BufReader}; use crate::mux::{FRAME_BYTES, FlowReader, FlowWriter, MuxChunk}; +use super::Buffers; + pub(crate) trait AsyncReadAny: AsyncRead + Send + Unpin { fn as_any_mut(&mut self) -> &mut dyn Any; } @@ -54,6 +56,7 @@ impl AsRef<[u8]> for RelayChunk { pub(crate) async fn read_owned( reader: &mut Pin>, + buffers: &Buffers, ) -> io::Result> { let any = reader.as_mut().get_mut().as_any_mut(); if let Some(reader) = any.downcast_mut::() { @@ -71,18 +74,22 @@ pub(crate) async fn read_owned( .await .map(|chunk| chunk.map(RelayChunk::Mux)); } - read_owned_from(reader).await + read_owned_from(reader, buffers).await } pub(crate) async fn read_owned_from( reader: &mut R, + buffers: &Buffers, ) -> io::Result> { - let mut payload = BytesMut::with_capacity(FRAME_BYTES); - let count = reader.read_buf(&mut payload).await?; + let mut payload = buffers.get_tcp_buffer(); + let capacity = payload.len().min(FRAME_BYTES); + let count = reader.read(&mut payload[..capacity]).await?; if count == 0 { Ok(None) } else { - Ok(Some(RelayChunk::Bytes(payload.freeze()))) + Ok(Some(RelayChunk::Bytes( + Bytes::from_owner(payload).slice(..count), + ))) } } @@ -140,4 +147,20 @@ mod tests { assert_eq!(&received, b"owned payload"); assert_eq!(client.borrowed_write_copies(), 0); } + + #[tokio::test] + async fn generic_reads_use_a_pool_owned_chunk() { + let buffers = Buffers::new(FRAME_BYTES, 1); + let mut reader = &b"pooled"[..]; + let chunk = read_owned_from(&mut reader, &buffers) + .await + .unwrap() + .unwrap(); + + assert_eq!(chunk.as_ref(), b"pooled"); + let allocation = chunk.as_ref().as_ptr(); + drop(chunk); + let reused = buffers.get_tcp_buffer(); + assert_eq!(reused.as_ptr(), allocation); + } } diff --git a/src/vector/flow/tcp.rs b/src/vector/flow/tcp.rs index bdfd05b..89ee949 100644 --- a/src/vector/flow/tcp.rs +++ b/src/vector/flow/tcp.rs @@ -236,7 +236,7 @@ pub(in crate::vector) async fn relay_tcp( let downlink = tunnel.downlink; let client_to_portal = async { loop { - let Some(chunk) = read_owned_from(&mut client_read).await? else { + let Some(chunk) = read_owned_from(&mut client_read, &vector.buffers).await? else { tunnel.writer.shutdown().await?; return Ok::<(), anyhow::Error>(()); }; @@ -261,7 +261,7 @@ pub(in crate::vector) async fn relay_tcp( }; let portal_to_client = async { loop { - let Some(chunk) = read_owned(&mut tunnel.reader).await? else { + let Some(chunk) = read_owned(&mut tunnel.reader, &vector.buffers).await? else { client_write.shutdown().await?; return Ok::<(), anyhow::Error>(()); }; diff --git a/src/vector/session.rs b/src/vector/session.rs index c1455fc..c3b6224 100644 --- a/src/vector/session.rs +++ b/src/vector/session.rs @@ -37,7 +37,6 @@ use super::config::PortalClientConfig; use super::tls::{ClientTls, EXPORTER_LABEL, quic_protocol_version}; const QUIC_DATAGRAM_BUFFER_SIZE: usize = 4 * 1024 * 1024; -const TLS_MUX_FLOWS_PER_SHARD: usize = 4; const TLS_MUX_MAX_SHARDS_PER_DIRECTION: usize = 4; #[derive(Clone)] diff --git a/src/vector/session/tls.rs b/src/vector/session/tls.rs index 728d9bd..d0d8565 100644 --- a/src/vector/session/tls.rs +++ b/src/vector/session/tls.rs @@ -34,6 +34,7 @@ pub(in crate::vector) enum OpenedTls { struct TlsMux { handle: MuxHandle, version: ProtocolVersion, + target_density: usize, } impl TlsManager { @@ -87,6 +88,7 @@ impl TlsManager { .map(|stream| OpenedTls::Mux(stream, shard.version)) .map_err(Into::into); } + let connect_started = Instant::now(); let lane = self.connect_lane().await?; let TlsLane { mut stream, @@ -106,7 +108,11 @@ impl TlsManager { stream.flush().await?; let (handle, incoming) = MuxHandle::start(stream, MuxConfig::default())?; drop(incoming); - let shard = TlsMux { handle, version }; + let shard = TlsMux { + handle, + version, + target_density: mux_target_density(connect_started.elapsed()), + }; self.mux(direction).lock().await.push(shard.clone()); let manager = self.clone(); let lifetime = shard.clone(); @@ -215,13 +221,24 @@ fn select_available_mux(muxes: &[TlsMux]) -> Option { if !available.handle.has_stream_capacity() { return None; } - if active < TLS_MUX_FLOWS_PER_SHARD || muxes.len() >= TLS_MUX_MAX_SHARDS_PER_DIRECTION { + if (active < available.target_density && !available.handle.is_send_congested()) + || muxes.len() >= TLS_MUX_MAX_SHARDS_PER_DIRECTION + { Some(available) } else { None } } +fn mux_target_density(setup_latency: Duration) -> usize { + match setup_latency.as_millis() { + 0..30 => 16, + 30..75 => 8, + 75..200 => 4, + _ => 2, + } +} + #[cfg(test)] #[path = "../../tests/vector/session/tls.rs"] mod tests; From cc3457a312b8d7fe418e88cefbf4e185d761e541 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Tue, 8 Sep 2026 11:34:55 +0800 Subject: [PATCH 24/62] Make nw2 the sole wire protocol --- docs/README.md | 4 +- docs/compatibility.md | 133 +++------------- docs/configuration.md | 7 +- docs/integrations.md | 6 +- docs/mux-performance.md | 41 +++-- docs/protocol.md | 53 +++---- docs/quick-start.md | 5 +- docs/security.md | 2 +- src/common/tls.rs | 4 +- src/mux/driver.rs | 200 ++++++++++++------------ src/mux/handle.rs | 3 +- src/mux/mod.rs | 27 +++- src/mux/stream.rs | 16 +- src/mux/wire.rs | 94 ++++++----- src/portal/conn.rs | 78 ++++----- src/portal/conn/auth.rs | 5 +- src/portal/conn/relay_tcp.rs | 1 - src/portal/conn/relay_uot.rs | 1 - src/portal/conn/session.rs | 1 - src/portal/conn/tcp/flow.rs | 1 - src/portal/conn/tcp/mod.rs | 34 ++-- src/portal/pairing.rs | 6 - src/portal/pairing/state.rs | 21 +-- src/protocol/auth.rs | 2 +- src/protocol/mod.rs | 4 +- src/protocol/version.rs | 52 ------ src/telemetry/hub.rs | 8 - src/telemetry/wire.rs | 5 +- src/tests/common/tls.rs | 4 +- src/tests/mux/runtime.rs | 77 +++++++++ src/tests/mux/wire.rs | 50 ++++-- src/tests/portal/conn/relay.rs | 2 - src/tests/portal/conn/support.rs | 18 ++- src/tests/portal/conn/tcp.rs | 36 ++++- src/tests/portal/pairing.rs | 5 +- src/tests/portal/pairing/rejection.rs | 4 +- src/tests/portal/pairing/replacement.rs | 2 +- src/tests/portal/pairing/version.rs | 124 --------------- src/tests/protocol/auth.rs | 6 +- src/tests/protocol/version.rs | 16 -- src/tests/telemetry/hub.rs | 35 ----- src/tests/tui/client.rs | 2 - src/tests/vector/session/tls.rs | 1 - src/tests/vector/tls.rs | 66 +++++--- src/tui/client/adapter.rs | 4 - src/tui/model/filter.rs | 4 - src/tui/model/types.rs | 1 - src/tui/render/feed.rs | 12 -- src/vector/flow.rs | 20 +-- src/vector/flow/tcp.rs | 10 -- src/vector/session.rs | 10 +- src/vector/session/quic.rs | 4 +- src/vector/session/tls.rs | 12 +- src/vector/socks/server.rs | 3 +- src/vector/socks/server/udp.rs | 4 +- src/vector/tls.rs | 24 ++- src/vector/udp_flow.rs | 10 +- 57 files changed, 569 insertions(+), 811 deletions(-) delete mode 100644 src/protocol/version.rs delete mode 100644 src/tests/portal/pairing/version.rs delete mode 100644 src/tests/protocol/version.rs diff --git a/docs/README.md b/docs/README.md index 7681a04..7c34fa1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -90,8 +90,8 @@ before service traffic is accepted. The full syntax and error rules are in | `mux=0` | Dedicated lane per flow | Native streams/datagrams | One flow per carrier | | `mux=1` | Shared bounded Mux | Native streams/datagrams | Assigned flows close with the carrier | -V2 peers negotiate the fixed `nw2` ALPN and fall back to the default V1 value -`now/1` for compatibility. The client-side route-policy matrix is: +Nowhere 2 peers negotiate the fixed `nw2` ALPN. Peers without `nw2` cannot +complete a Nowhere 2 carrier handshake. The client-side route-policy matrix is: | `up` ↓ / `down` → | `tcp` | `udp` | `mix` | |---|---|---|---| diff --git a/docs/compatibility.md b/docs/compatibility.md index 0960687..a001c85 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -1,21 +1,18 @@ # Interoperability -## Version negotiation +## Nowhere 2 wire boundary -Nowhere 2 uses ALPN as its data-plane version selector. Portal and Vector offer -the fixed protocols `nw2` and `now/1`, in that order. A negotiated `nw2` carrier -is V2; a negotiated `now/1` carrier is compatible V1. The `alpn` URL parameter -is ignored as an unknown parameter. +Nowhere 2 supports one data-plane protocol. TLS/TCP and QUIC clients must offer +the ALPN `nw2`, and the negotiated ALPN must be exactly `nw2`. A peer that +offers only `now/1`, a custom ALPN, or no ALPN cannot establish a Nowhere 2 +carrier. -| Client | Portal | Negotiated version | -|---|---|---| -| V2 | V2 | `nw2` / V2 | -| V2 | default V1 | `now/1` / V1 | -| default V1 | V2 | `now/1` / V1 | +Nowhere 1 wire compatibility is intentionally absent. This includes the +authentication key derivation and TLS Mux framing. Portal and Vector must be +upgraded together. -V1 installations using a custom ALPN cannot interoperate with V2. A V1 client -that had already customized its ALPN to `nw2` is classified as V2; this rare -collision has no compatibility exception. +The `alpn` URL parameter remains an ignored unknown parameter. It cannot change +the fixed wire protocol. ## Endpoint configuration @@ -23,109 +20,29 @@ The compact `portal://key@host:port`, `vector://key@host:port`, and `next=key@host:port` forms declare TCP and UDP on the same port. The empty Portal host in `portal://key@:port` is an alias for the wildcard host `*`. -Carrier paths select the enabled carriers. The `net` query follows the -unknown-parameter rule and has no effect on carrier selection: +Explicit carrier paths select listeners and dial targets: ```text portal://key@*/tcp:2006 portal://key@*/udp:2017 +portal://key@*/tcp4:2006/udp6:2017 ``` -Use `/tcp4:PORT`, `/tcp6:PORT`, `/udp4:PORT`, and `/udp6:PORT` when the address -family must also be fixed. URL compatibility is separate from wire -compatibility: default V1 and V2 peers communicate using the version negotiation -described above. Compatible compact address syntax does not imply identical -query semantics across versions. - -The endpoint path is local process configuration and never appears on the wire. -It determines which TCP or UDP socket carries the TLS/QUIC connection before -ALPN and AuthFrame processing begin. - -| Configuration | V2 behavior | V1 interoperability consequence | -|---|---|---| -| `HOST:PORT` | Both carriers use one unrestricted port | A default V1 client can reach both carriers | -| `HOST/tcp:2006` | Only TLS/TCP exists | A peer must select TCP and use port 2006 | -| `HOST/udp:2017` | Only QUIC/UDP exists | A peer must select UDP and use port 2017 | -| `HOST/tcp:2006/udp:2017` | Carriers use independent ports | A client must understand or otherwise know both ports | -| `HOST/tcp4:2006/udp6:2017` | Carrier DNS/address results are family-filtered | Peer reachability must satisfy the same families | - -A V1 client URL has one authority port and cannot describe separate TCP and UDP -ports. A V2 Portal intended to serve unmodified V1 clients therefore uses the -compact form on that compatibility edge. A V2 Vector may use an explicit -endpoint with a V1 Portal when each declared address and port matches a listener -that the V1 Portal actually exposes. - -`net` has no meaning to the V2 endpoint parser. For example, -`portal://key@*:2000?net=tcp` still declares both carriers because the compact -endpoint declares both. A TCP-only V2 service uses -`portal://key@*/tcp:2006`. This query difference does not change ALPN or the -compatible V1 wire encoding. +The `net` query follows the unknown-parameter rule and has no effect. Endpoint +syntax is local configuration and is not transmitted on the wire. ## TLS lane contract -Vector `mux=0` opens one authenticated TLS connection per Flow. Vector `mux=1` -opens marked Mux connections and assigns logical streams to dynamic Shards. - -Portal accepts both forms on one listener. After the 32-byte authentication -frame: - -- `0xff` identifies a Mux connection; -- every other byte is the first byte of a dedicated FlowHeader. - -The marker cannot collide with a valid FlowHeader. Dedicated and marked Mux -connections use the same listener without separate inbound configuration. -An authenticated dedicated connection has 40 seconds to provide its first -FlowHeader byte. - -```text - first byte after AuthFrame - | - +-----------------+-----------------+ - | | - 0xff any other byte - | | - v v - +--------------------+ +--------------------+ - | Mux frame decoder | | FlowHeader decoder | - | shared TLS carrier | | dedicated TLS lane | - +--------------------+ +--------------------+ -``` - -Portal dispatches every authenticated TLS connection by its framing: - -| Bytes after AuthFrame | Selected form | Result | -|---|---|---| -| Valid FlowHeader | Dedicated TLS | accepted | -| `0xff`, then valid Mux frames | Marked Mux TLS | accepted | -| Unmarked Mux bytes | Invalid FlowHeader | rejected | - -The `0xff` byte is the Mux mode marker. It is always present on a Mux carrier -and never appears on a dedicated lane. - -## Runtime contract - -Mux Shards open lazily using TLS setup latency and carrier pressure, up to a -4-Shard directional pool. They select the least-loaded live Shard and close -after 30 seconds fully idle. -Dedicated lanes and Mux streams use the -same authentication, FlowHeader, Target, setup result, pairing and limits. -QUIC behavior is independent from the client Mux setting. - -Peers must also use matching credentials and reachable carrier families. A -Portal with `next=` negotiates the upstream version independently and applies -the same `tcp|udp|mix` policy as Vector for that hop. Mix resolves locally -before transmission, and the peer receives a standard TT, TQ, QT, or QQ -FlowHeader. Portal compatibility is independent of whether the client policy -is fixed or mixed. -The upstream Mux selection defaults to `0`, is ignored without an enabled -`next`, and canonicalizes to `0` for a fixed `udp/udp` route. +Vector `mux=0` opens one authenticated TLS connection per flow. Vector `mux=1` +opens marked Mux carriers and assigns logical streams to adaptive shards. +Portal accepts both forms on one TLS listener. After the 32-byte AuthFrame, +`0xff` selects Mux framing; every other valid first byte begins a dedicated +FlowHeader. -Each hop has its own endpoint constraints. A V2 Portal may accept an inbound V1 -TLS client on a compact listener and use an IPv6-only QUIC endpoint for its V2 -`next` hop. The incoming ALPN, local listener family, upstream ALPN, and upstream -address family are evaluated independently. +Mux carriers open lazily, choose the least-loaded live shard, and use at most +four physical TLS carriers per logical direction. A symmetric TCP/TCP flow uses +one full-duplex logical stream. A carrier closes after it has remained fully +idle for 30 seconds. -Interoperability tests exercise both peer roles: one endpoint as Portal and the -other as client. The complete 3×3 `up`/`down` policy matrix covers all four -concrete routes and all five policies containing `mix`, together with both -negotiated versions, dedicated TLS, and marked Mux. +Dedicated lanes, Mux streams, and QUIC streams use the same FlowHeader, Target, +setup result, pairing, and resource limits. diff --git a/docs/configuration.md b/docs/configuration.md index f08f03b..253945e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -231,10 +231,9 @@ Flow owns one on-demand lane that closes with the Flow. Mux applies when at least one direction is `tcp` or `mix`. `udp/udp&mux=1` canonicalizes to `mux=0`. -Portal and Vector offer fixed ALPNs in the order `nw2`, `now/1`. V2 peers select -`nw2`; a V2 peer talking to a default V1 peer selects `now/1`. The -`alpn` query is ignored under the normal unknown-parameter rule. Protocol -version and Mux are independent settings. Portal's `mux` option controls only +Portal and Vector use only the fixed ALPN `nw2`. A peer that does not offer +`nw2` cannot establish a carrier. The `alpn` query is ignored under the +normal unknown-parameter rule. Portal's `mux` option controls only its `next` client. Inbound Portal connections accept a `0xff`-marked Mux carrier or an unmarked dedicated lane on the same listener. diff --git a/docs/integrations.md b/docs/integrations.md index b9112aa..b6e2621 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -64,9 +64,9 @@ family, because doing so would change the user's declared service edge. ## Alternate clients Implementers should follow [Protocol](protocol.md). QUIC uses native reliable -streams and DATAGRAM frames, never TLS Mux framing. V2 clients offer `nw2` and -the compatible V1 value `now/1`; the negotiated value selects the version for -that carrier. A Mux TLS connection places the `0xff` marker after +streams and DATAGRAM frames, never TLS Mux framing. Clients must offer `nw2`, +and the negotiated ALPN must be exactly `nw2`. A Mux TLS connection places +the `0xff` marker after authentication; a dedicated lane places its FlowHeader there instead. Portal accepts both forms on the same TLS listener and selects the decoder from that first byte. diff --git a/docs/mux-performance.md b/docs/mux-performance.md index 036bdf7..1bdc945 100644 --- a/docs/mux-performance.md +++ b/docs/mux-performance.md @@ -1,19 +1,32 @@ # TLS Mux performance baseline -The local benchmark runs a real Vector, Toxiproxy-delayed TCP/TLS carrier, -Portal, and TCP target. It synchronizes the application transfers, reports -payload throughput, and samples peak RSS for both Nowhere processes every -100 ms. +The auxiliary macOS benchmark runs Vector, Toxiproxy, Portal, and a TCP target. +Toxiproxy terminates and re-originates TCP, so its latency results are useful +for quick development comparisons but are not packet-level TCP network +emulation. ```sh cargo build --release --locked scripts/bench-mux-local.sh 100 1 64 1 ``` -Arguments are RTT in milliseconds, concurrent flows, MiB per flow, `mux=0|1`, -and an optional binary path. Run samples serially so their fixed local ports do -not overlap. Apple Container's default kernel does not provide the netem qdisc, -so the local runner uses bidirectional Toxiproxy latency on the real carrier. +Hard throughput and RSS comparisons use `tests/bench-mux-netem.sh` on a Linux +host with network namespaces and the `netem` qdisc. The harness delays only the +physical carrier veth and samples RSS every 10 ms. + +```sh +sudo tests/bench-mux-matrix.py \ + --baseline /path/to/baseline/nowhere \ + --current target/release/nowhere \ + --output mux-matrix.json +``` + +The fixed matrix is RTT 0/30/100/200/300 ms by 1/4/16 flows, with three serial +samples per cell and median comparison. One-flow payloads are 64 MiB except +32 MiB at 300 ms; four flows total 64 MiB and sixteen flows total 128 MiB. +Throughput may not regress by more than 3%. Per-process peak RSS may not rise by +more than the larger of 5% or 2 MiB. The runner also records non-gating 0.1% +loss observations at 100 ms/1 flow and 300 ms/16 flows. ## 2026-09-07 development baseline @@ -46,8 +59,9 @@ increasing the profile budgets. ## 2026-09-08 adaptive pass -This pass retains the minimal 8-byte STREAM/WINDOW wire format and the 1/8-window -credit-return threshold. A 1/16 threshold did not improve throughput and would +These historical results used the former 8-byte STREAM/WINDOW development +format and the 1/8-window credit-return threshold. A 1/16 threshold did not +improve throughput and would send more control frames. Generic TCP relay reads now transfer a pooled buffer lease into `Bytes`, so the allocation returns to the existing transport pool when the last payload owner drops. Mux-to-relay reads continue to hand off their @@ -82,3 +96,10 @@ completed. The 100 and 300 ms sustained samples were run from fresh proxy containers; the benchmark now records carrier topology alongside throughput and RSS so future comparisons can distinguish pool-size changes from data-path changes. + +## Current Nowhere 2 frame boundary + +The current Mux header remains 8 bytes but uses separate OPEN, DATA, WINDOW, +and CLOSE kinds. It is wire-incompatible with every earlier development Mux +format. Historical results above remain useful as performance baselines; they +do not demonstrate current wire interoperability. diff --git a/docs/protocol.md b/docs/protocol.md index e5ca9bd..fc3dbc0 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -21,10 +21,9 @@ otherwise. ## 1. Carrier model -TLS/TCP and QUIC use TLS 1.3 and negotiate a data-plane version through ALPN. -Nowhere 2 offers `nw2` first and the default V1 value `now/1` second. A selected -`nw2` carrier is V2; every accepted non-`nw2` carrier is V1, which in this -implementation means exactly `now/1`. +TLS/TCP and QUIC use TLS 1.3 with the sole ALPN `nw2`. A client that does not +offer `nw2`, or a handshake that does not select exactly `nw2`, is rejected +before Nowhere authentication. Nowhere 2 does not implement the V1 wire format. ### Command endpoint mapping @@ -172,7 +171,7 @@ The shared key is 1–255 decoded bytes and is never transmitted. Authentication uses these fixed derivations: ```text -salt = SHA256("nowhere/now/1/auth-root") +salt = SHA256("nowhere/nw2/auth-root") auth_root = HMAC-SHA256(salt, shared_key) auth_key = HMAC-SHA256(auth_root, "authentication" || 0x01) @@ -184,9 +183,7 @@ tag = first 16 bytes of transport || exporter[32] || session_id[16]) ``` -The 32-byte exporter uses label `EXPORTER-Nowhere-Auth` and empty context. The -fixed derivation labels are shared by the compatible V1 and V2 paths in this -release; the negotiated version does not change authentication bytes. +The 32-byte exporter uses label `EXPORTER-Nowhere-Auth` and empty context. Authentication is bound to the current TLS connection; replaying a captured AuthFrame on another connection fails. @@ -213,7 +210,7 @@ Mux frame. ### MuxHeader -Every Mux frame starts with an 8-byte header. A DATA STREAM frame carries +Every Mux frame starts with an 8-byte header. A DATA frame carries exactly `value` payload bytes; control frames carry no payload. ```text @@ -221,46 +218,38 @@ MuxHeader - 8 bytes offset 0 1 2 4 8 +--------+--------+---------------+-----------------------+ - | kind | flags | value | flow_id | + | kind | code | value | flow_id | | u8 | u8 | u16 | u32 | +--------+--------+---------------+-----------------------+ ``` | `kind` | Name | `value` | `flow_id` | |---:|---|---|---| -| `0x01` | STREAM | DATA length, or initial window extension with SYN | nonzero | -| `0x02` | WINDOW | returned credit in 1 KiB units | `0` for connection, nonzero for stream | +| `0x01` | OPEN | opener receive-window extension in 1 KiB units | nonzero | +| `0x02` | DATA | payload length, 1..65535 | nonzero | +| `0x03` | WINDOW | returned credit in 1 KiB units | `0` for connection, nonzero for stream | +| `0x04` | CLOSE | always `0` | nonzero | -For STREAM, the low three flag bits are: +OPEN, DATA, and WINDOW require `code=0`. OPEN carries no payload and extends +the opener's 4 MiB initial stream receive window. The runtime emits DATA +payloads of at most 32 KiB. -```text -flags byte - - bit 7 3 2 1 0 - +---------------------+-----+-----+-----+ - | reserved | RST | FIN | SYN | - +---------------------+-----+-----+-----+ -``` - -- `SYN=0x01` creates the logical stream. It is isolated, carries no payload, - and `value` extends the peer's 4 MiB initial stream window in 1 KiB units. -- `FIN=0x02` half-closes the sender after optional payload is delivered. -- `RST=0x04` resets the stream. It MUST be the only flag and `value` MUST be 0. -- All other flag bits MUST be zero. +CLOSE carries no payload. `code=0` is FIN half-close and `code=1` is RESET. +Other codes are invalid. -WINDOW uses `flags=0`, carries no payload, and requires nonzero credit in 1 KiB +WINDOW carries no payload and requires nonzero credit in 1 KiB units. A WINDOW with `flow_id=0` replenishes connection credit; a nonzero ID replenishes that logical stream. Credit that would exceed the configured window closes the carrier. A late stream-local WINDOW for an already closed stream is ignored. -STREAM data for an unknown flow is a carrier error. Late FIN or RST processing +DATA for an unknown flow is a carrier error. Late or duplicate CLOSE processing is idempotent. Closing the physical Mux carrier fails every logical stream on that carrier. -The runtime emits at most 32 KiB of data per STREAM frame. Mux uses an initial -4 MiB stream window and 8 MiB connection window. Each side sends one WINDOW to -extend its connection window and includes its stream-window extension in SYN. +Mux uses an initial 4 MiB stream window and 8 MiB connection window. Each side +sends one WINDOW to extend its connection window. OPEN advertises the opener's +stream extension; the receiver returns its stream extension with WINDOW. The selected transport profile sets final windows to 4/8, 8/16, or 16/32 MiB. Each shard permits 256 active streams and 512 queued outbound frame slots. Payload must obtain both stream and connection credit before it enters the outbound queue. diff --git a/docs/quick-start.md b/docs/quick-start.md index 2e68710..1ef1157 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -124,9 +124,8 @@ automatically: nowhere 'vector://secret@127.0.0.1:2000?up=tcp&down=tcp&mux=1&socks=127.0.0.1:1080' ``` -Nowhere 2 negotiates `nw2` with another V2 peer and falls back to `now/1` when -connecting to a default V1 peer. There is no configurable ALPN parameter; Mux -selection is independent from protocol version. +Nowhere 2 requires the fixed `nw2` ALPN at both ends and does not interoperate +with the V1 wire protocol. There is no configurable ALPN parameter. `udp/udp&mux=1` is canonicalized to `mux=0` because no TLS lane can use it. ## 3. Use SOCKS5 diff --git a/docs/security.md b/docs/security.md index 3566a67..57d9119 100644 --- a/docs/security.md +++ b/docs/security.md @@ -66,7 +66,7 @@ A receiver charges both windows before delivery and returns credit only after application consumption. Closing the carrier releases queued payload. The fixed maximum frame payload is 65,535 bytes and the runtime emits at most -32 KiB per STREAM frame. Malformed kinds, flags, IDs, lengths, window overflow, +32 KiB per DATA frame. Malformed kinds, codes, IDs, lengths, window overflow, and DATA for unknown streams close the carrier. Late terminal and credit frames for a terminal stream are idempotent. diff --git a/src/common/tls.rs b/src/common/tls.rs index b39889e..9a1f059 100644 --- a/src/common/tls.rs +++ b/src/common/tls.rs @@ -15,7 +15,7 @@ use quinn::crypto::rustls::QuicServerConfig; use rustls::crypto::ring; use url::Url; -use crate::protocol::SUPPORTED_ALPNS; +use crate::protocol::ALPN; pub(crate) use self::tls_cert::certificate_sha256; use self::tls_cert::{ReloadingCertResolver, new_self_signed_cert}; @@ -109,7 +109,7 @@ pub(crate) fn new_server_configs_with_reload_interval( server_crypto.max_early_data_size = 0; server_crypto.send_half_rtt_data = false; - server_crypto.alpn_protocols = SUPPORTED_ALPNS.iter().map(|alpn| alpn.to_vec()).collect(); + server_crypto.alpn_protocols = vec![ALPN.to_vec()]; let quic_crypto = QuicServerConfig::try_from(server_crypto.clone()) .map_err(|e| anyhow!("common::tls::new_server_configs: QUIC TLS config failed: {e}"))?; logger.event(format_args!("CERT_SHA256|{cert_sha256}")); diff --git a/src/mux/driver.rs b/src/mux/driver.rs index a32f5d9..d36fb23 100644 --- a/src/mux/driver.rs +++ b/src/mux/driver.rs @@ -1,18 +1,17 @@ // Copyright (C) 2026 NodePassProject // SPDX-License-Identifier: GPL-3.0-only -use std::io; -use std::io::IoSlice; +use std::io::{self, IoSlice}; use std::sync::Arc; -use super::wire::{ - FLAG_FIN, FLAG_RST, FLAG_SYN, FlowId, FrameHeader, FrameKind, HEADER_LEN, decode_header, - encode_header, -}; use bytes::Bytes; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio::sync::mpsc; +use super::wire::{ + CLOSE_FIN, CLOSE_RESET, FlowId, FrameHeader, FrameKind, HEADER_LEN, decode_header, + encode_header, +}; use super::{Inbound, MuxChunk, Outbound, Shared}; pub(super) async fn send_data( @@ -34,10 +33,9 @@ pub(super) async fn send_data( .map_err(|_| closed())?; shared .data_tx - .send(Outbound { - header: frame_stream(flow_id, 0, payload.len())?, + .send(Outbound::Frame { + header: frame_data(flow_id, payload.len())?, payload, - flushed: None, }) .await .map_err(|_| closed())?; @@ -60,19 +58,21 @@ pub(super) async fn run_reader(mut reader: R, shared: Arc< } let header = decode_header(&encoded).map_err(invalid)?; let payload_len = match header.kind { - FrameKind::Stream if header.flags & FLAG_SYN == 0 => header.value as usize, - FrameKind::Stream | FrameKind::Window => 0, + FrameKind::Data => header.value as usize, + FrameKind::Open | FrameKind::Window | FrameKind::Close => 0, }; let mut payload = vec![0; payload_len]; - if !payload.is_empty() { + if payload_len != 0 { tokio::select! { _ = shared.closed_notify.notified() => return Ok(()), result = reader.read_exact(&mut payload) => { result?; } } } match header.kind { - FrameKind::Stream => receive_stream(&shared, header, Bytes::from(payload)).await?, + FrameKind::Open => receive_open(&shared, header).await?, + FrameKind::Data => receive_data(&shared, header, Bytes::from(payload)).await?, FrameKind::Window => receive_window(&shared, header)?, + FrameKind::Close => receive_close(&shared, header).await, } if payload_len != 0 { data_frames = data_frames.wrapping_add(1); @@ -89,60 +89,52 @@ pub(super) async fn run_reader(mut reader: R, shared: Arc< } } -async fn receive_stream( - shared: &Arc, - header: FrameHeader, - payload: Bytes, -) -> io::Result<()> { - if header.flags & FLAG_SYN != 0 { - let terminal_permit = shared.reserve_terminal().await?; - let stream = shared.insert_flow(header.flow_id, terminal_permit, true)?; - let extra_credit = header.value as usize; - if extra_credit != 0 { - let credit = shared.send_credit(header.flow_id)?; - if credit.available_permits().saturating_add(extra_credit) - > super::credit_units(super::MAX_STREAM_WINDOW_BYTES) - { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "stream window overflow", - )); - } - credit.add_permits(extra_credit); +async fn receive_open(shared: &Arc, header: FrameHeader) -> io::Result<()> { + let terminal_permit = shared.reserve_terminal().await?; + let stream = shared.insert_flow(header.flow_id, terminal_permit, true)?; + let extra_credit = header.value as usize; + if extra_credit != 0 { + let credit = shared.send_credit(header.flow_id)?; + if credit.available_permits().saturating_add(extra_credit) + > super::credit_units(super::MAX_STREAM_WINDOW_BYTES) + { + return Err(invalid("stream window overflow")); } - shared - .incoming_tx - .send(stream) - .await - .map_err(|_| closed())?; + credit.add_permits(extra_credit); } - if header.flags & FLAG_RST != 0 { - let flow = shared.remove_flow(header.flow_id); - if let Some(flow) = flow { + shared.incoming_tx.send(stream).await.map_err(|_| closed()) +} + +async fn receive_data(shared: &Arc, header: FrameHeader, payload: Bytes) -> io::Result<()> { + let charge = frame_charge(payload.len()); + let inbound = shared.admit_receive(header.flow_id, charge)?; + inbound + .send(Inbound::Data { payload, charge }) + .await + .map_err(|_| closed()) +} + +async fn receive_close(shared: &Shared, header: FrameHeader) { + if header.code == CLOSE_RESET { + if let Some(flow) = shared.remove_flow(header.flow_id) { let _ = flow.inbound.send(Inbound::Reset).await; } - return Ok(()); - } - if !payload.is_empty() { - let charge = frame_charge(payload.len()); - let inbound = shared.admit_receive(header.flow_id, charge)?; - inbound - .send(Inbound::Data { payload, charge }) - .await - .map_err(|_| closed())?; + return; } - if header.flags & FLAG_FIN != 0 { - let inbound = shared - .flows - .lock() - .expect("mux flow lock") - .get(&header.flow_id) - .map(|flow| flow.inbound.clone()); - if let Some(inbound) = inbound { - let _ = inbound.send(Inbound::Fin).await; - } + let inbound = { + let mut flows = shared.flows.lock().expect("mux flow lock"); + flows.get_mut(&header.flow_id).and_then(|flow| { + if flow.remote_fin { + None + } else { + flow.remote_fin = true; + Some(flow.inbound.clone()) + } + }) + }; + if let Some(inbound) = inbound { + let _ = inbound.send(Inbound::Fin).await; } - Ok(()) } fn receive_window(shared: &Shared, header: FrameHeader) -> io::Result<()> { @@ -154,10 +146,7 @@ fn receive_window(shared: &Shared, header: FrameHeader) -> io::Result<()> { .saturating_add(credit) > super::credit_units(super::MAX_CONNECTION_WINDOW_BYTES) { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "connection window overflow", - )); + return Err(invalid("connection window overflow")); } shared.connection_send_credit.add_permits(credit); shared.connection_send_peak.fetch_max( @@ -168,18 +157,12 @@ fn receive_window(shared: &Shared, header: FrameHeader) -> io::Result<()> { } let mut flows = shared.flows.lock().expect("mux flow lock"); let Some(flow) = flows.get_mut(&header.flow_id) else { - // Flow-close frames and their final credit updates can cross on the - // full-duplex carrier. A late stream-local WINDOW has no authority to - // change connection credit and is safe to ignore. return Ok(()); }; if flow.send_credit.available_permits().saturating_add(credit) > super::credit_units(super::MAX_STREAM_WINDOW_BYTES) { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "stream window overflow", - )); + return Err(invalid("stream window overflow")); } flow.send_credit.add_permits(credit); Ok(()) @@ -195,15 +178,14 @@ pub(super) async fn run_terminals(shared: Arc, mut terminal_rx: mpsc::Re flow_id = terminal_rx.recv() => flow_id, }; let Some(flow_id) = flow_id else { return }; - let Ok(header) = frame_stream(flow_id, FLAG_FIN, 0) else { + let Ok(header) = frame_close(flow_id, CLOSE_FIN) else { continue; }; let sent = tokio::select! { _ = shared.closed_notify.notified() => return, - sent = shared.data_tx.send(Outbound { + sent = shared.data_tx.send(Outbound::Frame { header, payload: MuxChunk::empty(), - flushed: None, }) => sent, }; if sent.is_err() { @@ -239,31 +221,38 @@ pub(super) async fn run_writer( } }; let Some(item) = item else { return Ok(()) }; - if item.flushed.is_some() && item.payload.is_empty() && item.header.flags == 0 { - let result = writer.flush().await; - if let Some(done) = item.flushed { + match item { + Outbound::Flush(done) => { + let result = writer.flush().await; + let failed = result.is_err(); let _ = done.send(result); + if failed { + return Err(closed()); + } } - continue; - } - if item.flushed.is_none() && item.payload.is_empty() { - headers.clear(); - headers.extend_from_slice(&encode_header(item.header).map_err(invalid)?); - while headers.len() < 8 * 256 { - let Ok(next) = data_rx.try_recv() else { break }; - if next.flushed.is_none() && next.payload.is_empty() { - headers.extend_from_slice(&encode_header(next.header).map_err(invalid)?); - } else { - pending_item = Some(next); - break; + Outbound::Frame { header, payload } if payload.is_empty() => { + headers.clear(); + headers.extend_from_slice(&encode_header(header).map_err(invalid)?); + while headers.len() < 8 * 256 { + let Ok(next) = data_rx.try_recv() else { break }; + match next { + Outbound::Frame { header, payload } if payload.is_empty() => { + headers.extend_from_slice(&encode_header(header).map_err(invalid)?); + } + next => { + pending_item = Some(next); + break; + } + } } + writer.write_all(&headers).await?; + writer.flush().await?; + } + Outbound::Frame { header, payload } => { + let header = encode_header(header).map_err(invalid)?; + write_frame_vectored(&mut writer, &header, payload.as_ref()).await?; } - writer.write_all(&headers).await?; - writer.flush().await?; - continue; } - let header = encode_header(item.header).map_err(invalid)?; - write_frame_vectored(&mut writer, &header, item.payload.as_ref()).await?; } } .await; @@ -281,11 +270,12 @@ async fn write_frame_vectored( let mut payload_offset = 0; while header_offset != header.len() || payload_offset != payload.len() { let written = if header_offset != header.len() { - let buffers = [ - IoSlice::new(&header[header_offset..]), - IoSlice::new(&payload[payload_offset..]), - ]; - writer.write_vectored(&buffers).await? + writer + .write_vectored(&[ + IoSlice::new(&header[header_offset..]), + IoSlice::new(&payload[payload_offset..]), + ]) + .await? } else { writer.write(&payload[payload_offset..]).await? }; @@ -360,11 +350,15 @@ fn frame_charge(payload: usize) -> usize { pub(super) fn frame_open(flow_id: FlowId, receive_window_bytes: usize) -> io::Result { let extra = receive_window_bytes.saturating_sub(super::BASE_STREAM_WINDOW_BYTES); - FrameHeader::stream(flow_id, FLAG_SYN, super::credit_units(extra)).map_err(invalid) + FrameHeader::open(flow_id, super::credit_units(extra)).map_err(invalid) +} + +pub(super) fn frame_data(flow_id: FlowId, length: usize) -> io::Result { + FrameHeader::data(flow_id, length).map_err(invalid) } -pub(super) fn frame_stream(flow_id: FlowId, flags: u8, length: usize) -> io::Result { - FrameHeader::stream(flow_id, flags, length).map_err(invalid) +pub(super) fn frame_close(flow_id: FlowId, code: u8) -> io::Result { + FrameHeader::close(flow_id, code).map_err(invalid) } fn invalid(error: impl std::fmt::Display) -> io::Error { diff --git a/src/mux/handle.rs b/src/mux/handle.rs index 6147c13..1d21cc4 100644 --- a/src/mux/handle.rs +++ b/src/mux/handle.rs @@ -73,10 +73,9 @@ impl MuxHandle { let stream = self.shared.insert_flow(flow_id, terminal_permit, false)?; self.shared .data_tx - .send(Outbound { + .send(Outbound::Frame { header: frame_open(flow_id, self.shared.config.stream_window_bytes)?, payload: super::MuxChunk::empty(), - flushed: None, }) .await .map_err(|_| closed())?; diff --git a/src/mux/mod.rs b/src/mux/mod.rs index c7316d7..d24dd5f 100644 --- a/src/mux/mod.rs +++ b/src/mux/mod.rs @@ -150,6 +150,7 @@ struct FlowState { pending_receive_credit: usize, window_queued: bool, local_parts: u8, + remote_fin: bool, } enum Inbound { @@ -158,10 +159,12 @@ enum Inbound { Reset, } -struct Outbound { - header: FrameHeader, - payload: MuxChunk, - flushed: Option>>, +enum Outbound { + Frame { + header: FrameHeader, + payload: MuxChunk, + }, + Flush(oneshot::Sender>), } impl MuxChunk { @@ -258,6 +261,7 @@ impl Shared { pending_receive_credit: initial_credit, window_queued: advertise_window && initial_credit != 0, local_parts: 2, + remote_fin: false, }, ); let active_streams = flows.len(); @@ -294,6 +298,7 @@ impl Shared { return; } self.flows.lock().expect("mux flow lock").clear(); + self.connection_send_credit.close(); self.active_streams_tx.send_replace(0); self.closed_notify.notify_waiters(); } @@ -325,6 +330,12 @@ impl Shared { let flow = flows.get_mut(&flow_id).ok_or_else(|| { io::Error::new(io::ErrorKind::InvalidData, "frame for unknown mux flow") })?; + if flow.remote_fin { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "DATA received after mux FIN", + )); + } if flow.receive_credit < charge || *connection < charge { return Err(io::Error::new( io::ErrorKind::InvalidData, @@ -340,6 +351,9 @@ impl Shared { if self.closed.load(Ordering::Acquire) { return; } + let previous = self + .pending_connection_credit + .fetch_add(charge, Ordering::AcqRel); let (flow_ready, flow_notify) = { let mut connection = self .connection_receive_credit @@ -365,7 +379,7 @@ impl Shared { .min(u16::MAX as usize); (ready, flow.pending_receive_credit >= threshold) } else { - return; + (false, false) } }; if flow_ready { @@ -374,9 +388,6 @@ impl Shared { .expect("mux ready-flow lock") .push_back(flow_id); } - let previous = self - .pending_connection_credit - .fetch_add(charge, Ordering::AcqRel); let threshold = credit_units(self.config.connection_window_bytes / WINDOW_UPDATE_DIVISOR) .min(u16::MAX as usize); if flow_notify || previous.saturating_add(charge) >= threshold { diff --git a/src/mux/stream.rs b/src/mux/stream.rs index 52c91d8..8057868 100644 --- a/src/mux/stream.rs +++ b/src/mux/stream.rs @@ -6,12 +6,12 @@ use std::io::IoSlice; use std::pin::Pin; use std::task::{Context, Poll}; -use super::wire::FLAG_FIN; +use super::wire::CLOSE_FIN; use bytes::Bytes; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tokio::sync::oneshot; -use super::driver::{closed, frame_stream, send_data}; +use super::driver::{closed, frame_close, send_data}; use super::{FRAME_BYTES, FlowReader, FlowWriter, Inbound, MuxChunk, MuxStream, Outbound}; fn copy_payload(payload: &[u8]) -> Bytes { @@ -220,16 +220,11 @@ impl AsyncWrite for FlowWriter { } if self.pending_action.is_none() { let shared = self.shared.clone(); - let flow_id = self.flow_id; self.pending_action = Some(Box::pin(async move { let (tx, rx) = oneshot::channel(); shared .data_tx - .send(Outbound { - header: frame_stream(flow_id, 0, 0)?, - payload: MuxChunk::empty(), - flushed: Some(tx), - }) + .send(Outbound::Flush(tx)) .await .map_err(|_| closed())?; rx.await.map_err(|_| closed())? @@ -253,10 +248,9 @@ impl AsyncWrite for FlowWriter { self.pending_action = Some(Box::pin(async move { shared .data_tx - .send(Outbound { - header: frame_stream(flow_id, FLAG_FIN, 0)?, + .send(Outbound::Frame { + header: frame_close(flow_id, CLOSE_FIN)?, payload: MuxChunk::empty(), - flushed: None, }) .await .map_err(|_| closed()) diff --git a/src/mux/wire.rs b/src/mux/wire.rs index e204a31..b85b149 100644 --- a/src/mux/wire.rs +++ b/src/mux/wire.rs @@ -7,15 +7,16 @@ use std::fmt; pub(super) type FlowId = u32; pub(super) const HEADER_LEN: usize = 8; -pub(super) const FLAG_SYN: u8 = 0x01; -pub(super) const FLAG_FIN: u8 = 0x02; -pub(super) const FLAG_RST: u8 = 0x04; +pub(super) const CLOSE_FIN: u8 = 0; +pub(super) const CLOSE_RESET: u8 = 1; #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u8)] pub(super) enum FrameKind { - Stream = 0x01, - Window = 0x02, + Open = 0x01, + Data = 0x02, + Window = 0x03, + Close = 0x04, } impl TryFrom for FrameKind { @@ -23,8 +24,10 @@ impl TryFrom for FrameKind { fn try_from(value: u8) -> Result { match value { - 0x01 => Ok(Self::Stream), - 0x02 => Ok(Self::Window), + 0x01 => Ok(Self::Open), + 0x02 => Ok(Self::Data), + 0x03 => Ok(Self::Window), + 0x04 => Ok(Self::Close), _ => Err(WireError::UnknownKind(value)), } } @@ -33,25 +36,33 @@ impl TryFrom for FrameKind { #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(super) struct FrameHeader { pub kind: FrameKind, - pub flags: u8, + pub code: u8, pub value: u16, pub flow_id: FlowId, } impl FrameHeader { - pub fn stream(flow_id: FlowId, flags: u8, payload_len: usize) -> Result { - Self::new(FrameKind::Stream, flags, payload_len, flow_id) + pub fn open(flow_id: FlowId, window_extension: usize) -> Result { + Self::new(FrameKind::Open, 0, window_extension, flow_id) + } + + pub fn data(flow_id: FlowId, payload_len: usize) -> Result { + Self::new(FrameKind::Data, 0, payload_len, flow_id) } pub fn window(flow_id: FlowId, credit: usize) -> Result { Self::new(FrameKind::Window, 0, credit, flow_id) } - fn new(kind: FrameKind, flags: u8, value: usize, flow_id: FlowId) -> Result { + pub fn close(flow_id: FlowId, code: u8) -> Result { + Self::new(FrameKind::Close, code, 0, flow_id) + } + + fn new(kind: FrameKind, code: u8, value: usize, flow_id: FlowId) -> Result { let value = u16::try_from(value).map_err(|_| WireError::ValueTooLarge)?; let header = Self { kind, - flags, + code, value, flow_id, }; @@ -61,38 +72,53 @@ impl FrameHeader { pub fn validate(self) -> Result<(), WireError> { match self.kind { - FrameKind::Stream => { - if self.flow_id == 0 { - return Err(WireError::InvalidFlowId); + FrameKind::Open => { + require_flow(self.flow_id)?; + if self.code != 0 { + return Err(WireError::ReservedCode); } - if self.flags & !(FLAG_SYN | FLAG_FIN | FLAG_RST) != 0 { - return Err(WireError::ReservedFlags); - } - if self.flags & FLAG_RST != 0 && (self.flags != FLAG_RST || self.value != 0) { - return Err(WireError::InvalidReset); + } + FrameKind::Data => { + require_flow(self.flow_id)?; + if self.code != 0 { + return Err(WireError::ReservedCode); } - if self.flags & FLAG_SYN != 0 && self.flags != FLAG_SYN { - return Err(WireError::InvalidOpen); + if self.value == 0 { + return Err(WireError::InvalidData); } } FrameKind::Window => { - if self.flags != 0 { - return Err(WireError::ReservedFlags); + if self.code != 0 { + return Err(WireError::ReservedCode); } if self.value == 0 { return Err(WireError::InvalidWindow); } } + FrameKind::Close => { + require_flow(self.flow_id)?; + if self.code > CLOSE_RESET || self.value != 0 { + return Err(WireError::InvalidClose); + } + } } Ok(()) } } +fn require_flow(flow_id: FlowId) -> Result<(), WireError> { + if flow_id == 0 { + Err(WireError::InvalidFlowId) + } else { + Ok(()) + } +} + pub(super) fn encode_header(header: FrameHeader) -> Result<[u8; HEADER_LEN], WireError> { header.validate()?; let mut output = [0; HEADER_LEN]; output[0] = header.kind as u8; - output[1] = header.flags; + output[1] = header.code; output[2..4].copy_from_slice(&header.value.to_be_bytes()); output[4..8].copy_from_slice(&header.flow_id.to_be_bytes()); Ok(output) @@ -104,7 +130,7 @@ pub(super) fn decode_header(input: &[u8]) -> Result { } let header = FrameHeader { kind: FrameKind::try_from(input[0])?, - flags: input[1], + code: input[1], value: u16::from_be_bytes([input[2], input[3]]), flow_id: u32::from_be_bytes(input[4..8].try_into().expect("fixed flow ID")), }; @@ -117,11 +143,11 @@ pub(super) enum WireError { InvalidHeaderLength(usize), UnknownKind(u8), ValueTooLarge, - ReservedFlags, + ReservedCode, InvalidFlowId, + InvalidData, InvalidWindow, - InvalidReset, - InvalidOpen, + InvalidClose, } impl fmt::Display for WireError { @@ -132,13 +158,11 @@ impl fmt::Display for WireError { } Self::UnknownKind(kind) => write!(formatter, "unknown frame kind: {kind}"), Self::ValueTooLarge => formatter.write_str("frame value exceeds u16"), - Self::ReservedFlags => formatter.write_str("reserved frame flags are non-zero"), + Self::ReservedCode => formatter.write_str("reserved frame code is non-zero"), Self::InvalidFlowId => formatter.write_str("invalid zero flow ID"), - Self::InvalidWindow => formatter.write_str("window credit must be non-zero"), - Self::InvalidReset => { - formatter.write_str("RST must be the only flag and carry no data") - } - Self::InvalidOpen => formatter.write_str("SYN must be an isolated open frame"), + Self::InvalidData => formatter.write_str("DATA payload must be non-empty"), + Self::InvalidWindow => formatter.write_str("WINDOW credit must be non-zero"), + Self::InvalidClose => formatter.write_str("invalid CLOSE code or value"), } } } diff --git a/src/portal/conn.rs b/src/portal/conn.rs index 0df0651..65c484f 100644 --- a/src/portal/conn.rs +++ b/src/portal/conn.rs @@ -26,7 +26,7 @@ pub(super) use self::tcp::handle_tcp_incoming; use super::PortalInner; use super::admission::UnauthenticatedGuard; use crate::common::rate_limit_bytes_per_second; -use crate::protocol::ProtocolVersion; +use crate::protocol::ALPN; use crate::telemetry::{RuntimeEvent, RuntimeKind, RuntimeLevel}; pub(super) async fn handle_incoming( @@ -57,61 +57,51 @@ pub(super) async fn handle_incoming( // Keep them silent to avoid log amplification. Err(_) => return, }; - let version = match conn + let valid_alpn = conn .handshake_data() .and_then(|data| data.downcast::().ok()) - .and_then(|data| ProtocolVersion::from_alpn(data.protocol.as_deref()).ok()) - { - Some(version) => version, - None => { - conn.close(VarInt::from_u32(1), b"unsupported protocol"); - portal.logger.debug(format_args!( - "portal::conn::handle_incoming: invalid negotiated QUIC protocol" - )); - return; - } - }; - handle_connection(portal, conn, version, admission, shutdown).await; + .is_some_and(|data| data.protocol.as_deref() == Some(ALPN)); + if !valid_alpn { + conn.close(VarInt::from_u32(1), b"unsupported protocol"); + portal.logger.debug(format_args!( + "portal::conn::handle_incoming: invalid negotiated QUIC protocol" + )); + return; + } + handle_connection(portal, conn, admission, shutdown).await; } /// Runs authentication and then dispatches accepted streams/datagrams. async fn handle_connection( portal: Arc, conn: Connection, - version: ProtocolVersion, admission: UnauthenticatedGuard, shutdown: CancellationToken, ) { let auth_deadline = authentication_deadline(portal.runtime.handshake_timeout); - let authenticated = match authenticate_connection( - portal.clone(), - conn.clone(), - version, - auth_deadline, - &shutdown, - ) - .await - { - AuthenticationOutcome::Success(authenticated) => authenticated, - AuthenticationOutcome::Failure(err) => { - let (code, reason) = authentication_failure_close(); - conn.close(code, reason); - drop(admission); - portal.telemetry.emit_runtime( - RuntimeEvent::new( - RuntimeLevel::Warn, - RuntimeKind::Authentication, - format!("QUIC authentication failed: {err}"), - ) - .with_client(conn.remote_address().to_string()), - ); - portal.logger.error(format_args!( - "portal::conn::handle_connection: authentication failed: {err}" - )); - return; - } - AuthenticationOutcome::Shutdown => return, - }; + let authenticated = + match authenticate_connection(portal.clone(), conn.clone(), auth_deadline, &shutdown).await + { + AuthenticationOutcome::Success(authenticated) => authenticated, + AuthenticationOutcome::Failure(err) => { + let (code, reason) = authentication_failure_close(); + conn.close(code, reason); + drop(admission); + portal.telemetry.emit_runtime( + RuntimeEvent::new( + RuntimeLevel::Warn, + RuntimeKind::Authentication, + format!("QUIC authentication failed: {err}"), + ) + .with_client(conn.remote_address().to_string()), + ); + portal.logger.error(format_args!( + "portal::conn::handle_connection: authentication failed: {err}" + )); + return; + } + AuthenticationOutcome::Shutdown => return, + }; if portal.drain.is_cancelled() { conn.close(VarInt::from_u32(0), b""); drop(admission); diff --git a/src/portal/conn/auth.rs b/src/portal/conn/auth.rs index 6b358c4..a33a575 100644 --- a/src/portal/conn/auth.rs +++ b/src/portal/conn/auth.rs @@ -14,8 +14,6 @@ use crate::protocol::{AuthTransport, read_auth_frame}; use super::session::PortalSession; use crate::portal::PortalInner; -use crate::portal::pairing::SessionKey; -use crate::protocol::ProtocolVersion; const AUTH_EXPORTER_LABEL: &[u8] = b"EXPORTER-Nowhere-Auth"; @@ -44,7 +42,6 @@ pub(super) enum AuthenticationOutcome { pub(super) async fn authenticate_connection( portal: Arc, conn: Connection, - version: ProtocolVersion, deadline: Instant, shutdown: &CancellationToken, ) -> AuthenticationOutcome { @@ -112,7 +109,7 @@ pub(super) async fn authenticate_connection( session: Arc::new(PortalSession::new( portal.clone(), conn.clone(), - SessionKey::new(version, session_id), + session_id, )), first_send, first_recv, diff --git a/src/portal/conn/relay_tcp.rs b/src/portal/conn/relay_tcp.rs index c1a5fbd..ee212b2 100644 --- a/src/portal/conn/relay_tcp.rs +++ b/src/portal/conn/relay_tcp.rs @@ -41,7 +41,6 @@ pub(in crate::portal) async fn relay_paired_tcp(portal: Arc, paired id: 0, timestamp_ms: now_unix_ms(), protocol: TrafficProtocol::Tcp, - wire_version: Some(uplink_path.version), flow_id: Some(flow_id.into()), session_tag: None, client: Some(uplink_path.peer.clone()), diff --git a/src/portal/conn/relay_uot.rs b/src/portal/conn/relay_uot.rs index 2635499..9b8c87e 100644 --- a/src/portal/conn/relay_uot.rs +++ b/src/portal/conn/relay_uot.rs @@ -48,7 +48,6 @@ pub(in crate::portal) async fn relay_paired_udp(portal: Arc, paired id: 0, timestamp_ms: now_unix_ms(), protocol: TrafficProtocol::Udp, - wire_version: Some(uplink_path.version), flow_id: Some(flow_id.into()), session_tag: None, client: Some(uplink_path.peer.clone()), diff --git a/src/portal/conn/session.rs b/src/portal/conn/session.rs index a659dc6..137659d 100644 --- a/src/portal/conn/session.rs +++ b/src/portal/conn/session.rs @@ -57,7 +57,6 @@ pub(in crate::portal) struct DatagramReadyRequest { impl PortalSession { fn link_path(&self) -> crate::portal::pairing::LinkPath { crate::portal::pairing::LinkPath { - version: self.session_key.version, peer: self.conn.remote_address().to_string(), local: self.conn.local_ip().map_or_else( || self.portal.endpoint_addr.clone(), diff --git a/src/portal/conn/tcp/flow.rs b/src/portal/conn/tcp/flow.rs index feff7da..b91f5fc 100644 --- a/src/portal/conn/tcp/flow.rs +++ b/src/portal/conn/tcp/flow.rs @@ -81,7 +81,6 @@ pub(super) async fn process_flow( None }; let path = crate::portal::pairing::LinkPath { - version: session_id.version, peer: peer.to_string(), local: local.map_or_else(|| portal.endpoint_addr.clone(), |value| value.to_string()), }; diff --git a/src/portal/conn/tcp/mod.rs b/src/portal/conn/tcp/mod.rs index 3e7777f..f386275 100644 --- a/src/portal/conn/tcp/mod.rs +++ b/src/portal/conn/tcp/mod.rs @@ -16,7 +16,7 @@ use tokio::io::AsyncReadExt; use tokio::net::TcpStream; use tokio::task::JoinSet; use tokio::time::{timeout, timeout_at}; -use tokio_rustls::TlsAcceptor; +use tokio_rustls::LazyConfigAcceptor; use tokio_util::sync::CancellationToken; use crate::common::MUX_MARKER; @@ -24,7 +24,7 @@ use crate::mux::{MUX_IDLE_TIMEOUT, MuxConfig, MuxHandle}; use crate::portal::PortalInner; use crate::portal::admission::UnauthenticatedGuard; use crate::portal::pairing::SessionKey; -use crate::protocol::{AuthTransport, ProtocolVersion, read_auth_frame}; +use crate::protocol::{ALPN, AuthTransport, read_auth_frame}; use crate::telemetry::{RuntimeEvent, RuntimeKind, RuntimeLevel}; use self::flow::process_flow; @@ -90,12 +90,25 @@ pub(super) async fn handle_tcp_incoming_with_timeouts( .debug(format_args!("portal::conn::tcp: TCP_NODELAY failed: {err}")); } let local = stream.local_addr().ok(); - let acceptor = TlsAcceptor::from(portal.tls_server_config.clone()); + let server_config = portal.tls_server_config.clone(); let tls_stream = match tokio::select! { biased; _ = shutdown.cancelled() => return, _ = portal.drain.cancelled() => return, - result = timeout(portal.runtime.handshake_timeout, acceptor.accept(stream)) => result, + result = timeout(portal.runtime.handshake_timeout, async move { + let start = LazyConfigAcceptor::new(rustls::server::Acceptor::default(), stream).await?; + let offers_nw2 = start + .client_hello() + .alpn() + .is_some_and(|mut protocols| protocols.any(|protocol| protocol == ALPN)); + if !offers_nw2 { + return Err(std::io::Error::new( + ErrorKind::InvalidData, + "client did not offer nw2 ALPN", + )); + } + start.into_stream(server_config).await + }) => result, } { Ok(Ok(stream)) => stream, Ok(Err(err)) => { @@ -114,17 +127,8 @@ pub(super) async fn handle_tcp_incoming_with_timeouts( } Err(_) => return, }; - let auth_deadline = authentication_deadline(portal.runtime.handshake_timeout); let mut tls_stream = tls_stream; - let version = match ProtocolVersion::from_alpn(tls_stream.get_ref().1.alpn_protocol()) { - Ok(version) => version, - Err(err) => { - portal.logger.debug(format_args!( - "portal::conn::tcp: invalid negotiated protocol: {err}" - )); - return; - } - }; + let auth_deadline = authentication_deadline(portal.runtime.handshake_timeout); let mut exporter = [0u8; 32]; if let Err(err) = tls_stream.get_ref().1.export_keying_material( &mut exporter, @@ -169,7 +173,7 @@ pub(super) async fn handle_tcp_incoming_with_timeouts( } Err(_) => return, }; - let session_key = SessionKey::new(version, session_id); + let session_key = session_id; if let Err(err) = SockRef::from(tls_stream.get_ref().0).set_keepalive(true) { portal.logger.debug(format_args!( "portal::conn::tcp: TCP keepalive failed: {err}" diff --git a/src/portal/pairing.rs b/src/portal/pairing.rs index 649721d..3e66c35 100644 --- a/src/portal/pairing.rs +++ b/src/portal/pairing.rs @@ -142,12 +142,6 @@ impl PairingRegistry { target: Option<&Target>, link: &LinkHalf, ) -> Result<(), PairingError> { - if link.path.version != session_id.version { - return Err(PairingError::new( - FlowErrorCode::MetadataConflict, - "portal::pairing: carrier protocol version mismatch", - )); - } if header.kind != expected_kind { return Err(PairingError::new( FlowErrorCode::InvalidRequest, diff --git a/src/portal/pairing/state.rs b/src/portal/pairing/state.rs index 8e200eb..3d5e30d 100644 --- a/src/portal/pairing/state.rs +++ b/src/portal/pairing/state.rs @@ -11,28 +11,12 @@ use quinn::Connection; use tokio::sync::{OwnedSemaphorePermit, Semaphore, mpsc, oneshot}; use tokio_util::sync::CancellationToken; -use crate::protocol::{FlowKind, ProtocolVersion, SessionId, Target}; +use crate::protocol::{FlowKind, SessionId, Target}; pub(in crate::portal) type BoxReader = Pin>; pub(in crate::portal) type BoxWriter = Pin>; -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub(in crate::portal) struct SessionKey { - pub(in crate::portal) version: ProtocolVersion, - pub(in crate::portal) id: SessionId, -} - -impl SessionKey { - pub(in crate::portal) const fn new(version: ProtocolVersion, id: SessionId) -> Self { - Self { version, id } - } -} - -impl From for SessionKey { - fn from(id: SessionId) -> Self { - Self::new(ProtocolVersion::V2, id) - } -} +pub(in crate::portal) type SessionKey = SessionId; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub(in crate::portal) struct FlowKey { @@ -65,7 +49,6 @@ pub(in crate::portal) struct PendingTcp { #[derive(Clone, Debug, Eq, PartialEq)] pub(in crate::portal) struct LinkPath { - pub(in crate::portal) version: ProtocolVersion, pub(in crate::portal) peer: String, pub(in crate::portal) local: String, } diff --git a/src/protocol/auth.rs b/src/protocol/auth.rs index 62dd5ca..068597a 100644 --- a/src/protocol/auth.rs +++ b/src/protocol/auth.rs @@ -20,7 +20,7 @@ pub const AUTH_TAG_LEN: usize = 16; /// Length of the fixed authentication frame. pub const AUTH_FRAME_LEN: usize = SESSION_ID_LEN + AUTH_TAG_LEN; -const AUTH_ROOT_SALT_LABEL: &[u8] = b"nowhere/now/1/auth-root"; +const AUTH_ROOT_SALT_LABEL: &[u8] = b"nowhere/nw2/auth-root"; const AUTH_KEY_INFO: &[u8] = b"authentication"; /// Authentication key derived once from the configured shared key. diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 5db4c7c..5311ec7 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -10,7 +10,6 @@ mod request; mod result; mod uot; mod util; -mod version; pub use auth::{ AUTH_FRAME_LEN, AUTH_TAG_LEN, AuthFrame, AuthKey, AuthTransport, Credentials, TLS_EXPORTER_LEN, @@ -43,4 +42,5 @@ pub use uot::{ UOT_HEADER_LEN, UOT_PACKET_MAX, encode_udp_packet, encode_udp_packet_header, read_udp_packet, read_udp_packet_into, write_udp_packet, }; -pub use version::{ProtocolVersion, SUPPORTED_ALPNS, V1_ALPN, V2_ALPN}; +/// The sole ALPN accepted by Nowhere 2 carriers. +pub const ALPN: &[u8] = b"nw2"; diff --git a/src/protocol/version.rs b/src/protocol/version.rs deleted file mode 100644 index d1f6f20..0000000 --- a/src/protocol/version.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2026 NodePassProject -// SPDX-License-Identifier: GPL-3.0-only - -//! Data-plane protocol version negotiated through TLS ALPN. - -use anyhow::{Result, bail}; -use serde::{Deserialize, Serialize}; - -/// ALPN used by Nowhere 1.x peers with their default configuration. -pub const V1_ALPN: &[u8] = b"now/1"; -/// Fixed ALPN for Nowhere 2.x peers. -pub const V2_ALPN: &[u8] = b"nw2"; -/// Supported ALPNs in server-preference order. -pub const SUPPORTED_ALPNS: [&[u8]; 2] = [V2_ALPN, V1_ALPN]; - -/// Data-plane protocol version selected for one authenticated carrier. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum ProtocolVersion { - V1, - V2, -} - -impl ProtocolVersion { - /// Classifies an ALPN selected by TLS or QUIC. - pub fn from_alpn(alpn: Option<&[u8]>) -> Result { - match alpn { - Some(V2_ALPN) => Ok(Self::V2), - Some(V1_ALPN) => Ok(Self::V1), - Some(_) => bail!("protocol::ProtocolVersion: unsupported negotiated ALPN"), - None => bail!("protocol::ProtocolVersion: peer did not negotiate ALPN"), - } - } - - /// Stable short label used by local telemetry and diagnostics. - pub const fn as_str(self) -> &'static str { - match self { - Self::V1 => "v1", - Self::V2 => "v2", - } - } -} - -impl std::fmt::Display for ProtocolVersion { - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str(self.as_str()) - } -} - -#[cfg(test)] -#[path = "../tests/protocol/version.rs"] -mod tests; diff --git a/src/telemetry/hub.rs b/src/telemetry/hub.rs index f225f93..e751e68 100644 --- a/src/telemetry/hub.rs +++ b/src/telemetry/hub.rs @@ -184,7 +184,6 @@ impl TelemetryHub { id: start.id, timestamp_ms: start.timestamp_ms, protocol: start.protocol, - wire_version: start.wire_version, flow_id: start.flow_id, session_tag: start.session_tag, client: start.client, @@ -224,7 +223,6 @@ impl TelemetryHub { timestamp_ms: now_unix_ms(), duration_ms: started_at.elapsed().as_millis().min(u64::MAX as u128) as u64, protocol: started.protocol, - wire_version: started.wire_version, flow_id: started.flow_id, session_tag: started.session_tag.clone(), client: started.client.clone(), @@ -287,12 +285,6 @@ impl AccessSpan { } } - pub(crate) fn set_wire_version(&mut self, version: crate::protocol::ProtocolVersion) { - if let Some(started) = &mut self.started { - started.wire_version = Some(version); - } - } - pub(crate) fn finish(self, outcome: AccessOutcome, error: Option) { self.finish_once(outcome, error); } diff --git a/src/telemetry/wire.rs b/src/telemetry/wire.rs index 3433218..3c8c026 100644 --- a/src/telemetry/wire.rs +++ b/src/telemetry/wire.rs @@ -8,7 +8,7 @@ use std::time::Duration; use anyhow::Result; use serde::{Deserialize, Serialize}; -use crate::protocol::{Carrier, ProtocolVersion}; +use crate::protocol::Carrier; use super::process::{now_unix_ms, process_incarnation, process_uid}; @@ -140,7 +140,6 @@ pub(crate) struct AccessStart { pub(crate) id: u64, pub(crate) timestamp_ms: u64, pub(crate) protocol: TrafficProtocol, - pub(crate) wire_version: Option, pub(crate) flow_id: Option, pub(crate) session_tag: Option, pub(crate) client: Option, @@ -156,7 +155,6 @@ pub(crate) struct AccessStarted { pub(crate) id: u64, pub(crate) timestamp_ms: u64, pub(crate) protocol: TrafficProtocol, - pub(crate) wire_version: Option, pub(crate) flow_id: Option, pub(crate) session_tag: Option, pub(crate) client: Option, @@ -174,7 +172,6 @@ pub(crate) struct AccessFinished { pub(crate) timestamp_ms: u64, pub(crate) duration_ms: u64, pub(crate) protocol: TrafficProtocol, - pub(crate) wire_version: Option, pub(crate) flow_id: Option, pub(crate) session_tag: Option, pub(crate) client: Option, diff --git a/src/tests/common/tls.rs b/src/tests/common/tls.rs index 80f39bc..c7cc32b 100644 --- a/src/tests/common/tls.rs +++ b/src/tests/common/tls.rs @@ -28,12 +28,12 @@ fn server_tls_config_explicitly_disables_early_data() { } #[test] -fn server_prefers_v2_and_accepts_default_v1() { +fn server_offers_only_nw2() { let (_, tls, _) = new_server_configs( &Url::parse("portal://secret@127.0.0.1:2000?tls=1&alpn=private/2&pool=8").unwrap(), Logger::new(LogLevel::None, false), ) .unwrap(); - assert_eq!(tls.alpn_protocols, [b"nw2".to_vec(), b"now/1".to_vec()]); + assert_eq!(tls.alpn_protocols, [b"nw2".to_vec()]); } diff --git a/src/tests/mux/runtime.rs b/src/tests/mux/runtime.rs index 95567be..906283d 100644 --- a/src/tests/mux/runtime.rs +++ b/src/tests/mux/runtime.rs @@ -1,6 +1,83 @@ +use super::wire::{CLOSE_FIN, CLOSE_RESET, FrameHeader, encode_header}; use super::*; use tokio::io::{AsyncReadExt, AsyncWriteExt}; +#[test] +fn production_idle_timeout_remains_thirty_seconds() { + assert_eq!(MUX_IDLE_TIMEOUT, Duration::from_secs(30)); +} + +async fn assert_raw_frame_closes_carrier(frame: &[u8]) { + let (left, mut peer) = tokio::io::duplex(1 << 20); + let (handle, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + peer.write_all(frame).await.unwrap(); + tokio::time::timeout(Duration::from_secs(1), handle.closed()) + .await + .expect("invalid frame must close carrier"); +} + +#[tokio::test] +async fn invalid_kind_and_unknown_flow_data_close_carrier() { + assert_raw_frame_closes_carrier(&[0xff, 0, 0, 0, 0, 0, 0, 1]).await; + + let mut frame = encode_header(FrameHeader::data(99, 1).unwrap()) + .unwrap() + .to_vec(); + frame.push(0); + assert_raw_frame_closes_carrier(&frame).await; +} + +#[tokio::test] +async fn duplicate_open_and_credit_overflow_close_carrier() { + let open = encode_header(FrameHeader::open(7, 0).unwrap()).unwrap(); + let mut duplicate = open.to_vec(); + duplicate.extend_from_slice(&open); + assert_raw_frame_closes_carrier(&duplicate).await; + + let overflow = encode_header(FrameHeader::window(0, u16::MAX as usize).unwrap()).unwrap(); + assert_raw_frame_closes_carrier(&overflow).await; +} + +#[tokio::test] +async fn duplicate_close_and_late_stream_window_are_idempotent() { + let (left, mut peer) = tokio::io::duplex(1 << 20); + let (handle, mut incoming) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + peer.write_all(&encode_header(FrameHeader::open(7, 0).unwrap()).unwrap()) + .await + .unwrap(); + let stream = incoming.accept().await.unwrap().unwrap(); + let reset = encode_header(FrameHeader::close(7, CLOSE_RESET).unwrap()).unwrap(); + peer.write_all(&reset).await.unwrap(); + peer.write_all(&reset).await.unwrap(); + peer.write_all(&encode_header(FrameHeader::window(7, 1).unwrap()).unwrap()) + .await + .unwrap(); + tokio::task::yield_now().await; + assert!(!handle.is_closed()); + drop(stream); +} + +#[tokio::test] +async fn data_after_fin_closes_carrier() { + let (left, mut peer) = tokio::io::duplex(1 << 20); + let (handle, mut incoming) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + peer.write_all(&encode_header(FrameHeader::open(8, 0).unwrap()).unwrap()) + .await + .unwrap(); + let _stream = incoming.accept().await.unwrap().unwrap(); + peer.write_all(&encode_header(FrameHeader::close(8, CLOSE_FIN).unwrap()).unwrap()) + .await + .unwrap(); + let mut data = encode_header(FrameHeader::data(8, 1).unwrap()) + .unwrap() + .to_vec(); + data.push(0); + peer.write_all(&data).await.unwrap(); + tokio::time::timeout(Duration::from_secs(1), handle.closed()) + .await + .expect("DATA after FIN must close carrier"); +} + #[tokio::test] async fn idle_deadline_resets_when_a_stream_becomes_active() { let (left, right) = tokio::io::duplex(1 << 20); diff --git a/src/tests/mux/wire.rs b/src/tests/mux/wire.rs index e7d7e0a..efef5f0 100644 --- a/src/tests/mux/wire.rs +++ b/src/tests/mux/wire.rs @@ -1,28 +1,44 @@ use super::*; #[test] -fn header_has_stable_eight_byte_vector() { - let header = FrameHeader::stream(0x0102_0304, FLAG_SYN, 0x0506).unwrap(); - let encoded = encode_header(header).unwrap(); - assert_eq!(encoded, [1, FLAG_SYN, 0x05, 0x06, 1, 2, 3, 4]); - assert_eq!(decode_header(&encoded).unwrap(), header); +fn headers_have_stable_eight_byte_vectors() { + let cases = [ + ( + FrameHeader::open(0x0102_0304, 0x0506).unwrap(), + [1, 0, 5, 6, 1, 2, 3, 4], + ), + ( + FrameHeader::data(0x0102_0304, 0x0506).unwrap(), + [2, 0, 5, 6, 1, 2, 3, 4], + ), + ( + FrameHeader::window(0, 0x0506).unwrap(), + [3, 0, 5, 6, 0, 0, 0, 0], + ), + ( + FrameHeader::close(0x0102_0304, CLOSE_RESET).unwrap(), + [4, 1, 0, 0, 1, 2, 3, 4], + ), + ]; + for (header, encoded) in cases { + assert_eq!(encode_header(header).unwrap(), encoded); + assert_eq!(decode_header(&encoded).unwrap(), header); + } } #[test] -fn only_connection_window_accepts_zero_flow_id() { +fn only_window_accepts_zero_flow_id() { assert!(FrameHeader::window(0, 1).is_ok()); - assert!(FrameHeader::stream(0, 0, 1).is_err()); + assert!(FrameHeader::open(0, 1).is_err()); + assert!(FrameHeader::data(0, 1).is_err()); + assert!(FrameHeader::close(0, CLOSE_FIN).is_err()); } #[test] -fn reset_is_exclusive_and_empty() { - assert!(FrameHeader::stream(1, FLAG_RST, 0).is_ok()); - assert!(FrameHeader::stream(1, FLAG_RST | FLAG_FIN, 0).is_err()); - assert!(FrameHeader::stream(1, FLAG_RST, 1).is_err()); -} - -#[test] -fn open_uses_value_for_initial_window_credit() { - assert!(FrameHeader::stream(1, FLAG_SYN, 12 * 1024).is_ok()); - assert!(FrameHeader::stream(1, FLAG_SYN | FLAG_FIN, 0).is_err()); +fn invalid_codes_and_values_are_rejected() { + assert!(FrameHeader::data(1, 0).is_err()); + assert!(FrameHeader::window(1, 0).is_err()); + assert!(FrameHeader::close(1, CLOSE_FIN).is_ok()); + assert!(FrameHeader::close(1, CLOSE_RESET).is_ok()); + assert!(FrameHeader::close(1, 2).is_err()); } diff --git a/src/tests/portal/conn/relay.rs b/src/tests/portal/conn/relay.rs index 98f1e7f..50b98aa 100644 --- a/src/tests/portal/conn/relay.rs +++ b/src/tests/portal/conn/relay.rs @@ -22,12 +22,10 @@ fn symmetric_exchange_path( #[test] fn paired_path_contains_both_carriers_and_both_client_links() { let uplink = LinkPath { - version: crate::protocol::ProtocolVersion::V2, peer: "198.51.100.1:1000".into(), local: "192.0.2.1:2000".into(), }; let downlink = LinkPath { - version: crate::protocol::ProtocolVersion::V2, peer: "[2001:db8::2]:2000".into(), local: "[2001:db8::1]:2000".into(), }; diff --git a/src/tests/portal/conn/support.rs b/src/tests/portal/conn/support.rs index dd2ef04..924cb86 100644 --- a/src/tests/portal/conn/support.rs +++ b/src/tests/portal/conn/support.rs @@ -71,6 +71,15 @@ impl ServerCertVerifier for AcceptAnyServerCertificate { pub(super) async fn connect_test_tls( listen_addr: SocketAddr, ) -> tokio_rustls::client::TlsStream { + connect_test_tls_with_alpns(listen_addr, vec![b"nw2".to_vec()]) + .await + .unwrap() +} + +pub(super) async fn connect_test_tls_with_alpns( + listen_addr: SocketAddr, + alpns: Vec>, +) -> std::io::Result> { let provider = Arc::new(rustls::crypto::ring::default_provider()); let mut client_config = rustls::ClientConfig::builder_with_provider(provider) .with_protocol_versions(&[&rustls::version::TLS13]) @@ -78,16 +87,15 @@ pub(super) async fn connect_test_tls( .dangerous() .with_custom_certificate_verifier(Arc::new(AcceptAnyServerCertificate)) .with_no_client_auth(); - client_config.alpn_protocols = vec![b"now/1".to_vec()]; + client_config.alpn_protocols = alpns; let connector = TlsConnector::from(Arc::new(client_config)); - let stream = TcpStream::connect(listen_addr).await.unwrap(); + let stream = TcpStream::connect(listen_addr).await?; connector .connect( ServerName::try_from("localhost").unwrap().to_owned(), stream, ) .await - .unwrap() } pub(super) fn tls_auth_frame( @@ -207,7 +215,7 @@ pub(super) async fn connect_test_quic_with_url_and_limits( .dangerous() .with_custom_certificate_verifier(Arc::new(AcceptAnyServerCertificate)) .with_no_client_auth(); - rustls_config.alpn_protocols = vec![b"now/1".to_vec()]; + rustls_config.alpn_protocols = vec![b"nw2".to_vec()]; let quic_crypto = QuicClientConfig::try_from(rustls_config).unwrap(); let mut client_endpoint = quinn::Endpoint::client(SocketAddr::from(([127, 0, 0, 1], 0))).unwrap(); @@ -236,7 +244,7 @@ pub(super) async fn connect_test_quic_to(listen_addr: SocketAddr) -> (quinn::End .dangerous() .with_custom_certificate_verifier(Arc::new(AcceptAnyServerCertificate)) .with_no_client_auth(); - rustls_config.alpn_protocols = vec![b"now/1".to_vec()]; + rustls_config.alpn_protocols = vec![b"nw2".to_vec()]; let quic_crypto = QuicClientConfig::try_from(rustls_config).unwrap(); let mut endpoint = quinn::Endpoint::client(SocketAddr::from(([127, 0, 0, 1], 0))).unwrap(); endpoint.set_default_client_config(quinn::ClientConfig::new(Arc::new(quic_crypto))); diff --git a/src/tests/portal/conn/tcp.rs b/src/tests/portal/conn/tcp.rs index 294f304..f65ea7a 100644 --- a/src/tests/portal/conn/tcp.rs +++ b/src/tests/portal/conn/tcp.rs @@ -25,8 +25,8 @@ use crate::protocol::{ use super::super::*; use super::support::{ - TestSocksAuth, connect_test_tls, spawn_test_socks5_tcp, spawn_test_socks5_udp, test_target, - tls_auth_frame, + TestSocksAuth, connect_test_tls, connect_test_tls_with_alpns, spawn_test_socks5_tcp, + spawn_test_socks5_udp, test_target, tls_auth_frame, }; fn duplex_setup(flow_id: u32, kind: FlowKind, target: &str) -> Vec { @@ -43,6 +43,36 @@ fn duplex_setup(flow_id: u32, kind: FlowKind, target: &str) -> Vec { setup } +#[tokio::test] +async fn portal_rejects_client_hello_without_nw2() { + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let listen_addr = listener.local_addr().unwrap(); + let portal = Portal::new( + Url::parse("portal://secret@127.0.0.1:2000?log=none&net=tcp").unwrap(), + Logger::new(LogLevel::None, false), + ) + .unwrap(); + let portal_inner = portal.inner.clone(); + let shutdown = CancellationToken::new(); + let child_shutdown = shutdown.clone(); + let server_task = tokio::spawn(async move { + let (stream, peer) = listener.accept().await.unwrap(); + let admission = portal_inner + .unauthenticated_admission + .try_acquire(peer.ip()) + .unwrap(); + handle_tcp_incoming(portal_inner, stream, peer, admission, child_shutdown).await; + }); + + assert!( + connect_test_tls_with_alpns(listen_addr, vec![b"now/1".to_vec()]) + .await + .is_err() + ); + shutdown.cancel(); + server_task.await.unwrap(); +} + #[tokio::test] async fn portal_accepts_delayed_dedicated_flow_header() { let echo_listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); @@ -383,7 +413,7 @@ async fn tls_mux_carrier_closes_after_becoming_fully_idle() { .unwrap(); let mut window = [0_u8; 8]; tls.read_exact(&mut window).await.unwrap(); - assert_eq!(window[0], 0x02); + assert_eq!(window[0], 0x03); assert_eq!(&window[4..], &[0, 0, 0, 0]); let mut byte = [0_u8; 1]; let read = tls.read(&mut byte).await; diff --git a/src/tests/portal/pairing.rs b/src/tests/portal/pairing.rs index dd66b94..893b2bf 100644 --- a/src/tests/portal/pairing.rs +++ b/src/tests/portal/pairing.rs @@ -65,7 +65,6 @@ fn target(value: &str) -> Target { fn path(label: &str) -> LinkPath { LinkPath { - version: crate::protocol::ProtocolVersion::V2, peer: format!("{label}.client:1234"), local: "portal.test:2000".into(), } @@ -84,7 +83,7 @@ fn available_udp_permits(registry: &PairingRegistry, session_id: SessionId) -> u .links .lock() .expect("link registry poisoned") - .get(&session_id.into()) + .get(&session_id) .expect("registered session") .udp_flow_budget .available_permits() @@ -131,5 +130,3 @@ mod rejection; mod replacement; #[path = "pairing/udp.rs"] mod udp; -#[path = "pairing/version.rs"] -mod version; diff --git a/src/tests/portal/pairing/rejection.rs b/src/tests/portal/pairing/rejection.rs index 26edd45..13c6506 100644 --- a/src/tests/portal/pairing/rejection.rs +++ b/src/tests/portal/pairing/rejection.rs @@ -111,7 +111,7 @@ async fn late_attach_receives_original_open_pair_timeout() { .is_none() ); let tcp_key = FlowKey { - session_id: tcp_session.into(), + session_id: tcp_session, flow_id: 16, }; tokio::time::timeout(Duration::from_secs(1), async { @@ -171,7 +171,7 @@ async fn late_attach_receives_original_open_pair_timeout() { .is_none() ); let udp_key = FlowKey { - session_id: udp_session.into(), + session_id: udp_session, flow_id: 17, }; tokio::time::timeout(Duration::from_secs(1), async { diff --git a/src/tests/portal/pairing/replacement.rs b/src/tests/portal/pairing/replacement.rs index c7bae51..186bb08 100644 --- a/src/tests/portal/pairing/replacement.rs +++ b/src/tests/portal/pairing/replacement.rs @@ -234,7 +234,7 @@ async fn stale_open_after_map_lock_leaves_exact_rejection_for_tcp_attach() { .await }); tokio::time::timeout(Duration::from_secs(1), async { - while registry.active_quic_generation(session_id.into()) == Some(old_generation) { + while registry.active_quic_generation(session_id) == Some(old_generation) { tokio::task::yield_now().await; } }) diff --git a/src/tests/portal/pairing/version.rs b/src/tests/portal/pairing/version.rs deleted file mode 100644 index 7830f02..0000000 --- a/src/tests/portal/pairing/version.rs +++ /dev/null @@ -1,124 +0,0 @@ -use super::*; -use crate::protocol::ProtocolVersion; - -fn versioned_half(version: ProtocolVersion, label: &str) -> LinkHalf { - LinkHalf::tcp(LinkPath { - version, - peer: format!("{label}.client:1234"), - local: "portal.test:2000".into(), - }) -} - -#[tokio::test] -async fn identical_session_and_flow_ids_are_isolated_by_protocol_version() { - let registry = registry(8, Duration::from_secs(30)); - let stats = Arc::new(Stats::default()); - let id = [41; SESSION_ID_LEN]; - let v1 = SessionKey::new(ProtocolVersion::V1, id); - let v2 = SessionKey::new(ProtocolVersion::V2, id); - let _v1_link = registry.register_tcp_link(v1, stats.clone()); - let _v2_link = registry.register_tcp_link(v2, stats); - - let (uplink, _uplink_peer) = tokio::io::duplex(64); - assert!( - registry - .submit_tcp( - v1, - header( - FlowRole::Open, - 9, - FlowKind::Tcp, - Carrier::TlsTcp, - Carrier::TlsTcp, - ), - Some(target("target.test:443")), - versioned_half(ProtocolVersion::V1, "v1"), - Some(Box::pin(uplink)), - None, - None, - ) - .await - .unwrap() - .is_none() - ); - - let (downlink, _downlink_peer) = tokio::io::duplex(64); - assert!( - registry - .submit_tcp( - v2, - header( - FlowRole::Attach, - 9, - FlowKind::Tcp, - Carrier::TlsTcp, - Carrier::TlsTcp, - ), - None, - versioned_half(ProtocolVersion::V2, "v2"), - None, - Some(Box::pin(downlink)), - None, - ) - .await - .unwrap() - .is_none() - ); - - let pending = registry.tcp.lock().await; - assert!(pending.contains_key(&FlowKey { - session_id: v1, - flow_id: 9 - })); - assert!(pending.contains_key(&FlowKey { - session_id: v2, - flow_id: 9 - })); - assert_eq!(pending.len(), 2); - drop(pending); - - registry - .reject_flow_setup(v1, 77, FlowErrorCode::DialFailed) - .await; - assert_eq!( - registry.terminal_rejection( - FlowKey { - session_id: v1, - flow_id: 77, - }, - false, - ), - Some(FlowErrorCode::DialFailed) - ); - assert_eq!( - registry.terminal_rejection( - FlowKey { - session_id: v2, - flow_id: 77, - }, - false, - ), - None - ); -} - -#[tokio::test] -async fn quic_replacement_is_scoped_to_protocol_version() { - let registry = registry(8, Duration::from_secs(30)); - let stats = Arc::new(Stats::default()); - let id = [42; SESSION_ID_LEN]; - let v1 = SessionKey::new(ProtocolVersion::V1, id); - let v2 = SessionKey::new(ProtocolVersion::V2, id); - let v1_replaced = tokio_util::sync::CancellationToken::new(); - - let _v1 = registry - .register_quic_link(v1, stats.clone(), v1_replaced.clone()) - .await; - let _v2 = registry - .register_quic_link(v2, stats, tokio_util::sync::CancellationToken::new()) - .await; - - assert!(!v1_replaced.is_cancelled()); - assert!(registry.active_quic_generation(v1).is_some()); - assert!(registry.active_quic_generation(v2).is_some()); -} diff --git a/src/tests/protocol/auth.rs b/src/tests/protocol/auth.rs index 7796939..d938669 100644 --- a/src/tests/protocol/auth.rs +++ b/src/tests/protocol/auth.rs @@ -17,7 +17,7 @@ fn hkdf_and_auth_frames_match_fixed_vectors() { let key = derive_auth_key(b"secret"); assert_eq!( hex(&key), - "1076221669fa28bcf70aa8545bddd6f760dcefbe279c3f38a5ff5d925708f867" + "8de7e08dd22134ac5acc57902658a36b7f6f9d219987ed49b934a7529a4d24c5" ); assert_eq!( hex(&encode_auth_frame( @@ -26,7 +26,7 @@ fn hkdf_and_auth_frames_match_fixed_vectors() { &EXPORTER, SESSION )), - "000102030405060708090a0b0c0d0e0f24a4c0d5f8946b65bcf270ed6e1c3dec" + "000102030405060708090a0b0c0d0e0f91f3033378b001f0de171717c027be00" ); assert_eq!( hex(&encode_auth_frame( @@ -35,7 +35,7 @@ fn hkdf_and_auth_frames_match_fixed_vectors() { &EXPORTER, SESSION )), - "000102030405060708090a0b0c0d0e0f8176b984db64a1e2c811e751d955b635" + "000102030405060708090a0b0c0d0e0f97d0be56bda8a0ee7596775b35efbf68" ); } diff --git a/src/tests/protocol/version.rs b/src/tests/protocol/version.rs deleted file mode 100644 index c179049..0000000 --- a/src/tests/protocol/version.rs +++ /dev/null @@ -1,16 +0,0 @@ -use super::*; - -#[test] -fn alpn_mapping_is_fixed_and_ordered_for_v2_preference() { - assert_eq!(SUPPORTED_ALPNS, [b"nw2".as_slice(), b"now/1".as_slice()]); - assert_eq!( - ProtocolVersion::from_alpn(Some(V2_ALPN)).unwrap(), - ProtocolVersion::V2 - ); - assert_eq!( - ProtocolVersion::from_alpn(Some(V1_ALPN)).unwrap(), - ProtocolVersion::V1 - ); - assert!(ProtocolVersion::from_alpn(Some(b"private/2")).is_err()); - assert!(ProtocolVersion::from_alpn(None).is_err()); -} diff --git a/src/tests/telemetry/hub.rs b/src/tests/telemetry/hub.rs index 3eb0e8a..6a49215 100644 --- a/src/tests/telemetry/hub.rs +++ b/src/tests/telemetry/hub.rs @@ -40,7 +40,6 @@ fn access_span_finishes_only_once() { id: 0, timestamp_ms: 1, protocol: TrafficProtocol::Tcp, - wire_version: Some(crate::protocol::ProtocolVersion::V1), flow_id: Some(7), session_tag: Some("abc123".to_owned()), client: Some("127.0.0.1:1".to_owned()), @@ -77,40 +76,6 @@ fn access_fields_are_not_built_without_a_receiver() { span.finish(AccessOutcome::Success, None); } -#[test] -fn negotiated_wire_version_is_added_to_completion() { - let hub = TelemetryHub::new(descriptor()); - let mut events = hub.event_receiver(); - let mut span = hub.start_access(|| AccessStart { - id: 0, - timestamp_ms: 1, - protocol: TrafficProtocol::Tcp, - wire_version: None, - flow_id: None, - session_tag: None, - client: None, - path_peers: Vec::new(), - target: "example:443".to_owned(), - initial_uplink: Some(Carrier::TlsTcp), - initial_downlink: Some(Carrier::TlsTcp), - path: None, - }); - span.set_wire_version(crate::protocol::ProtocolVersion::V2); - span.finish(AccessOutcome::Success, None); - - let Ok(ServerMessage::AccessStart(start)) = events.try_recv() else { - panic!("missing access start"); - }; - assert_eq!(start.wire_version, None); - let Ok(ServerMessage::AccessFinish(finish)) = events.try_recv() else { - panic!("missing access finish"); - }; - assert_eq!( - finish.wire_version, - Some(crate::protocol::ProtocolVersion::V2) - ); -} - #[test] fn snapshot_contains_existing_transport_counters() { let hub = TelemetryHub::new(descriptor()); diff --git a/src/tests/tui/client.rs b/src/tests/tui/client.rs index e6c96ef..0a61e5a 100644 --- a/src/tests/tui/client.rs +++ b/src/tests/tui/client.rs @@ -40,7 +40,6 @@ fn completion_inherits_access_path() { id: 9, timestamp_ms: 1, protocol: TrafficProtocol::Tcp, - wire_version: Some(crate::protocol::ProtocolVersion::V1), flow_id: None, session_tag: Some("abc123".to_owned()), client: Some("10.0.0.1:9".to_owned()), @@ -56,7 +55,6 @@ fn completion_inherits_access_path() { timestamp_ms: 2, duration_ms: 1, protocol: TrafficProtocol::Tcp, - wire_version: Some(crate::protocol::ProtocolVersion::V1), flow_id: None, session_tag: Some("abc123".to_owned()), client: Some("10.0.0.1:9".to_owned()), diff --git a/src/tests/vector/session/tls.rs b/src/tests/vector/session/tls.rs index fcd58b0..4316d3a 100644 --- a/src/tests/vector/session/tls.rs +++ b/src/tests/vector/session/tls.rs @@ -7,7 +7,6 @@ use tokio::io::AsyncReadExt; fn shard(handle: MuxHandle) -> TlsMux { TlsMux { handle, - version: ProtocolVersion::V2, target_density: 4, } } diff --git a/src/tests/vector/tls.rs b/src/tests/vector/tls.rs index d1c8164..7c63315 100644 --- a/src/tests/vector/tls.rs +++ b/src/tests/vector/tls.rs @@ -29,7 +29,7 @@ async fn client_prefers_fixed_v2_alpn() { .with_no_client_auth() .with_single_cert(vec![certificate], key) .unwrap(); - server.alpn_protocols = vec![b"nw2".to_vec(), b"now/1".to_vec()]; + server.alpn_protocols = vec![b"nw2".to_vec()]; let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let endpoint = listener.local_addr().unwrap(); let server_task = tokio::spawn(async move { @@ -38,7 +38,7 @@ async fn client_prefers_fixed_v2_alpn() { }); let raw = format!("vector://secret@{endpoint}?alpn=private/2&socks=127.0.0.1:1080"); - let (_, _, version) = ClientTls::new(&config(&raw)) + let _ = ClientTls::new(&config(&raw)) .unwrap() .connect_tcp( &endpoint.to_string(), @@ -47,7 +47,6 @@ async fn client_prefers_fixed_v2_alpn() { ) .await .unwrap(); - assert_eq!(version, ProtocolVersion::V2); server_task.await.unwrap().unwrap(); } @@ -88,7 +87,7 @@ enum TestPin { Invalid, } -async fn test_pinned_handshake(pin: TestPin, sni: Option<&str>) -> Result { +async fn test_pinned_handshake(pin: TestPin, sni: Option<&str>) -> Result<()> { let generated = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap(); let certificate: CertificateDer<'static> = generated.cert.into(); let fingerprint = certificate_sha256(&certificate); @@ -102,7 +101,7 @@ async fn test_pinned_handshake(pin: TestPin, sni: Option<&str>) -> Result) -> Result = generated.cert.into(); + let key = PrivateKeyDer::from(PrivatePkcs8KeyDer::from( + generated.signing_key.serialize_der(), + )); + let mut server = + rustls::ServerConfig::builder_with_provider(Arc::new(ring::default_provider())) + .with_protocol_versions(&[&rustls::version::TLS13]) + .unwrap() + .with_no_client_auth() + .with_single_cert(vec![certificate], key) + .unwrap(); + server.alpn_protocols = vec![b"now/1".to_vec()]; + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let endpoint = listener.local_addr().unwrap(); + tokio::spawn(async move { + let (stream, _) = listener.accept().await.unwrap(); + let _ = TlsAcceptor::from(Arc::new(server)).accept(stream).await; + }); + let raw = format!("vector://secret@{endpoint}?socks=127.0.0.1:1080"); + assert!( + ClientTls::new(&config(&raw)) + .unwrap() + .connect_tcp( + &endpoint.to_string(), + "auto", + crate::common::AddressFamily::Any + ) + .await + .is_err() ); } -async fn negotiate_quic(server_alpns: Vec>) -> Result { +async fn negotiate_quic(server_alpns: Vec>) -> Result<()> { let generated = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap(); let certificate: CertificateDer<'static> = generated.cert.into(); let key = PrivateKeyDer::from(PrivatePkcs8KeyDer::from( @@ -177,24 +203,16 @@ async fn negotiate_quic(server_alpns: Vec>) -> Result { let mut client = quinn::Endpoint::client("127.0.0.1:0".parse().unwrap())?; client.set_default_client_config(tls.quic_client_config()?); let connection = client.connect(address, &tls.quic_server_name())?.await?; - let version = quic_protocol_version(&connection)?; + require_quic_nw2(&connection)?; connection.close(quinn::VarInt::from_u32(0), b""); let _ = server_task.await; - Ok(version) + Ok(()) } #[tokio::test] -async fn quic_prefers_v2_and_falls_back_to_default_v1() { - assert_eq!( - negotiate_quic(vec![b"nw2".to_vec(), b"now/1".to_vec()]) - .await - .unwrap(), - ProtocolVersion::V2 - ); - assert_eq!( - negotiate_quic(vec![b"now/1".to_vec()]).await.unwrap(), - ProtocolVersion::V1 - ); +async fn quic_requires_nw2() { + negotiate_quic(vec![b"nw2".to_vec()]).await.unwrap(); + assert!(negotiate_quic(vec![b"now/1".to_vec()]).await.is_err()); assert!(negotiate_quic(vec![b"private/2".to_vec()]).await.is_err()); } diff --git a/src/tui/client/adapter.rs b/src/tui/client/adapter.rs index d5d1762..21f8c17 100644 --- a/src/tui/client/adapter.rs +++ b/src/tui/client/adapter.rs @@ -138,7 +138,6 @@ pub(super) fn access_start_ui_value(value: AccessStarted) -> AccessRecord { TrafficProtocol::Udp => "UDP", } .to_owned(), - wire_version: value.wire_version.map(|version| version.to_string()), session_tag: value.session_tag, client: value.client, path_peers: value.path_peers, @@ -161,7 +160,6 @@ pub(super) fn access_finish_ui_value( client, path_peers, target, - wire_version, session_tag, initial_uplink, initial_downlink, @@ -176,7 +174,6 @@ pub(super) fn access_finish_ui_value( id, timestamp_ms, protocol, - wire_version, flow_id, session_tag, client, @@ -189,7 +186,6 @@ pub(super) fn access_finish_ui_value( }); record.timestamp_ms = timestamp_ms; record.event_id = id; - record.wire_version = wire_version.map(|version| version.to_string()); record.phase = AccessPhase::Finish; let benign_end = error.as_deref().is_some_and(is_benign_access_end); record.status = Some(match outcome { diff --git a/src/tui/model/filter.rs b/src/tui/model/filter.rs index 09ff684..5ab38ed 100644 --- a/src/tui/model/filter.rs +++ b/src/tui/model/filter.rs @@ -12,10 +12,6 @@ pub fn access_matches(record: &AccessRecord, lowercase_filter: &str) -> bool { .to_ascii_lowercase() .contains(lowercase_filter) || record.route.to_ascii_lowercase().contains(lowercase_filter) - || record - .wire_version - .as_deref() - .is_some_and(|value| value.contains(lowercase_filter)) || record .client .as_deref() diff --git a/src/tui/model/types.rs b/src/tui/model/types.rs index cd6038a..7e6066c 100644 --- a/src/tui/model/types.rs +++ b/src/tui/model/types.rs @@ -133,7 +133,6 @@ pub struct AccessRecord { pub event_id: u64, pub phase: AccessPhase, pub protocol: String, - pub wire_version: Option, pub session_tag: Option, pub client: Option, pub path_peers: Vec, diff --git a/src/tui/render/feed.rs b/src/tui/render/feed.rs index 81161d5..f3d22f6 100644 --- a/src/tui/render/feed.rs +++ b/src/tui/render/feed.rs @@ -127,18 +127,6 @@ fn access_line(record: &AccessRecord, app: &App) -> Line<'static> { format!("{:<4}", record.protocol.to_ascii_uppercase()), accent(app, palette::protocol(&record.protocol)), ), - Span::styled( - format!( - "{:<3}", - record - .wire_version - .as_deref() - .unwrap_or("—") - .to_ascii_uppercase() - ), - dim(app), - ), - Span::raw(" "), Span::styled(format!("{status:<4}"), accent(app, status_color)), Span::raw(format!( " {} {} {}", diff --git a/src/vector/flow.rs b/src/vector/flow.rs index 8ab8c53..764559e 100644 --- a/src/vector/flow.rs +++ b/src/vector/flow.rs @@ -25,8 +25,8 @@ use crate::common::{ }; use crate::protocol::{ AUTH_FRAME_LEN, AuthFrame, Carrier, FLOW_HEADER_LEN, FlowHeader, FlowKind, FlowResult, - FlowRole, ProtocolVersion, SetupResult, TARGET_MAX_ENCODED_LEN, Target, encode_target_into, - read_flow_result, write_flow_header, + FlowRole, SetupResult, TARGET_MAX_ENCODED_LEN, Target, encode_target_into, read_flow_result, + write_flow_header, }; use crate::telemetry::{AccessOutcome, AccessSpan, RuntimeEvent, RuntimeKind, RuntimeLevel}; @@ -51,7 +51,6 @@ pub(super) struct PhysicalLane { _link: Option, _latency: Option, pub(super) _quic: Option>, - pub(super) version: ProtocolVersion, } impl PhysicalLane { @@ -105,7 +104,7 @@ pub(super) async fn open_lane( error })?; match opened { - OpenedTls::Mux(stream, version) => { + OpenedTls::Mux(stream) => { let (reader, writer) = stream.into_split(); Ok(PhysicalLane { reader: Some(Box::pin(reader)), @@ -115,7 +114,6 @@ pub(super) async fn open_lane( _link: None, _latency: None, _quic: None, - version, }) } OpenedTls::Dedicated(lane) => { @@ -128,7 +126,6 @@ pub(super) async fn open_lane( _link: Some(parts.link), _latency: Some(parts.latency), _quic: None, - version: parts.version, }) } } @@ -157,7 +154,6 @@ pub(super) async fn open_lane( } }; let pending_quic_auth = pending_auth.is_some(); - let version = session.version; Ok(PhysicalLane { reader: Some(Box::pin(reader)), writer: Some(Box::pin(writer)), @@ -166,7 +162,6 @@ pub(super) async fn open_lane( _link: None, _latency: None, _quic: Some(session), - version, }) } } @@ -188,14 +183,7 @@ pub(super) async fn prepare_lanes( open_lane(client, route.downlink, flow_id, MuxDirection::Down), ); match (uplink, downlink) { - (Ok(uplink), Ok(downlink)) if uplink.version == downlink.version => { - Ok(vec![uplink, downlink]) - } - (Ok(uplink), Ok(downlink)) => Err(anyhow!( - "split carriers negotiated different protocol versions: uplink={}, downlink={}", - uplink.version, - downlink.version, - )), + (Ok(uplink), Ok(downlink)) => Ok(vec![uplink, downlink]), (Err(error), Ok(downlink)) => { drop(downlink); Err(error) diff --git a/src/vector/flow/tcp.rs b/src/vector/flow/tcp.rs index 89ee949..01b5579 100644 --- a/src/vector/flow/tcp.rs +++ b/src/vector/flow/tcp.rs @@ -13,7 +13,6 @@ pub(crate) struct TcpTunnel { _lease: Option, uplink: Carrier, downlink: Carrier, - version: ProtocolVersion, _flow_permit: Option, } @@ -32,10 +31,6 @@ impl TcpTunnel { (self.uplink, self.downlink) } - pub(crate) fn protocol_version(&self) -> ProtocolVersion { - self.version - } - pub(crate) fn into_parts(self) -> (BoxReader, BoxWriter, TcpTunnelGuard) { let Self { reader, @@ -44,7 +39,6 @@ impl TcpTunnel { _lease, uplink: _, downlink: _, - version: _, _flow_permit, } = self; ( @@ -130,7 +124,6 @@ pub(crate) async fn open_tcp( downlink, hops, }; - let version = lane.version; let pending_auth = lane.take_pending_auth(); write_open_request( lane.writer.as_mut().expect("lane writer"), @@ -153,14 +146,12 @@ pub(crate) async fn open_tcp( _lease: Some(lease), uplink, downlink, - version, _flow_permit: Some(flow_permit), }); } let mut downlink_lane = lanes.pop().expect("downlink lane"); let mut uplink_lane = lanes.pop().expect("uplink lane"); - let version = uplink_lane.version; let open_header = FlowHeader { role: FlowRole::Open, flow_id, @@ -205,7 +196,6 @@ pub(crate) async fn open_tcp( _lease: Some(lease), uplink, downlink, - version, _flow_permit: Some(flow_permit), }) } diff --git a/src/vector/session.rs b/src/vector/session.rs index c3b6224..2656587 100644 --- a/src/vector/session.rs +++ b/src/vector/session.rs @@ -27,14 +27,14 @@ use crate::common::{ use crate::mux::{MUX_IDLE_TIMEOUT, MuxConfig, MuxHandle, MuxStream}; use crate::protocol::{ AuthFrame, AuthKey, AuthTransport, Credentials, DatagramReassembler, FlowId, OwnedUdpFragment, - OwnedUdpFrame, ProtocolVersion, ReassemblyConfig, ReassemblyOutcome, SessionId, - decode_udp_frame_owned, encode_auth_frame, encode_udp_close, + OwnedUdpFrame, ReassemblyConfig, ReassemblyOutcome, SessionId, decode_udp_frame_owned, + encode_auth_frame, encode_udp_close, }; use crate::telemetry::{RuntimeEvent, RuntimeKind, RuntimeLevel, TelemetryHub}; use crate::transport::{Stats, transport_flow_control}; use super::config::PortalClientConfig; -use super::tls::{ClientTls, EXPORTER_LABEL, quic_protocol_version}; +use super::tls::{ClientTls, EXPORTER_LABEL, require_quic_nw2}; const QUIC_DATAGRAM_BUFFER_SIZE: usize = 4 * 1024 * 1024; const TLS_MUX_MAX_SHARDS_PER_DIRECTION: usize = 4; @@ -62,7 +62,6 @@ impl ClientSignals { pub(super) struct TlsLane { pub(super) stream: TlsStream, - pub(super) version: ProtocolVersion, pending_auth: Option, _link: LinkGuard, latency: LatencyGuard, @@ -74,14 +73,12 @@ pub(super) struct TlsLaneParts { pub(super) pending_auth: Option, pub(super) link: LinkGuard, pub(super) latency: LatencyGuard, - pub(super) version: ProtocolVersion, } impl TlsLane { pub(super) fn into_parts(self) -> TlsLaneParts { let Self { stream, - version, pending_auth, _link, latency, @@ -93,7 +90,6 @@ impl TlsLane { pending_auth, link: _link, latency, - version, } } } diff --git a/src/vector/session/quic.rs b/src/vector/session/quic.rs index 27b13e0..e1d5695 100644 --- a/src/vector/session/quic.rs +++ b/src/vector/session/quic.rs @@ -137,7 +137,7 @@ impl QuicManager { .await .map_err(|_| anyhow!("vector::session::QuicManager: QUIC handshake timeout"))? .context("vector::session::QuicManager: QUIC handshake failed")?; - let version = quic_protocol_version(&connection)?; + require_quic_nw2(&connection)?; let mut exporter = [0u8; crate::protocol::TLS_EXPORTER_LEN]; connection .export_keying_material(&mut exporter, EXPORTER_LABEL, b"") @@ -168,7 +168,6 @@ impl QuicManager { queue_budget: Arc::new(Semaphore::new(self.queue_bytes)), _link: LinkGuard::new(self.stats.clone(), self.telemetry.clone(), true), latency, - version, }); spawn_datagram_loop(Arc::downgrade(&session), self.shutdown.clone()); Ok(session) @@ -197,7 +196,6 @@ pub(in crate::vector) struct QuicSession { queue_budget: Arc, _link: LinkGuard, latency: LatencyGuard, - pub(in crate::vector) version: ProtocolVersion, } pub(in crate::vector) type QueuedDatagram = BudgetedDatagram; diff --git a/src/vector/session/tls.rs b/src/vector/session/tls.rs index d0d8565..5c553a0 100644 --- a/src/vector/session/tls.rs +++ b/src/vector/session/tls.rs @@ -27,13 +27,12 @@ pub(in crate::vector) enum MuxDirection { pub(in crate::vector) enum OpenedTls { Dedicated(Box), - Mux(MuxStream, ProtocolVersion), + Mux(MuxStream), } #[derive(Clone)] struct TlsMux { handle: MuxHandle, - version: ProtocolVersion, target_density: usize, } @@ -85,14 +84,13 @@ impl TlsManager { .handle .open_stream(flow_id) .await - .map(|stream| OpenedTls::Mux(stream, shard.version)) + .map(OpenedTls::Mux) .map_err(Into::into); } let connect_started = Instant::now(); let lane = self.connect_lane().await?; let TlsLane { mut stream, - version, pending_auth, _link, latency, @@ -110,7 +108,6 @@ impl TlsManager { drop(incoming); let shard = TlsMux { handle, - version, target_density: mux_target_density(connect_started.elapsed()), }; self.mux(direction).lock().await.push(shard.clone()); @@ -125,7 +122,7 @@ impl TlsManager { .handle .open_stream(flow_id) .await - .map(|stream| OpenedTls::Mux(stream, version)) + .map(OpenedTls::Mux) .map_err(Into::into) } @@ -189,7 +186,7 @@ impl TlsManager { .endpoint .as_ref() .ok_or_else(|| anyhow!("vector::session::TlsManager: TCP carrier is not configured"))?; - let (stream, exporter, version) = self + let (stream, exporter) = self .tls .connect_tcp(endpoint, &self.dialer_ip, *family) .await?; @@ -203,7 +200,6 @@ impl TlsManager { ); Ok(TlsLane { stream, - version, pending_auth: Some(auth), _link: LinkGuard::new(self.stats.clone(), self.telemetry.clone(), false), latency, diff --git a/src/vector/socks/server.rs b/src/vector/socks/server.rs index 9014727..cf4ebdc 100644 --- a/src/vector/socks/server.rs +++ b/src/vector/socks/server.rs @@ -172,7 +172,7 @@ async fn handle_client( .await?; return Ok(()); } - let mut access = start_access( + let access = start_access( &vector, TrafficProtocol::Tcp, Some(peer.to_string()), @@ -181,7 +181,6 @@ async fn handle_client( let target = to_target(&request.address)?; match open_tcp(vector.client.clone(), &target, 0).await { Ok(tunnel) => { - access.set_wire_version(tunnel.protocol_version()); let reply = tunnel.socks_reply(); write_reply(&mut stream, reply, &SocksAddress::unspecified()).await?; tokio::select! { diff --git a/src/vector/socks/server/udp.rs b/src/vector/socks/server/udp.rs index 4aaf496..ce80f2f 100644 --- a/src/vector/socks/server/udp.rs +++ b/src/vector/socks/server/udp.rs @@ -146,7 +146,7 @@ async fn open_and_relay_udp_target( .lock() .unwrap_or_else(|lock| lock.into_inner()) .map(|endpoint| endpoint.to_string()); - let mut access = start_access(&vector, TrafficProtocol::Udp, source, &target); + let access = start_access(&vector, TrafficProtocol::Udp, source, &target); let protocol_target = match to_target(&target) { Ok(target) => target, Err(error) => { @@ -167,7 +167,6 @@ async fn open_and_relay_udp_target( } }, }; - access.set_wire_version(tunnel.protocol_version()); relay_udp_target( vector, UdpClientSide { @@ -354,7 +353,6 @@ pub(super) fn start_access( id: 0, timestamp_ms: now_unix_ms(), protocol, - wire_version: None, flow_id: None, session_tag: None, client, diff --git a/src/vector/tls.rs b/src/vector/tls.rs index f067034..a21bddf 100644 --- a/src/vector/tls.rs +++ b/src/vector/tls.rs @@ -22,7 +22,7 @@ use tokio_rustls::{TlsConnector, client::TlsStream}; use crate::common::{ AddressFamily, certificate_sha256, dial_tcp_from_local_ip_family, handshake_timeout, }; -use crate::protocol::{ProtocolVersion, SUPPORTED_ALPNS, TLS_EXPORTER_LEN, TlsExporter}; +use crate::protocol::{ALPN, TLS_EXPORTER_LEN, TlsExporter}; use super::config::PortalClientConfig; @@ -77,7 +77,7 @@ impl ClientTls { })) .with_no_client_auth() }; - client.alpn_protocols = SUPPORTED_ALPNS.iter().map(|alpn| alpn.to_vec()).collect(); + client.alpn_protocols = vec![ALPN.to_vec()]; client.enable_early_data = false; let server_name = @@ -108,7 +108,7 @@ impl ClientTls { endpoint: &str, dialer_ip: &str, family: AddressFamily, - ) -> Result<(TlsStream, TlsExporter, ProtocolVersion)> { + ) -> Result<(TlsStream, TlsExporter)> { let stream = dial_tcp_from_local_ip_family(dialer_ip, endpoint, handshake_timeout(), family) .await @@ -124,27 +124,35 @@ impl ClientTls { .await .map_err(|_| anyhow!("vector::tls::connect_tcp: TLS handshake timeout"))? .context("vector::tls::connect_tcp: TLS handshake failed")?; + require_nw2(tls.get_ref().1.alpn_protocol()) + .context("vector::tls::connect_tcp: invalid negotiated protocol")?; let mut exporter = [0u8; TLS_EXPORTER_LEN]; tls.get_ref() .1 .export_keying_material(&mut exporter, EXPORTER_LABEL, Some(&[])) .context("vector::tls::connect_tcp: TLS exporter failed")?; - let version = ProtocolVersion::from_alpn(tls.get_ref().1.alpn_protocol()) - .context("vector::tls::connect_tcp: invalid negotiated protocol")?; - Ok((tls, exporter, version)) + Ok((tls, exporter)) } } -pub(super) fn quic_protocol_version(connection: &Connection) -> Result { +pub(super) fn require_quic_nw2(connection: &Connection) -> Result<()> { let handshake = connection .handshake_data() .ok_or_else(|| anyhow!("vector::tls: QUIC handshake data unavailable"))? .downcast::() .map_err(|_| anyhow!("vector::tls: unexpected QUIC handshake data"))?; - ProtocolVersion::from_alpn(handshake.protocol.as_deref()) + require_nw2(handshake.protocol.as_deref()) .context("vector::tls: invalid QUIC negotiated protocol") } +fn require_nw2(alpn: Option<&[u8]>) -> Result<()> { + match alpn { + Some(ALPN) => Ok(()), + Some(_) => bail!("unsupported negotiated ALPN"), + None => bail!("peer did not negotiate ALPN"), + } +} + #[derive(Clone)] struct PinnedCertificateVerification { pin: String, diff --git a/src/vector/udp_flow.rs b/src/vector/udp_flow.rs index b450c00..bf664a4 100644 --- a/src/vector/udp_flow.rs +++ b/src/vector/udp_flow.rs @@ -14,9 +14,7 @@ use tokio::sync::mpsc; use tokio::time::timeout; use crate::common::{UdpDatagramSend, handshake_timeout}; -use crate::protocol::{ - Carrier, FlowHeader, FlowKind, FlowRole, ProtocolVersion, Target, write_udp_packet, -}; +use crate::protocol::{Carrier, FlowHeader, FlowKind, FlowRole, Target, write_udp_packet}; use super::PortalClient; use super::flow::{ @@ -31,7 +29,6 @@ pub(crate) struct UdpTunnel { quic: Option>, pub(super) uplink: Carrier, pub(super) downlink: Carrier, - version: ProtocolVersion, sender: UdpTunnelSender, receiver: UdpTunnelReceiver, _lanes: Vec, @@ -44,9 +41,6 @@ impl UdpTunnel { pub(crate) fn carriers(&self) -> (Carrier, Carrier) { (self.uplink, self.downlink) } - pub(crate) fn protocol_version(&self) -> ProtocolVersion { - self.version - } pub(crate) async fn send(&mut self, payload: &[u8]) -> Result { self.sender.send(payload).await } @@ -222,7 +216,6 @@ pub(crate) async fn open_udp( quic, mut down_datagrams, } = prepared; - let version = lanes[0].version; if let Err(error) = setup_udp_lanes(&mut lanes, flow_id, route, target, hops).await { if let Some(quic) = &quic { quic.remove_udp(flow_id); @@ -259,7 +252,6 @@ pub(crate) async fn open_udp( flow_id, uplink, downlink, - version, sender: UdpTunnelSender { flow_id, writer, From ba3037543cb2a53f9b4698616dd1ebd5c3ca011d Mon Sep 17 00:00:00 2001 From: yosebyte Date: Tue, 8 Sep 2026 11:34:55 +0800 Subject: [PATCH 25/62] Add packet-level TLS Mux benchmark matrix --- tests/bench-mux-matrix.py | 106 +++++++++++++++++++++++ tests/bench-mux-netem.sh | 64 ++++++++++++++ tests/mux-bench-worker.py | 172 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 342 insertions(+) create mode 100755 tests/bench-mux-matrix.py create mode 100755 tests/bench-mux-netem.sh create mode 100755 tests/mux-bench-worker.py diff --git a/tests/bench-mux-matrix.py b/tests/bench-mux-matrix.py new file mode 100755 index 0000000..48528b3 --- /dev/null +++ b/tests/bench-mux-matrix.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +"""Run the fixed RTT/flow matrix and enforce throughput/RSS regression limits.""" + +import argparse +import json +import statistics +import subprocess +from pathlib import Path + + +RTTS = (0, 30, 100, 200, 300) +FLOWS = (1, 4, 16) + + +def mib_per_flow(rtt, flows): + if flows == 1: + return 32 if rtt == 300 else 64 + if flows == 4: + return 16 + return 8 + + +def sample(script, binary, rtt, flows, loss=0): + output = subprocess.check_output( + [ + str(script), + str(rtt), + str(flows), + str(mib_per_flow(rtt, flows)), + "1", + str(binary), + str(loss), + ], + text=True, + ) + return json.loads(output) + + +def median_sample(samples): + keys = ("mbps", "portal_peak_rss_mib", "vector_peak_rss_mib") + result = {key: statistics.median(item[key] for item in samples) for key in keys} + result["samples"] = samples + return result + + +def run_matrix(script, binary, repeats): + return { + f"{rtt}ms/{flows}": median_sample( + [sample(script, binary, rtt, flows) for _ in range(repeats)] + ) + for rtt in RTTS + for flows in FLOWS + } + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--baseline", required=True) + parser.add_argument("--current", required=True) + parser.add_argument("--repeats", type=int, default=3) + parser.add_argument("--output") + parser.add_argument("--skip-loss-observation", action="store_true") + args = parser.parse_args() + if args.repeats < 3: + parser.error("--repeats must be at least 3") + script = Path(__file__).with_name("bench-mux-netem.sh").resolve() + baseline = run_matrix(script, Path(args.baseline).resolve(), args.repeats) + current = run_matrix(script, Path(args.current).resolve(), args.repeats) + failures = [] + for cell, before in baseline.items(): + after = current[cell] + if after["mbps"] < before["mbps"] * 0.97: + failures.append( + f"{cell}: throughput {after['mbps']:.2f} < 97% of {before['mbps']:.2f}" + ) + for process in ("portal", "vector"): + key = f"{process}_peak_rss_mib" + limit = before[key] + max(before[key] * 0.05, 2.0) + if after[key] > limit: + failures.append( + f"{cell}: {process} RSS {after[key]:.2f} MiB > {limit:.2f} MiB" + ) + loss = {} + if not args.skip_loss_observation: + for rtt, flows in ((100, 1), (300, 16)): + cell = f"{rtt}ms/{flows}" + loss[cell] = median_sample( + [sample(script, Path(args.current).resolve(), rtt, flows, 0.1) + for _ in range(args.repeats)] + ) + report = { + "baseline": baseline, + "current": current, + "loss_0_1_percent_observation": loss, + "failures": failures, + } + encoded = json.dumps(report, indent=2, sort_keys=True) + if args.output: + Path(args.output).write_text(encoded + "\n", encoding="utf-8") + print(encoded) + if failures: + raise SystemExit(1) + + +if __name__ == "__main__": + main() diff --git a/tests/bench-mux-netem.sh b/tests/bench-mux-netem.sh new file mode 100755 index 0000000..0e0f230 --- /dev/null +++ b/tests/bench-mux-netem.sh @@ -0,0 +1,64 @@ +#!/bin/sh +# Packet-level RTT/loss and RSS benchmark for a Linux host with iproute2. +set -eu + +rtt_ms=${1:-100} +flows=${2:-1} +mib_per_flow=${3:-64} +mux=${4:-1} +binary=${5:-target/release/nowhere} +loss=${6:-0} + +[ "$(uname -s)" = Linux ] || { echo "Linux is required" >&2; exit 2; } +[ "$(id -u)" -eq 0 ] || { echo "root is required for network namespaces" >&2; exit 2; } +command -v ip >/dev/null +command -v tc >/dev/null + +root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +binary=$(realpath "$binary") +worker="$root/tests/mux-bench-worker.py" +suffix=$$ +portal_ns=nwp-$suffix +vector_ns=nwv-$suffix +portal_pid= +vector_pid= +target_pid= + +cleanup() { + [ -z "$vector_pid" ] || kill "$vector_pid" 2>/dev/null || true + [ -z "$portal_pid" ] || kill "$portal_pid" 2>/dev/null || true + [ -z "$target_pid" ] || kill "$target_pid" 2>/dev/null || true + ip netns delete "$vector_ns" 2>/dev/null || true + ip netns delete "$portal_ns" 2>/dev/null || true +} +trap cleanup EXIT INT TERM + +ip netns add "$portal_ns" +ip netns add "$vector_ns" +ip link add nwpv-$suffix type veth peer name nwvp-$suffix +ip link set nwpv-$suffix netns "$portal_ns" +ip link set nwvp-$suffix netns "$vector_ns" +ip -n "$portal_ns" addr add 10.203.0.1/30 dev nwpv-$suffix +ip -n "$vector_ns" addr add 10.203.0.2/30 dev nwvp-$suffix +ip -n "$portal_ns" link set lo up +ip -n "$vector_ns" link set lo up +ip -n "$portal_ns" link set nwpv-$suffix up +ip -n "$vector_ns" link set nwvp-$suffix up + +one_way=$(awk -v rtt="$rtt_ms" 'BEGIN { printf "%.3f", rtt / 2 }') +ip netns exec "$portal_ns" tc qdisc add dev nwpv-$suffix root netem delay "${one_way}ms" loss "${loss}%" +ip netns exec "$vector_ns" tc qdisc add dev nwvp-$suffix root netem delay "${one_way}ms" loss "${loss}%" + +ip netns exec "$portal_ns" python3 "$worker" target --port 19000 & +target_pid=$! +ip netns exec "$portal_ns" env NOW_TRANSPORT_MEMORY_PROFILE=throughput "$binary" 'portal://secret@10.203.0.1/tcp:2000?log=none' & +portal_pid=$! +ip netns exec "$portal_ns" python3 "$worker" wait --port 2000 + +ip netns exec "$vector_ns" env NOW_TRANSPORT_MEMORY_PROFILE=throughput "$binary" "vector://secret@10.203.0.1/tcp:2000?mux=$mux&socks=127.0.0.1:1080&log=none" & +vector_pid=$! +ip netns exec "$vector_ns" python3 "$worker" wait --port 1080 + +bytes=$((mib_per_flow * 1024 * 1024)) +result=$(ip netns exec "$vector_ns" python3 "$worker" client --flows "$flows" --bytes "$bytes" --socks-port 1080 --target-port 19000 --portal-pid "$portal_pid" --vector-pid "$vector_pid") +python3 -c 'import json,sys; d=json.loads(sys.argv[1]); d.update(rtt_ms=float(sys.argv[2]), loss_percent=float(sys.argv[3]), mux=int(sys.argv[4])); print(json.dumps(d, sort_keys=True))' "$result" "$rtt_ms" "$loss" "$mux" diff --git a/tests/mux-bench-worker.py b/tests/mux-bench-worker.py new file mode 100755 index 0000000..e6c8730 --- /dev/null +++ b/tests/mux-bench-worker.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +"""Namespace-local target and load generator for the TLS Mux netem benchmark.""" + +import argparse +import json +import socket +import struct +import threading +import time + + +def receive_exact(sock, size): + received = 0 + while received < size: + block = sock.recv(min(256 * 1024, size - received)) + if not block: + raise RuntimeError(f"early EOF after {received} bytes") + received += len(block) + + +def target(port): + listener = socket.create_server(("127.0.0.1", port), reuse_port=False) + while True: + conn, _ = listener.accept() + + def serve(stream): + try: + request = b"" + while len(request) < 8: + block = stream.recv(8 - len(request)) + if not block: + return + request += block + remaining = struct.unpack("!Q", request)[0] + payload = bytes(256 * 1024) + while remaining: + count = min(remaining, len(payload)) + stream.sendall(payload[:count]) + remaining -= count + finally: + stream.close() + + threading.Thread(target=serve, args=(conn,), daemon=True).start() + + +def receive_socks_reply(sock): + head = sock.recv(4) + if len(head) != 4 or head[1] != 0: + raise RuntimeError(f"SOCKS CONNECT failed: {head!r}") + lengths = {1: 4, 4: 16} + length = lengths.get(head[3]) + if head[3] == 3: + length = receive_one(sock) + if length is None: + raise RuntimeError("invalid SOCKS address type") + receive_exact(sock, length + 2) + + +def receive_one(sock): + value = sock.recv(1) + if len(value) != 1: + raise RuntimeError("early SOCKS EOF") + return value[0] + + +def rss_kib(pid, field): + try: + with open(f"/proc/{pid}/status", encoding="ascii") as status: + for line in status: + if line.startswith(field + ":"): + return int(line.split()[1]) + except (FileNotFoundError, ProcessLookupError): + pass + return 0 + + +def client(args): + barrier = threading.Barrier(args.flows) + durations = [None] * args.flows + peak = {"portal": 0, "vector": 0} + sampling = threading.Event() + sampling.set() + + def monitor(): + while sampling.is_set(): + peak["portal"] = max(peak["portal"], rss_kib(args.portal_pid, "VmRSS")) + peak["vector"] = max(peak["vector"], rss_kib(args.vector_pid, "VmRSS")) + time.sleep(0.01) + + def flow(index): + try: + sock = socket.create_connection(("127.0.0.1", args.socks_port), timeout=60) + sock.sendall(b"\x05\x01\x00") + if sock.recv(2) != b"\x05\x00": + raise RuntimeError("SOCKS authentication failed") + sock.sendall( + b"\x05\x01\x00\x01\x7f\x00\x00\x01" + + struct.pack("!H", args.target_port) + ) + receive_socks_reply(sock) + barrier.wait(timeout=60) + started = time.monotonic() + sock.sendall(struct.pack("!Q", args.bytes)) + receive_exact(sock, args.bytes) + durations[index] = time.monotonic() - started + sock.close() + except Exception as error: + durations[index] = str(error) + barrier.abort() + + monitor_thread = threading.Thread(target=monitor, daemon=True) + monitor_thread.start() + threads = [threading.Thread(target=flow, args=(index,)) for index in range(args.flows)] + for thread in threads: + thread.start() + for thread in threads: + thread.join() + sampling.clear() + monitor_thread.join() + errors = [value for value in durations if not isinstance(value, float)] + if errors: + raise RuntimeError(str(errors)) + peak["portal"] = max(peak["portal"], rss_kib(args.portal_pid, "VmHWM")) + peak["vector"] = max(peak["vector"], rss_kib(args.vector_pid, "VmHWM")) + elapsed = max(durations) + total = args.bytes * args.flows + print(json.dumps({ + "flows": args.flows, + "mib": total / 1024 / 1024, + "seconds": round(elapsed, 3), + "mbps": round(total * 8 / elapsed / 1_000_000, 2), + "portal_peak_rss_mib": round(peak["portal"] / 1024, 2), + "vector_peak_rss_mib": round(peak["vector"] / 1024, 2), + }, sort_keys=True)) + + +def wait_port(port): + deadline = time.monotonic() + 10 + while time.monotonic() < deadline: + try: + with socket.create_connection(("127.0.0.1", port), timeout=0.2): + return + except OSError: + time.sleep(0.05) + raise RuntimeError(f"port {port} did not open") + + +def main(): + parser = argparse.ArgumentParser() + sub = parser.add_subparsers(dest="role", required=True) + target_parser = sub.add_parser("target") + target_parser.add_argument("--port", type=int, required=True) + wait_parser = sub.add_parser("wait") + wait_parser.add_argument("--port", type=int, required=True) + client_parser = sub.add_parser("client") + client_parser.add_argument("--flows", type=int, required=True) + client_parser.add_argument("--bytes", type=int, required=True) + client_parser.add_argument("--socks-port", type=int, required=True) + client_parser.add_argument("--target-port", type=int, required=True) + client_parser.add_argument("--portal-pid", type=int, required=True) + client_parser.add_argument("--vector-pid", type=int, required=True) + args = parser.parse_args() + if args.role == "target": + target(args.port) + elif args.role == "wait": + wait_port(args.port) + else: + client(args) + + +if __name__ == "__main__": + main() From 17905d0898b53a1596f84d4fdbeb96d0cc4d4ba2 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Tue, 8 Sep 2026 11:37:44 +0800 Subject: [PATCH 26/62] Record nw2 Mux smoke benchmark --- docs/mux-performance.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/mux-performance.md b/docs/mux-performance.md index 1bdc945..6cffbfd 100644 --- a/docs/mux-performance.md +++ b/docs/mux-performance.md @@ -103,3 +103,20 @@ The current Mux header remains 8 bytes but uses separate OPEN, DATA, WINDOW, and CLOSE kinds. It is wire-incompatible with every earlier development Mux format. Historical results above remain useful as performance baselines; they do not demonstrate current wire interoperability. + +## 2026-09-08 nw2 frame smoke comparison + +The macOS Toxiproxy runner compared the saved `cc64358` binary with the +OPEN/DATA/WINDOW/CLOSE implementation. Each cell is the median of three serial +runs using the throughput profile. + +| Implementation | RTT | Flows | Payload | Throughput | Portal peak RSS | Vector peak RSS | +|---|---:|---:|---:|---:|---:|---:| +| `cc64358` | 100 ms | 1 | 64 MiB | 900.17 Mbps | 21.53 MiB | 9.78 MiB | +| nw2 frames | 100 ms | 1 | 64 MiB | 918.02 Mbps | 22.16 MiB | 9.98 MiB | +| `cc64358` | 300 ms | 16 | 128 MiB | 1.690 Gbps | 69.77 MiB | 13.11 MiB | +| nw2 frames | 300 ms | 16 | 128 MiB | 1.689 Gbps | 69.20 MiB | 12.61 MiB | + +Both smoke cells satisfy the 3% throughput and RSS thresholds. These results +exercise the real binaries and carrier topology, but Toxiproxy's TCP +termination means the Linux netem matrix remains the gating measurement. From 39906bbcb3f8f873d80940a345c66d425ea188df Mon Sep 17 00:00:00 2001 From: yosebyte Date: Tue, 8 Sep 2026 11:38:53 +0800 Subject: [PATCH 27/62] Keep Mux benchmark tooling under tests --- docs/mux-performance.md | 2 +- {scripts => tests}/bench-mux-local.sh | 2 +- scripts/mux-bench.py => tests/mux-bench-local.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename {scripts => tests}/bench-mux-local.sh (98%) rename scripts/mux-bench.py => tests/mux-bench-local.py (99%) diff --git a/docs/mux-performance.md b/docs/mux-performance.md index 6cffbfd..3043ce0 100644 --- a/docs/mux-performance.md +++ b/docs/mux-performance.md @@ -7,7 +7,7 @@ emulation. ```sh cargo build --release --locked -scripts/bench-mux-local.sh 100 1 64 1 +tests/bench-mux-local.sh 100 1 64 1 ``` Hard throughput and RSS comparisons use `tests/bench-mux-netem.sh` on a Linux diff --git a/scripts/bench-mux-local.sh b/tests/bench-mux-local.sh similarity index 98% rename from scripts/bench-mux-local.sh rename to tests/bench-mux-local.sh index 1a8f31e..8d33248 100755 --- a/scripts/bench-mux-local.sh +++ b/tests/bench-mux-local.sh @@ -51,7 +51,7 @@ container exec "$container_name" /toxiproxy-cli toxic add \ container exec "$container_name" /toxiproxy-cli toxic add \ -n latency_downstream -t latency -a "latency=$one_way_ms" -d nowhere >/dev/null -python3 scripts/mux-bench.py \ +python3 tests/mux-bench-local.py \ --binary "$binary" \ --rtt-ms "$rtt_ms" \ --flows "$flows" \ diff --git a/scripts/mux-bench.py b/tests/mux-bench-local.py similarity index 99% rename from scripts/mux-bench.py rename to tests/mux-bench-local.py index a72c772..5ec329a 100755 --- a/scripts/mux-bench.py +++ b/tests/mux-bench-local.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Run an end-to-end TLS Mux throughput/RSS sample through Toxiproxy.""" +"""Run an auxiliary TLS Mux throughput/RSS sample through Toxiproxy.""" import argparse import json From cc5623aaeb8016f479a483d5ebd04ed4572b8d60 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Tue, 8 Sep 2026 11:47:39 +0800 Subject: [PATCH 28/62] Fix Mux shutdown waits and benchmark readiness --- src/mux/driver.rs | 30 +++++++++------ src/mux/handle.rs | 4 +- src/mux/mod.rs | 31 ++++++++++++---- src/tests/mux/runtime.rs | 65 +++++++++++++++++++++++++++++++++ src/tests/portal/conn/tcp.rs | 33 +++++++++++------ src/tests/transport/owned_io.rs | 42 +++++++++++++++++++++ src/transport/owned_io.rs | 46 +---------------------- src/vector/session/tls.rs | 7 ++++ tests/bench-mux-netem.sh | 2 +- tests/mux-bench-worker.py | 7 ++-- 10 files changed, 187 insertions(+), 80 deletions(-) create mode 100644 src/tests/transport/owned_io.rs diff --git a/src/mux/driver.rs b/src/mux/driver.rs index d36fb23..2ccc3f0 100644 --- a/src/mux/driver.rs +++ b/src/mux/driver.rs @@ -45,7 +45,7 @@ pub(super) async fn send_data( } pub(super) async fn run_reader(mut reader: R, shared: Arc) { - let result: io::Result<()> = async { + let operation = async { let mut data_frames = 0_u8; loop { if shared.closed.load(std::sync::atomic::Ordering::Acquire) { @@ -53,7 +53,7 @@ pub(super) async fn run_reader(mut reader: R, shared: Arc< } let mut encoded = [0; HEADER_LEN]; tokio::select! { - _ = shared.closed_notify.notified() => return Ok(()), + _ = shared.closed_notify.cancelled() => return Ok(()), result = reader.read_exact(&mut encoded) => { result?; } } let header = decode_header(&encoded).map_err(invalid)?; @@ -64,7 +64,7 @@ pub(super) async fn run_reader(mut reader: R, shared: Arc< let mut payload = vec![0; payload_len]; if payload_len != 0 { tokio::select! { - _ = shared.closed_notify.notified() => return Ok(()), + _ = shared.closed_notify.cancelled() => return Ok(()), result = reader.read_exact(&mut payload) => { result?; } } } @@ -82,8 +82,12 @@ pub(super) async fn run_reader(mut reader: R, shared: Arc< } } } - } - .await; + }; + let result: io::Result<()> = tokio::select! { + biased; + _ = shared.closed_notify.cancelled() => return, + result = operation => result, + }; if result.is_err() { shared.close(); } @@ -174,7 +178,7 @@ pub(super) async fn run_terminals(shared: Arc, mut terminal_rx: mpsc::Re return; } let flow_id = tokio::select! { - _ = shared.closed_notify.notified() => return, + _ = shared.closed_notify.cancelled() => return, flow_id = terminal_rx.recv() => flow_id, }; let Some(flow_id) = flow_id else { return }; @@ -182,7 +186,7 @@ pub(super) async fn run_terminals(shared: Arc, mut terminal_rx: mpsc::Re continue; }; let sent = tokio::select! { - _ = shared.closed_notify.notified() => return, + _ = shared.closed_notify.cancelled() => return, sent = shared.data_tx.send(Outbound::Frame { header, payload: MuxChunk::empty(), @@ -202,7 +206,7 @@ pub(super) async fn run_writer( let mut control = Vec::with_capacity(8 * 64); let mut headers = Vec::with_capacity(8 * 256); let mut pending_item = None; - let result: io::Result<()> = async { + let operation = async { loop { if shared.closed.load(std::sync::atomic::Ordering::Acquire) { return Ok(()); @@ -212,7 +216,7 @@ pub(super) async fn run_writer( } else { tokio::select! { biased; - _ = shared.closed_notify.notified() => return Ok(()), + _ = shared.closed_notify.cancelled() => return Ok(()), _ = shared.control_notify.notified() => { write_pending_windows(&mut writer, &shared, &mut control).await?; continue; @@ -254,8 +258,12 @@ pub(super) async fn run_writer( } } } - } - .await; + }; + let result: io::Result<()> = tokio::select! { + biased; + _ = shared.closed_notify.cancelled() => return, + result = operation => result, + }; if result.is_err() { shared.close(); } diff --git a/src/mux/handle.rs b/src/mux/handle.rs index 1d21cc4..2127a72 100644 --- a/src/mux/handle.rs +++ b/src/mux/handle.rs @@ -49,7 +49,7 @@ impl MuxHandle { incoming_tx, active_streams_tx, closed: AtomicBool::new(false), - closed_notify: Notify::new(), + closed_notify: tokio_util::sync::CancellationToken::new(), #[cfg(test)] borrowed_write_copies: AtomicUsize::new(0), }); @@ -152,7 +152,7 @@ impl MuxHandle { if self.is_closed() { return; } - self.shared.closed_notify.notified().await; + self.shared.closed_notify.cancelled().await; } } diff --git a/src/mux/mod.rs b/src/mux/mod.rs index d24dd5f..aac4f2b 100644 --- a/src/mux/mod.rs +++ b/src/mux/mod.rs @@ -64,7 +64,14 @@ impl MuxConfig { } } fn validate(self) -> io::Result { - if self.stream_window_bytes < FRAME_BYTES + if self.stream_window_bytes < BASE_STREAM_WINDOW_BYTES + || self.stream_window_bytes > MAX_STREAM_WINDOW_BYTES + || self.connection_window_bytes < BASE_CONNECTION_WINDOW_BYTES + || self.connection_window_bytes > MAX_CONNECTION_WINDOW_BYTES + || !self.stream_window_bytes.is_multiple_of(CREDIT_UNIT_BYTES) + || !self + .connection_window_bytes + .is_multiple_of(CREDIT_UNIT_BYTES) || self.connection_window_bytes < self.stream_window_bytes || self.max_streams == 0 || self.outbound_frames == 0 @@ -138,7 +145,7 @@ struct Shared { incoming_tx: mpsc::Sender, active_streams_tx: watch::Sender, closed: AtomicBool, - closed_notify: Notify, + closed_notify: tokio_util::sync::CancellationToken, #[cfg(test)] borrowed_write_copies: AtomicUsize, } @@ -214,7 +221,7 @@ impl Drop for ReceiveCredit { impl Shared { async fn reserve_terminal(&self) -> io::Result> { tokio::select! { - _ = self.closed_notify.notified() => Err(closed()), + _ = self.closed_notify.cancelled() => Err(closed()), permit = self.terminal_tx.clone().reserve_owned() => permit.map_err(|_| closed()), } } @@ -297,10 +304,15 @@ impl Shared { if self.closed.swap(true, Ordering::AcqRel) { return; } - self.flows.lock().expect("mux flow lock").clear(); + let mut flows = self.flows.lock().expect("mux flow lock"); + for flow in flows.values() { + flow.send_credit.close(); + } + flows.clear(); + drop(flows); self.connection_send_credit.close(); self.active_streams_tx.send_replace(0); - self.closed_notify.notify_waiters(); + self.closed_notify.cancel(); } fn send_credit(&self, flow_id: FlowId) -> io::Result> { @@ -315,6 +327,9 @@ impl Shared { fn remove_flow(&self, flow_id: FlowId) -> Option { let mut flows = self.flows.lock().expect("mux flow lock"); let removed = flows.remove(&flow_id); + if let Some(flow) = &removed { + flow.send_credit.close(); + } let active_streams = flows.len(); drop(flows); self.active_streams_tx.send_replace(active_streams); @@ -351,9 +366,6 @@ impl Shared { if self.closed.load(Ordering::Acquire) { return; } - let previous = self - .pending_connection_credit - .fetch_add(charge, Ordering::AcqRel); let (flow_ready, flow_notify) = { let mut connection = self .connection_receive_credit @@ -388,6 +400,9 @@ impl Shared { .expect("mux ready-flow lock") .push_back(flow_id); } + let previous = self + .pending_connection_credit + .fetch_add(charge, Ordering::AcqRel); let threshold = credit_units(self.config.connection_window_bytes / WINDOW_UPDATE_DIVISOR) .min(u16::MAX as usize); if flow_notify || previous.saturating_add(charge) >= threshold { diff --git a/src/tests/mux/runtime.rs b/src/tests/mux/runtime.rs index 906283d..55ef06c 100644 --- a/src/tests/mux/runtime.rs +++ b/src/tests/mux/runtime.rs @@ -327,3 +327,68 @@ async fn concurrent_streams_cross_the_connection_window() { task.await.unwrap(); } } + +#[tokio::test] +async fn closing_carrier_wakes_exhausted_stream_credit() { + let (left, right) = tokio::io::duplex(1024); + let (handle, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + let mut stream = handle.open_stream(501).await.unwrap(); + let credit = handle.shared.send_credit(501).unwrap(); + let permits = credit.available_permits(); + credit + .clone() + .acquire_many_owned(permits as u32) + .await + .unwrap() + .forget(); + let write = tokio::spawn(async move { stream.write_all(b"blocked").await }); + tokio::task::yield_now().await; + assert!(!write.is_finished()); + handle.close(); + assert!( + tokio::time::timeout(Duration::from_secs(1), write) + .await + .unwrap() + .unwrap() + .is_err() + ); + drop(right); +} + +#[tokio::test] +async fn closing_carrier_interrupts_blocked_io_and_releases_shared_state() { + let (left, _right) = tokio::io::duplex(1); + let (handle, incoming) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + let weak = Arc::downgrade(&handle.shared); + tokio::task::yield_now().await; + handle.close(); + drop(handle); + drop(incoming); + tokio::time::timeout(Duration::from_secs(1), async { + while weak.upgrade().is_some() { + tokio::task::yield_now().await; + } + }) + .await + .expect("blocked I/O must release carrier state after close"); +} + +#[test] +fn profiles_outside_wire_window_limits_are_rejected() { + for (stream, connection) in [ + (MIB, 8 * MIB), + (4 * MIB, 4 * MIB), + (32 * MIB, 32 * MIB), + (4 * MIB + 1, 8 * MIB), + ] { + assert!( + MuxConfig { + stream_window_bytes: stream, + connection_window_bytes: connection, + ..MuxConfig::default() + } + .validate() + .is_err() + ); + } +} diff --git a/src/tests/portal/conn/tcp.rs b/src/tests/portal/conn/tcp.rs index f65ea7a..18af6b6 100644 --- a/src/tests/portal/conn/tcp.rs +++ b/src/tests/portal/conn/tcp.rs @@ -56,19 +56,30 @@ async fn portal_rejects_client_hello_without_nw2() { let shutdown = CancellationToken::new(); let child_shutdown = shutdown.clone(); let server_task = tokio::spawn(async move { - let (stream, peer) = listener.accept().await.unwrap(); - let admission = portal_inner - .unauthenticated_admission - .try_acquire(peer.ip()) - .unwrap(); - handle_tcp_incoming(portal_inner, stream, peer, admission, child_shutdown).await; + for _ in 0..3 { + let (stream, peer) = listener.accept().await.unwrap(); + let admission = portal_inner + .unauthenticated_admission + .try_acquire(peer.ip()) + .unwrap(); + handle_tcp_incoming( + portal_inner.clone(), + stream, + peer, + admission, + child_shutdown.clone(), + ) + .await; + } }); - assert!( - connect_test_tls_with_alpns(listen_addr, vec![b"now/1".to_vec()]) - .await - .is_err() - ); + for alpns in [vec![b"now/1".to_vec()], vec![b"private/2".to_vec()], vec![]] { + assert!( + connect_test_tls_with_alpns(listen_addr, alpns) + .await + .is_err() + ); + } shutdown.cancel(); server_task.await.unwrap(); } diff --git a/src/tests/transport/owned_io.rs b/src/tests/transport/owned_io.rs new file mode 100644 index 0000000..283afaa --- /dev/null +++ b/src/tests/transport/owned_io.rs @@ -0,0 +1,42 @@ +use super::*; +use crate::mux::{MuxConfig, MuxHandle}; + +#[tokio::test] +async fn owned_bytes_enter_mux_without_a_borrowed_payload_copy() { + let (left, right) = tokio::io::duplex(1 << 20); + let (client, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + let (_server, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); + let stream = client.open_stream(901).await.unwrap(); + let (_reader, writer) = stream.into_split(); + let mut writer: Pin> = Box::pin(writer); + let mut peer = incoming.accept().await.unwrap().unwrap(); + client.reset_borrowed_write_copies(); + + write_owned( + &mut writer, + RelayChunk::Bytes(Bytes::from_static(b"owned payload")), + ) + .await + .unwrap(); + let mut received = [0; 13]; + peer.read_exact(&mut received).await.unwrap(); + + assert_eq!(&received, b"owned payload"); + assert_eq!(client.borrowed_write_copies(), 0); +} + +#[tokio::test] +async fn generic_reads_use_a_pool_owned_chunk() { + let buffers = Buffers::new(FRAME_BYTES, 1); + let mut reader = &b"pooled"[..]; + let chunk = read_owned_from(&mut reader, &buffers) + .await + .unwrap() + .unwrap(); + + assert_eq!(chunk.as_ref(), b"pooled"); + let allocation = chunk.as_ref().as_ptr(); + drop(chunk); + let reused = buffers.get_tcp_buffer(); + assert_eq!(reused.as_ptr(), allocation); +} diff --git a/src/transport/owned_io.rs b/src/transport/owned_io.rs index 3d039ed..b3198a7 100644 --- a/src/transport/owned_io.rs +++ b/src/transport/owned_io.rs @@ -120,47 +120,5 @@ pub(crate) async fn write_owned_to( } #[cfg(test)] -mod tests { - use super::*; - use crate::mux::{MuxConfig, MuxHandle}; - - #[tokio::test] - async fn owned_bytes_enter_mux_without_a_borrowed_payload_copy() { - let (left, right) = tokio::io::duplex(1 << 20); - let (client, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); - let (_server, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); - let stream = client.open_stream(901).await.unwrap(); - let (_reader, writer) = stream.into_split(); - let mut writer: Pin> = Box::pin(writer); - let mut peer = incoming.accept().await.unwrap().unwrap(); - client.reset_borrowed_write_copies(); - - write_owned( - &mut writer, - RelayChunk::Bytes(Bytes::from_static(b"owned payload")), - ) - .await - .unwrap(); - let mut received = [0; 13]; - peer.read_exact(&mut received).await.unwrap(); - - assert_eq!(&received, b"owned payload"); - assert_eq!(client.borrowed_write_copies(), 0); - } - - #[tokio::test] - async fn generic_reads_use_a_pool_owned_chunk() { - let buffers = Buffers::new(FRAME_BYTES, 1); - let mut reader = &b"pooled"[..]; - let chunk = read_owned_from(&mut reader, &buffers) - .await - .unwrap() - .unwrap(); - - assert_eq!(chunk.as_ref(), b"pooled"); - let allocation = chunk.as_ref().as_ptr(); - drop(chunk); - let reused = buffers.get_tcp_buffer(); - assert_eq!(reused.as_ptr(), allocation); - } -} +#[path = "../tests/transport/owned_io.rs"] +mod tests; diff --git a/src/vector/session/tls.rs b/src/vector/session/tls.rs index 5c553a0..1bea38a 100644 --- a/src/vector/session/tls.rs +++ b/src/vector/session/tls.rs @@ -87,6 +87,13 @@ impl TlsManager { .map(OpenedTls::Mux) .map_err(Into::into); } + if self.mux(direction).lock().await.len() >= TLS_MUX_MAX_SHARDS_PER_DIRECTION { + return Err(std::io::Error::new( + std::io::ErrorKind::WouldBlock, + "TLS mux pool has no stream capacity", + ) + .into()); + } let connect_started = Instant::now(); let lane = self.connect_lane().await?; let TlsLane { diff --git a/tests/bench-mux-netem.sh b/tests/bench-mux-netem.sh index 0e0f230..38e895a 100755 --- a/tests/bench-mux-netem.sh +++ b/tests/bench-mux-netem.sh @@ -53,7 +53,7 @@ ip netns exec "$portal_ns" python3 "$worker" target --port 19000 & target_pid=$! ip netns exec "$portal_ns" env NOW_TRANSPORT_MEMORY_PROFILE=throughput "$binary" 'portal://secret@10.203.0.1/tcp:2000?log=none' & portal_pid=$! -ip netns exec "$portal_ns" python3 "$worker" wait --port 2000 +ip netns exec "$portal_ns" python3 "$worker" wait --port 2000 --host 10.203.0.1 ip netns exec "$vector_ns" env NOW_TRANSPORT_MEMORY_PROFILE=throughput "$binary" "vector://secret@10.203.0.1/tcp:2000?mux=$mux&socks=127.0.0.1:1080&log=none" & vector_pid=$! diff --git a/tests/mux-bench-worker.py b/tests/mux-bench-worker.py index e6c8730..d4626eb 100755 --- a/tests/mux-bench-worker.py +++ b/tests/mux-bench-worker.py @@ -134,11 +134,11 @@ def flow(index): }, sort_keys=True)) -def wait_port(port): +def wait_port(port, host): deadline = time.monotonic() + 10 while time.monotonic() < deadline: try: - with socket.create_connection(("127.0.0.1", port), timeout=0.2): + with socket.create_connection((host, port), timeout=0.2): return except OSError: time.sleep(0.05) @@ -152,6 +152,7 @@ def main(): target_parser.add_argument("--port", type=int, required=True) wait_parser = sub.add_parser("wait") wait_parser.add_argument("--port", type=int, required=True) + wait_parser.add_argument("--host", default="127.0.0.1") client_parser = sub.add_parser("client") client_parser.add_argument("--flows", type=int, required=True) client_parser.add_argument("--bytes", type=int, required=True) @@ -163,7 +164,7 @@ def main(): if args.role == "target": target(args.port) elif args.role == "wait": - wait_port(args.port) + wait_port(args.port, args.host) else: client(args) From f31276f9059f04c2887aecf0938a760954530c00 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Tue, 8 Sep 2026 18:29:40 +0800 Subject: [PATCH 29/62] Rework TLS Mux pooling and stream admission --- docs/configuration.md | 25 ++-- docs/mux-performance.md | 64 ++++++++- docs/operations.md | 40 +++--- docs/protocol.md | 31 +++-- docs/security.md | 10 +- src/mux/driver.rs | 50 ++++--- src/mux/handle.rs | 49 ++++--- src/mux/mod.rs | 64 ++++----- src/mux/stream.rs | 13 +- src/tests/mux/runtime.rs | 116 ++++++++++++++++- src/tests/portal/conn/vector.rs | 37 ++++-- src/tests/vector/session/tls.rs | 198 ++++++++++++++++------------ src/vector/flow.rs | 31 ++--- src/vector/session.rs | 4 +- src/vector/session/tls.rs | 222 ++++++++++++++------------------ tests/mux-bench-local.py | 15 ++- 16 files changed, 588 insertions(+), 381 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 253945e..8209cd2 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -221,11 +221,14 @@ has no health score or circuit breaker. Both carriers must be declared for `mix`; a single-carrier endpoint rejects a policy that selects the absent carrier. -With `mux=1`, Shards open lazily according to active flow pressure. New flows -use the least-loaded shard. TLS setup latency selects a target density of 16, -8, 4, or 2 flows at 30, 75, and 200 ms, while connection-credit or queue -pressure can expand earlier. -Each direction has at most 4 shards. A shard closes after 30 seconds +With `mux=1`, one session shares a pool of at most eight full-duplex TLS carriers. +New flows reuse idle carriers; when all are busy and a slot is available, +they establish another carrier. Establishments may run in parallel and count +against the same eight slots. At capacity, flows choose the lowest credit/queue +occupancy, breaking ties by live streams plus pending reservations. Connecting +carriers also accept reservations to balance cold bursts. Existing streams do not migrate, and a full pool +continues accepting new streams. There is no fixed Mux stream density or count limit. +A carrier closes after 30 seconds fully idle. With `mux=0`, every TLS-carried Flow owns one on-demand lane that closes with the Flow. Mux applies when at least one direction is `tcp` or `mix`. `udp/udp&mux=1` canonicalizes to @@ -308,19 +311,19 @@ Durations use humantime syntax such as `250ms`, `15s`, `2m`, or `1h`. | `NOW_RELOAD_INTERVAL` | `1h` | Supplied-certificate reload interval | TLS Mux shares the transport profile's 4/8, 8/16, or 16/32 MiB stream/connection -receive windows with QUIC. A Mux carrier permits 256 active streams and 512 +receive windows with QUIC. A Mux carrier has no fixed stream count limit and 512 queued frame slots; queued payload remains charged against the connection -window. The application adapts shard density to measured TLS setup latency and -live carrier pressure, caps each direction at 4 shards, reuses the least-loaded -shard after that, and retires +window. Each flow has at most one DATA frame queued or being written, so a bulk +writer cannot fill the shared queue. Receive queues are bounded by byte credit +without blocking unrelated flows on per-flow frame counts. The application +shares at most eight carriers across both directions and retires fully idle shards after 30 seconds. `NOW_MAX_TCP_FLOWS` is the hard per-session logical TCP limit shared by TLS and QUIC. `NOW_MAX_UDP_FLOWS` is the corresponding UDP limit shared by UoT and QUIC DATAGRAM. Excess flows fail without waiting for capacity. QUIC internally admits the sum of both limits as bidirectional streams; this derived capacity has no separate setting. -Portal and Vector use the same QUIC profile regardless of negotiated protocol -version or the client Mux setting. +Portal and Vector use the same QUIC profile regardless of the client Mux setting. The stream/connection/send windows are respectively 4/8/8 MiB for `memory`, 8/16/16 MiB for `balanced`, and 16/32/32 MiB for `throughput`. These are flow-control ceilings, not eager allocations. Larger windows are useful only diff --git a/docs/mux-performance.md b/docs/mux-performance.md index 3043ce0..0e0a17e 100644 --- a/docs/mux-performance.md +++ b/docs/mux-performance.md @@ -46,9 +46,9 @@ claims. | One Mux Shard | 300 ms | 16 | 128 MiB | 670.71 Mbps | 26.72 MiB | 10.19 MiB | | Four Mux Shards | 300 ms | 16 | 128 MiB | 1.69 Gbps | 61.61 MiB | 11.88 MiB | -The larger profile removes the dominant high-RTT collapse. Four Shards improve +In these historical proxy samples, the larger profile removes the dominant high-RTT collapse. Four Shards improve aggregate 16-flow throughput by about 2.5 times at both measured RTTs, while -raising peak RSS substantially. The runtime therefore adapts target density to +raising peak RSS substantially. That implementation adapted target density to TLS setup latency and live carrier pressure, and caps the directional pool at four. A single flow remains on one Shard. @@ -75,11 +75,11 @@ owned frame payload directly. | 100 ms | 16 | 128 MiB | 4 | 4.74 Gbps | 57.48 MiB | 12.78 MiB | | 300 ms | 16 | 128 MiB | 4 | 1.66 Gbps | 75.64 MiB | 26.30 MiB | -The adaptive policy keeps 16 simultaneous low-RTT flows on one carrier, while +That historical adaptive policy kept 16 simultaneous low-RTT flows on one carrier, while 100 and 300 ms samples expand to four. The high-RTT aggregate results match or slightly exceed the fixed-four baseline within normal local-run variation, so -the four-carrier cap remains useful under high BDP without imposing four TLS -connections on low-latency workloads. Single-flow throughput is unchanged. +these observations motivated the former four-carrier cap. They do not validate +the current eight-carrier pool or prove an optimal pool size. Multi-DATA vectored batching was tested and rejected. Tokio-rustls can complete a vectored write after consuming only part of a header/payload pair; extending @@ -120,3 +120,57 @@ runs using the throughput profile. Both smoke cells satisfy the 3% throughput and RSS thresholds. These results exercise the real binaries and carrier topology, but Toxiproxy's TCP termination means the Linux netem matrix remains the gating measurement. + +## 2026-09-08 eight-carrier pool and stream admission + +The current implementation shares at most eight connecting/established TLS +slots per session across directions. Idle carriers are reused before creating +more. At capacity, placement uses credit/queue occupancy, then live plus pending +streams. Connecting slots accept reservations and share one initializer, so a +cold burst is balanced before handshakes finish. There is no fixed Mux stream +count limit, setup-latency density rule, or scheduling timer. Application session +admission limits remain separate. + +Each stream may have one queued/in-progress DATA frame. Receive queues rely on +byte credit rather than a per-stream packet count, removing the slow-small-packet +reader stall. No wire fields were added. The 30-second fully idle timeout remains. + +The saved pre-change release is `cc5623a` (`/tmp/nowhere-mux-before-pool8`). Both +implementations used `NOW_TRANSPORT_MEMORY_PROFILE=throughput`. No builds or tests +ran concurrently with the samples below. + +| Measurement | Implementation | TLS carriers | Throughput | Portal peak RSS | Vector peak RSS | +|---|---|---:|---:|---:|---:| +| Toxiproxy 300 ms, 16 flows, 128 MiB; median of 3 | Before | 4 | 1.668 Gbps | 75.56 MiB | 18.33 MiB | +| Toxiproxy 300 ms, 16 flows, 128 MiB; median of 3 | Current | 8 | 2.833 Gbps | 12.73 MiB | 10.66 MiB | +| Direct loopback, 16 flows, 24 GiB; one sample | Before | 1 | 13.086 Gbps | 26.61 MiB | 10.45 MiB | +| Direct loopback, 16 flows, 24 GiB; one sample | Current | 8 | 11.224 Gbps | 13.23 MiB | 12.78 MiB | + +The final delayed samples were 2833.47, 2836.52, and 2766.98 Mbps; baseline samples +were 1667.70, 1668.21, and 1662.83 Mbps. The delayed median improves 69.9%, while +the loopback point regresses 14.2%. Loopback transfers took 15.755 and 18.367 +seconds respectively. The fixed eight-slot policy therefore favors delayed +parallel transfers, not universal throughput improvement. This is not a completed +no-regression acceptance; the Linux netem matrix is still outstanding. + +```sh +tests/bench-mux-local.sh 300 16 8 1 target/release/nowhere +python3 tests/mux-bench-local.py --direct --rtt-ms 0 \ + --flows 16 --mib-per-flow 1536 --binary target/release/nowhere +``` + +Rejected experiments: + +- Pressure-only background expansion left the initial 16-flow burst on one + carrier and reached only 671.63 Mbps at the same proxy delay. +- Expanding without reservations for connecting carriers produced a 1094.90 Mbps + outlier among otherwise roughly 2.9 Gbps samples. Connecting-slot reservations + remove that first-completed-carrier placement bias. +- Allowing four queued DATA frames per flow gave only 11.368 Gbps in the loopback + point, with Portal/Vector RSS increasing to 14.89/15.38 MiB. The one-frame bound + was retained. + +A longer 300 ms proxy attempt (16 x 256 MiB) failed on the saved baseline with +early EOF and timeouts, so no sustained delayed comparison was obtained. This +does not establish whether the baseline or proxy path caused the failure. Short +proxy samples cannot substitute for packet-level loss/RTT testing. diff --git a/docs/operations.md b/docs/operations.md index 672598d..bf9e762 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -56,11 +56,11 @@ the other family. The important memory bounds are the 1,024 concurrent TCP flows and 256 UDP flows per authenticated client session, the selected 4/8, 8/16, or 16/32 MiB per-stream/per-Mux receive -windows, 256 streams per Mux, bounded reusable relay-buffer caches, and QUIC UDP +windows, bounded reusable relay-buffer caches, and QUIC UDP queue/reassembly limits. UoT and QUIC DATAGRAM share the UDP flow limit. TLS shards originated with `mux=1` by Vector or a Portal `next` client adapt their -target density to TLS setup latency and carrier pressure, stop at 4 shards per -direction, use least-loaded placement, and +pool to concurrent flow demand, stop at eight carriers per session +across both directions, use lowest-occupancy placement, and close after 30 seconds fully idle. Frame queue slots do not bypass byte credit. Windows are granted as permits and payload is admitted incrementally. @@ -74,31 +74,23 @@ bandwidth-delay product. ### TLS Shard placement -An originating client keeps separate uplink and downlink Shard sets. Only a -direction that selects TLS/TCP uses a set; a symmetric `tcp/tcp` flow uses one -duplex stream from the uplink set. +An originating client shares one full-duplex TLS carrier pool across directions. +Mux imposes no fixed stream count limit; session application admission is separate. ```text - +-------------------------+ -new TLS-carried Flow --->| live Shard below 4? | - +------------+------------+ - | - +-------------+---------------+ - | yes | no - v v - +------------------+ +------------------+ - | choose the | | open one TLS | - | least-loaded one | | Mux Shard | - +---------+--------+ +---------+--------+ - | | - +--------------+--------------+ - | - v - open logical stream +new flow --> idle carrier? --> reuse + | + +--> free slot? --> establish TLS (up to eight in parallel) + | + +--> lowest credit/queue occupancy --> multiplex + (connecting slots accept reservations too) ``` -While load grows from zero, a direction uses `ceil(active flows / 4)` Shards. -After load falls, an empty Shard remains available during its idle period: +Connections are created on demand. At most eight pool slots cover establishment +and carrier lifetime. Each slot shares one initializer and counts pending flows +alongside live streams. A cancelled initializer can be retried in the same slot. +No polling task or setup-latency threshold is used. +After load falls, an empty carrier remains available during its idle period: ```text +--------+ last stream closes +------+ 30s with no stream +--------+ diff --git a/docs/protocol.md b/docs/protocol.md index fc3dbc0..30f4ff4 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -251,7 +251,9 @@ Mux uses an initial 4 MiB stream window and 8 MiB connection window. Each side sends one WINDOW to extend its connection window. OPEN advertises the opener's stream extension; the receiver returns its stream extension with WINDOW. The selected transport profile sets final windows to 4/8, 8/16, or 16/32 MiB. -Each shard permits 256 active streams and 512 queued outbound frame slots. Payload must obtain +There is no fixed Mux logical-stream count limit. Each carrier has 512 queued +outbound frame slots; each stream may have one DATA frame queued or being written. +Payload must obtain both stream and connection credit before it enters the outbound queue. ```text @@ -269,13 +271,26 @@ application write Both credit checks precede queue admission. A stream therefore cannot reserve payload beyond either advertised receive window. -Client-side Shards open lazily in separate uplink and downlink sets. A new flow -uses the least-loaded live Shard for its TLS direction. Its target density is -derived from measured TLS setup latency: 16, 8, 4, or 2 active flows as setup -latency crosses 30, 75, and 200 ms. Exhausting three quarters of connection -credit or frame-queue capacity can open the next Shard earlier, up to 4 live -Shards per direction. Once the pool is full, new flows reuse its least-loaded Shard. A symmetric `tcp/tcp` flow -uses one duplex stream from the uplink set. A fully idle Shard closes after 30 +Client-side TLS Mux carriers share one session pool, with at most eight established +or connecting carriers combined. Each TLS carrier is full duplex. New flows +reuse idle carriers first. If all are busy and capacity remains, the new flow +establishes another carrier; independent establishments run concurrently. At +capacity, new flows use the carrier with the lowest maximum occupancy of send +credit, receive credit, and outbound frame slots; stream count plus pending +reservations breaks ties. Connecting slots also accept reservations, so a cold +burst does not pile onto the first completed handshake. Each slot shares one +initializer; cancellation allows a waiter to retry it. Failed expansion can +fall back to an established carrier. There is +no fixed stream density, latency threshold, background polling, or migration +of established streams. This favors parallel throughput over minimizing the +number of carriers for many idle logical streams. + +Receive queues use byte-credit admission rather than blocking the carrier reader +on a per-flow frame count. Every DATA frame consumes at least one KiB of credit, +bounding queued payload and DATA metadata across the carrier. Stream and lifecycle +metadata still grow with live/pending streams; no fixed stream limit does not mean +constant process memory. Authentication and application-level admission policies +remain separate from Mux placement. A fully idle carrier closes after 30 seconds. Portal applies the same timeout to an authenticated Mux carrier with no active streams. Sharding is runtime placement and does not add wire fields. diff --git a/docs/security.md b/docs/security.md index 57d9119..1013f99 100644 --- a/docs/security.md +++ b/docs/security.md @@ -71,10 +71,12 @@ and DATA for unknown streams close the carrier. Late terminal and credit frames for a terminal stream are idempotent. The transport memory profile bounds Mux stream/connection windows at 4/8, -8/16, or 16/32 MiB, and each Mux allows 256 active streams. With client `mux=1`, -Vector or Portal `next` adapts a shard's target density to TLS setup latency and -live connection pressure, caps each direction at 4 shards, distributes new flows to the -least-loaded shard, and closes a fully idle shard after 30 seconds. One +8/16, or 16/32 MiB. Mux has no fixed logical-stream count limit. With client `mux=1`, +Vector or Portal `next` shares at most eight TLS carriers across both directions, +reuses idle carriers before creating more, distributes flows by occupancy at capacity, +and closes a fully idle carrier after 30 seconds. Stream and pending lifecycle +metadata remain proportional to admitted streams; byte credit bounds DATA, not +arbitrary OPEN traffic. One authenticated inbound Mux carrier is subject to the same fully idle timeout. One authenticated client session admits at most 1,024 concurrent logical TCP flows and 256 logical UDP flows across all of its carriers. UoT and QUIC diff --git a/src/mux/driver.rs b/src/mux/driver.rs index 2ccc3f0..ff312bb 100644 --- a/src/mux/driver.rs +++ b/src/mux/driver.rs @@ -20,7 +20,12 @@ pub(super) async fn send_data( payload: MuxChunk, ) -> io::Result<()> { let charge = frame_charge(payload.len()); - let flow_credit = shared.send_credit(flow_id)?; + let (flow_credit, slot) = { + let flows = shared.flows.lock().expect("mux flow lock"); + let flow = flows.get(&flow_id).ok_or_else(closed)?; + (flow.send_credit.clone(), flow.send_slot.clone()) + }; + let slot = slot.acquire_owned().await.map_err(|_| closed())?; let flow = flow_credit .acquire_many_owned(charge as u32) .await @@ -33,9 +38,10 @@ pub(super) async fn send_data( .map_err(|_| closed())?; shared .data_tx - .send(Outbound::Frame { + .send(Outbound::Data { header: frame_data(flow_id, payload.len())?, payload, + _slot: slot, }) .await .map_err(|_| closed())?; @@ -94,8 +100,7 @@ pub(super) async fn run_reader(mut reader: R, shared: Arc< } async fn receive_open(shared: &Arc, header: FrameHeader) -> io::Result<()> { - let terminal_permit = shared.reserve_terminal().await?; - let stream = shared.insert_flow(header.flow_id, terminal_permit, true)?; + let stream = shared.insert_flow(header.flow_id, true)?; let extra_credit = header.value as usize; if extra_credit != 0 { let credit = shared.send_credit(header.flow_id)?; @@ -106,22 +111,24 @@ async fn receive_open(shared: &Arc, header: FrameHeader) -> io::Result<( } credit.add_permits(extra_credit); } - shared.incoming_tx.send(stream).await.map_err(|_| closed()) + shared.incoming_tx.send(stream).map_err(|_| closed()) } async fn receive_data(shared: &Arc, header: FrameHeader, payload: Bytes) -> io::Result<()> { let charge = frame_charge(payload.len()); let inbound = shared.admit_receive(header.flow_id, charge)?; - inbound - .send(Inbound::Data { payload, charge }) - .await - .map_err(|_| closed()) + if inbound.send(Inbound::Data { payload, charge }).is_err() { + // The local read half may be abandoned while its writer is still + // live. Return credit for discarded bytes without killing other flows. + shared.release_receive(header.flow_id, charge); + } + Ok(()) } async fn receive_close(shared: &Shared, header: FrameHeader) { if header.code == CLOSE_RESET { if let Some(flow) = shared.remove_flow(header.flow_id) { - let _ = flow.inbound.send(Inbound::Reset).await; + let _ = flow.inbound.send(Inbound::Reset); } return; } @@ -137,7 +144,7 @@ async fn receive_close(shared: &Shared, header: FrameHeader) { }) }; if let Some(inbound) = inbound { - let _ = inbound.send(Inbound::Fin).await; + let _ = inbound.send(Inbound::Fin); } } @@ -172,7 +179,10 @@ fn receive_window(shared: &Shared, header: FrameHeader) -> io::Result<()> { Ok(()) } -pub(super) async fn run_terminals(shared: Arc, mut terminal_rx: mpsc::Receiver) { +pub(super) async fn run_terminals( + shared: Arc, + mut terminal_rx: mpsc::UnboundedReceiver, +) { loop { if shared.closed.load(std::sync::atomic::Ordering::Acquire) { return; @@ -187,10 +197,7 @@ pub(super) async fn run_terminals(shared: Arc, mut terminal_rx: mpsc::Re }; let sent = tokio::select! { _ = shared.closed_notify.cancelled() => return, - sent = shared.data_tx.send(Outbound::Frame { - header, - payload: MuxChunk::empty(), - }) => sent, + sent = shared.data_tx.send(Outbound::Control(header)) => sent, }; if sent.is_err() { return; @@ -234,13 +241,13 @@ pub(super) async fn run_writer( return Err(closed()); } } - Outbound::Frame { header, payload } if payload.is_empty() => { + Outbound::Control(header) => { headers.clear(); headers.extend_from_slice(&encode_header(header).map_err(invalid)?); while headers.len() < 8 * 256 { let Ok(next) = data_rx.try_recv() else { break }; match next { - Outbound::Frame { header, payload } if payload.is_empty() => { + Outbound::Control(header) => { headers.extend_from_slice(&encode_header(header).map_err(invalid)?); } next => { @@ -252,9 +259,14 @@ pub(super) async fn run_writer( writer.write_all(&headers).await?; writer.flush().await?; } - Outbound::Frame { header, payload } => { + Outbound::Data { + header, + payload, + _slot, + } => { let header = encode_header(header).map_err(invalid)?; write_frame_vectored(&mut writer, &header, payload.as_ref()).await?; + drop(_slot); } } } diff --git a/src/mux/handle.rs b/src/mux/handle.rs index 2127a72..e6fc60a 100644 --- a/src/mux/handle.rs +++ b/src/mux/handle.rs @@ -22,8 +22,8 @@ impl MuxHandle { { let config = config.validate()?; let (data_tx, data_rx) = mpsc::channel(config.outbound_frames); - let (terminal_tx, terminal_rx) = mpsc::channel(config.max_streams); - let (incoming_tx, incoming_rx) = mpsc::channel(config.max_streams); + let (terminal_tx, terminal_rx) = mpsc::unbounded_channel(); + let (incoming_tx, incoming_rx) = mpsc::unbounded_channel(); let (active_streams_tx, _) = watch::channel(0); let shared = Arc::new(Shared { config, @@ -42,7 +42,7 @@ impl MuxHandle { .connection_window_bytes .saturating_sub(super::BASE_CONNECTION_WINDOW_BYTES), )), - ready_flows: Mutex::new(VecDeque::with_capacity(config.max_streams)), + ready_flows: Mutex::new(VecDeque::new()), data_tx, terminal_tx, control_notify: Notify::new(), @@ -68,15 +68,24 @@ impl MuxHandle { )) } + #[cfg(test)] pub(crate) async fn open_stream(&self, flow_id: super::FlowId) -> io::Result { - let terminal_permit = self.shared.reserve_terminal().await?; - let stream = self.shared.insert_flow(flow_id, terminal_permit, false)?; + let stream = self.prepare_stream(flow_id)?; + self.open_prepared(stream).await + } + + pub(crate) fn prepare_stream(&self, flow_id: super::FlowId) -> io::Result { + self.shared.insert_flow(flow_id, false) + } + + pub(crate) async fn open_prepared(&self, stream: MuxStream) -> io::Result { + let flow_id = stream.flow_id(); self.shared .data_tx - .send(Outbound::Frame { - header: frame_open(flow_id, self.shared.config.stream_window_bytes)?, - payload: super::MuxChunk::empty(), - }) + .send(Outbound::Control(frame_open( + flow_id, + self.shared.config.stream_window_bytes, + )?)) .await .map_err(|_| closed())?; Ok(stream) @@ -90,15 +99,22 @@ impl MuxHandle { self.shared.flows.lock().expect("mux flow lock").len() } - pub(crate) fn has_stream_capacity(&self) -> bool { - self.active_streams() < self.shared.config.max_streams - } - - pub(crate) fn is_send_congested(&self) -> bool { + pub(crate) fn pressure(&self) -> usize { let available = self.shared.connection_send_credit.available_permits(); let peak = self.shared.connection_send_peak.load(Ordering::Relaxed); - available.saturating_mul(4) < peak - || self.shared.data_tx.capacity().saturating_mul(4) < self.shared.config.outbound_frames + let receive = *self + .shared + .connection_receive_credit + .lock() + .expect("mux credit lock"); + let receive_peak = super::credit_units(self.shared.config.connection_window_bytes); + let queue = self.shared.config.outbound_frames; + // Fixed-point occupancy; no per-frame timestamps or flow scans. + let occupancy = + |free: usize, total: usize| total.saturating_sub(free) * 1024 / total.max(1); + occupancy(available, peak) + .max(occupancy(receive, receive_peak)) + .max(occupancy(self.shared.data_tx.capacity(), queue)) } #[cfg(test)] @@ -117,6 +133,7 @@ impl MuxHandle { self.shared.close(); } + #[cfg(test)] pub(crate) fn same_carrier(&self, other: &Self) -> bool { Arc::ptr_eq(&self.shared, &other.shared) } diff --git a/src/mux/mod.rs b/src/mux/mod.rs index aac4f2b..a873763 100644 --- a/src/mux/mod.rs +++ b/src/mux/mod.rs @@ -28,17 +28,12 @@ const MAX_STREAM_WINDOW_BYTES: usize = 16 * MIB; const MAX_CONNECTION_WINDOW_BYTES: usize = 32 * MIB; const CREDIT_UNIT_BYTES: usize = 1024; const WINDOW_UPDATE_DIVISOR: usize = 8; -// Frame count is separate from the byte window: UoT carries many small -// packets, so the frame queue absorbs scheduling bursts while the byte window -// remains the hard payload bound. -const FLOW_CHANNEL_FRAMES: usize = 512; pub(crate) const MUX_IDLE_TIMEOUT: Duration = Duration::from_secs(30); #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) struct MuxConfig { pub stream_window_bytes: usize, pub connection_window_bytes: usize, - pub max_streams: usize, pub outbound_frames: usize, } @@ -59,7 +54,6 @@ impl MuxConfig { Self { stream_window_bytes: profile.stream_receive_window as usize, connection_window_bytes: profile.connection_receive_window as usize, - max_streams: 256, outbound_frames: 512, } } @@ -73,7 +67,6 @@ impl MuxConfig { .connection_window_bytes .is_multiple_of(CREDIT_UNIT_BYTES) || self.connection_window_bytes < self.stream_window_bytes - || self.max_streams == 0 || self.outbound_frames == 0 || self.connection_window_bytes > Semaphore::MAX_PERMITS { @@ -105,7 +98,7 @@ struct ReceiveCredit { pub(crate) struct FlowReader { shared: Arc, flow_id: FlowId, - receiver: mpsc::Receiver, + receiver: mpsc::UnboundedReceiver, current: Option<(Bytes, usize, usize)>, eof: bool, } @@ -115,7 +108,6 @@ pub(crate) struct FlowWriter { flow_id: FlowId, pending: Option, pending_action: Option, - terminal_permit: Option>, closed: bool, } @@ -125,7 +117,7 @@ pub(crate) struct MuxHandle { } pub(crate) struct Incoming { - receiver: mpsc::Receiver, + receiver: mpsc::UnboundedReceiver, } type WriteFuture = Pin> + Send>>; @@ -140,9 +132,9 @@ struct Shared { pending_connection_credit: AtomicUsize, ready_flows: Mutex>, data_tx: mpsc::Sender, - terminal_tx: mpsc::Sender, + terminal_tx: mpsc::UnboundedSender, control_notify: Notify, - incoming_tx: mpsc::Sender, + incoming_tx: mpsc::UnboundedSender, active_streams_tx: watch::Sender, closed: AtomicBool, closed_notify: tokio_util::sync::CancellationToken, @@ -151,8 +143,9 @@ struct Shared { } struct FlowState { - inbound: mpsc::Sender, + inbound: mpsc::UnboundedSender, send_credit: Arc, + send_slot: Arc, receive_credit: usize, pending_receive_credit: usize, window_queued: bool, @@ -167,10 +160,12 @@ enum Inbound { } enum Outbound { - Frame { + Data { header: FrameHeader, payload: MuxChunk, + _slot: tokio::sync::OwnedSemaphorePermit, }, + Control(FrameHeader), Flush(oneshot::Sender>), } @@ -193,10 +188,6 @@ impl MuxChunk { } } - fn empty() -> Self { - Self::from_bytes(Bytes::new()) - } - pub(crate) fn len(&self) -> usize { self.payload.len() } @@ -219,29 +210,20 @@ impl Drop for ReceiveCredit { } impl Shared { - async fn reserve_terminal(&self) -> io::Result> { - tokio::select! { - _ = self.closed_notify.cancelled() => Err(closed()), - permit = self.terminal_tx.clone().reserve_owned() => permit.map_err(|_| closed()), - } - } - fn insert_flow( self: &Arc, flow_id: FlowId, - terminal_permit: mpsc::OwnedPermit, advertise_window: bool, ) -> io::Result { if flow_id == 0 || self.closed.load(Ordering::Acquire) { return Err(closed()); } - let (sender, receiver) = mpsc::channel(FLOW_CHANNEL_FRAMES); + // Every DATA frame consumes at least one KiB of connection credit, + // bounding both payload and queue nodes without blocking the reader. + let (sender, receiver) = mpsc::unbounded_channel(); let mut flows = self.flows.lock().expect("mux flow lock"); - if flows.len() >= self.config.max_streams { - return Err(io::Error::new( - io::ErrorKind::WouldBlock, - "mux stream limit reached", - )); + if self.closed.load(Ordering::Acquire) { + return Err(closed()); } if flows.contains_key(&flow_id) { return Err(io::Error::new( @@ -264,6 +246,7 @@ impl Shared { FlowState { inbound: sender, send_credit, + send_slot: Arc::new(Semaphore::new(1)), receive_credit: credit_units(self.config.stream_window_bytes), pending_receive_credit: initial_credit, window_queued: advertise_window && initial_credit != 0, @@ -272,6 +255,7 @@ impl Shared { }, ); let active_streams = flows.len(); + self.active_streams_tx.send_replace(active_streams); drop(flows); if advertise_window && initial_credit != 0 { self.ready_flows @@ -280,7 +264,6 @@ impl Shared { .push_back(flow_id); self.control_notify.notify_one(); } - self.active_streams_tx.send_replace(active_streams); Ok(MuxStream { reader: FlowReader { shared: self.clone(), @@ -294,7 +277,6 @@ impl Shared { flow_id, pending: None, pending_action: None, - terminal_permit: Some(terminal_permit), closed: false, }, }) @@ -307,11 +289,12 @@ impl Shared { let mut flows = self.flows.lock().expect("mux flow lock"); for flow in flows.values() { flow.send_credit.close(); + flow.send_slot.close(); } flows.clear(); + self.active_streams_tx.send_replace(0); drop(flows); self.connection_send_credit.close(); - self.active_streams_tx.send_replace(0); self.closed_notify.cancel(); } @@ -329,14 +312,19 @@ impl Shared { let removed = flows.remove(&flow_id); if let Some(flow) = &removed { flow.send_credit.close(); + flow.send_slot.close(); } let active_streams = flows.len(); - drop(flows); self.active_streams_tx.send_replace(active_streams); + drop(flows); removed } - fn admit_receive(&self, flow_id: FlowId, charge: usize) -> io::Result> { + fn admit_receive( + &self, + flow_id: FlowId, + charge: usize, + ) -> io::Result> { let mut connection = self .connection_receive_credit .lock() @@ -421,8 +409,8 @@ impl Shared { flows.remove(&flow_id); } let active_streams = flows.len(); - drop(flows); self.active_streams_tx.send_replace(active_streams); + drop(flows); if flush_credit { self.control_notify.notify_one(); } diff --git a/src/mux/stream.rs b/src/mux/stream.rs index 8057868..2083ef7 100644 --- a/src/mux/stream.rs +++ b/src/mux/stream.rs @@ -147,6 +147,7 @@ impl FlowReader { impl Drop for FlowReader { fn drop(&mut self) { + self.receiver.close(); if let Some((_, _, charge)) = self.current.take() { self.shared.release_receive(self.flow_id, charge); } @@ -248,10 +249,7 @@ impl AsyncWrite for FlowWriter { self.pending_action = Some(Box::pin(async move { shared .data_tx - .send(Outbound::Frame { - header: frame_close(flow_id, CLOSE_FIN)?, - payload: MuxChunk::empty(), - }) + .send(Outbound::Control(frame_close(flow_id, CLOSE_FIN)?)) .await .map_err(|_| closed()) })); @@ -259,7 +257,6 @@ impl AsyncWrite for FlowWriter { match self.poll_action(cx) { Poll::Ready(Ok(())) => { self.closed = true; - self.terminal_permit = None; Poll::Ready(Ok(())) } other => other, @@ -330,13 +327,11 @@ impl FlowWriter { impl Drop for FlowWriter { fn drop(&mut self) { if !self.closed { - // One bounded dispatcher per carrier preserves ordering behind + // One dispatcher per carrier preserves ordering behind // already queued DATA without spawning a task for every dropped // stream. Dropping a writer is a half-close: split-direction users // intentionally discard the unused half while retaining the other. - if let Some(permit) = self.terminal_permit.take() { - permit.send(self.flow_id); - } + let _ = self.shared.terminal_tx.send(self.flow_id); } self.shared.release_part(self.flow_id); } diff --git a/src/tests/mux/runtime.rs b/src/tests/mux/runtime.rs index 55ef06c..bdef3b0 100644 --- a/src/tests/mux/runtime.rs +++ b/src/tests/mux/runtime.rs @@ -2,14 +2,118 @@ use super::wire::{CLOSE_FIN, CLOSE_RESET, FrameHeader, encode_header}; use super::*; use tokio::io::{AsyncReadExt, AsyncWriteExt}; +#[tokio::test] +async fn more_than_256_live_streams_transfer_and_half_close() { + tokio::time::timeout(Duration::from_secs(10), async { + let (left, right) = tokio::io::duplex(1 << 20); + let (client, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + let (server, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); + let mut streams = Vec::new(); + for id in 1..=1024 { + let outgoing = client.open_stream(id).await.unwrap(); + let accepted = incoming.accept().await.unwrap().unwrap(); + streams.push((outgoing, accepted)); + } + assert_eq!(client.active_streams(), 1024); + for (mut outgoing, mut accepted) in streams { + outgoing.write_all(b"ok").await.unwrap(); + drop(outgoing); + let mut bytes = Vec::new(); + accepted.read_to_end(&mut bytes).await.unwrap(); + assert_eq!(bytes, b"ok"); + } + assert_eq!(client.active_streams(), 0); + client.close(); + server.close(); + }) + .await + .expect("stream admission must not depend on terminal queue capacity"); +} + +#[tokio::test] +async fn slow_small_packet_reader_does_not_block_other_flows() { + tokio::time::timeout(Duration::from_secs(5), async { + let (left, right) = tokio::io::duplex(1 << 20); + let (client, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + let (server, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); + let mut slow = client.open_stream(1).await.unwrap(); + let _slow_peer = incoming.accept().await.unwrap().unwrap(); + for _ in 0..1024 { + slow.write_all(b"x").await.unwrap(); + } + let mut fast = client.open_stream(2).await.unwrap(); + let mut fast_peer = incoming.accept().await.unwrap().unwrap(); + fast.write_all(b"ok").await.unwrap(); + let mut bytes = [0; 2]; + fast_peer.read_exact(&mut bytes).await.unwrap(); + assert_eq!(&bytes, b"ok"); + client.close(); + server.close(); + }) + .await + .expect("per-flow queue must not stall the carrier reader"); +} + #[test] fn production_idle_timeout_remains_thirty_seconds() { assert_eq!(MUX_IDLE_TIMEOUT, Duration::from_secs(30)); } +#[tokio::test] +async fn abandoned_reader_returns_credit_without_closing_other_streams() { + tokio::time::timeout(Duration::from_secs(5), async { + let (left, right) = tokio::io::duplex(1 << 20); + let (client, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + let (server, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); + let outgoing = client.open_stream(1).await.unwrap(); + let mut accepted = incoming.accept().await.unwrap().unwrap(); + let (reader, _writer) = outgoing.into_split(); + drop(reader); + accepted + .write_all(&vec![0; 2 * MAX_CONNECTION_WINDOW_BYTES]) + .await + .unwrap(); + let mut other = client.open_stream(2).await.unwrap(); + let mut peer = incoming.accept().await.unwrap().unwrap(); + other.write_all(b"ok").await.unwrap(); + let mut bytes = [0; 2]; + peer.read_exact(&mut bytes).await.unwrap(); + assert_eq!(&bytes, b"ok"); + assert!(!client.is_closed()); + client.close(); + server.close(); + }) + .await + .unwrap(); +} + +#[tokio::test] +async fn a_blocked_flow_cannot_fill_the_shared_send_queue() { + let (left, _right) = tokio::io::duplex(1); + let (handle, _incoming) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + let mut a = handle.open_stream(1).await.unwrap(); + let mut b = handle.open_stream(2).await.unwrap(); + a.write_all(b"queued").await.unwrap(); + let pending = tokio::spawn(async move { a.write_all(b"blocked").await }); + tokio::time::timeout(Duration::from_secs(1), b.write_all(b"other")) + .await + .unwrap() + .unwrap(); + tokio::task::yield_now().await; + assert!(!pending.is_finished()); + handle.close(); + assert!( + tokio::time::timeout(Duration::from_secs(1), pending) + .await + .unwrap() + .unwrap() + .is_err() + ); +} + async fn assert_raw_frame_closes_carrier(frame: &[u8]) { let (left, mut peer) = tokio::io::duplex(1 << 20); - let (handle, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + let (handle, _incoming) = MuxHandle::start(left, MuxConfig::default()).unwrap(); peer.write_all(frame).await.unwrap(); tokio::time::timeout(Duration::from_secs(1), handle.closed()) .await @@ -52,7 +156,15 @@ async fn duplicate_close_and_late_stream_window_are_idempotent() { peer.write_all(&encode_header(FrameHeader::window(7, 1).unwrap()).unwrap()) .await .unwrap(); - tokio::task::yield_now().await; + peer.write_all(&encode_header(FrameHeader::open(9, 0).unwrap()).unwrap()) + .await + .unwrap(); + let next = tokio::time::timeout(Duration::from_secs(1), incoming.accept()) + .await + .unwrap() + .unwrap() + .unwrap(); + assert_eq!(next.flow_id(), 9); assert!(!handle.is_closed()); drop(stream); } diff --git a/src/tests/portal/conn/vector.rs b/src/tests/portal/conn/vector.rs index 4d677f8..bb23cbb 100644 --- a/src/tests/portal/conn/vector.rs +++ b/src/tests/portal/conn/vector.rs @@ -281,10 +281,12 @@ async fn mux_full_duplex_tcp_exceeds_each_direction_credit_window() { } #[tokio::test] -async fn mux_seventeenth_active_tcp_flow_opens_a_second_low_latency_shard() { - const FLOW_COUNT: usize = 17; +async fn mux_hundreds_of_tcp_flows_share_at_most_eight_carriers() { + const FLOW_COUNT: usize = 300; - let target = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let target = tokio::net::TcpSocket::new_v4().unwrap(); + target.bind("127.0.0.1:0".parse().unwrap()).unwrap(); + let target = target.listen(1024).unwrap(); let target_address = target.local_addr().unwrap(); let target_shutdown = CancellationToken::new(); let target_child_shutdown = target_shutdown.clone(); @@ -300,24 +302,33 @@ async fn mux_seventeenth_active_tcp_flow_opens_a_second_low_latency_shard() { let flows = timeout(TEST_TIMEOUT, async { let mut flows = Vec::with_capacity(FLOW_COUNT); + let mut opening = tokio::task::JoinSet::new(); for _ in 0..FLOW_COUNT { - let mut flow = TcpStream::connect(runtime.socks).await.unwrap(); - negotiate_socks(&mut flow).await; - flow.write_all(&ip_request(1, target_address)) - .await - .unwrap(); - read_ipv4_reply(&mut flow).await; - flows.push(flow); + // Exercise concurrent cold admission without overflowing the OS + // target listener's SYN backlog when the full suite runs in parallel. + if opening.len() == 32 { + flows.push(opening.join_next().await.unwrap().unwrap()); + } + let socks = runtime.socks; + opening.spawn(async move { + let mut flow = TcpStream::connect(socks).await.unwrap(); + negotiate_socks(&mut flow).await; + flow.write_all(&ip_request(1, target_address)) + .await + .unwrap(); + read_ipv4_reply(&mut flow).await; + flow + }); } - while runtime.portal_stats.link_tcp.load(Ordering::Relaxed) != 2 { - tokio::task::yield_now().await; + while let Some(flow) = opening.join_next().await { + flows.push(flow.unwrap()); } flows }) .await .unwrap(); - assert_eq!(runtime.portal_stats.link_tcp.load(Ordering::Relaxed), 2); + assert_eq!(runtime.portal_stats.link_tcp.load(Ordering::Relaxed), 8); assert_eq!( runtime.portal_stats.tcp_active.load(Ordering::Relaxed), FLOW_COUNT as i32 diff --git a/src/tests/vector/session/tls.rs b/src/tests/vector/session/tls.rs index 4316d3a..8de7319 100644 --- a/src/tests/vector/session/tls.rs +++ b/src/tests/vector/session/tls.rs @@ -4,107 +4,139 @@ use super::*; use tokio::io::AsyncReadExt; -fn shard(handle: MuxHandle) -> TlsMux { - TlsMux { - handle, - target_density: 4, - } +fn slot(handle: MuxHandle) -> Arc { + let slot = Arc::new(TlsMux::default()); + assert!(slot.handle.set(handle).is_ok()); + slot } -#[tokio::test] -async fn shard_selection_stops_at_its_target_density() { +async fn carrier(pressured: bool) -> (MuxHandle, MuxHandle, crate::mux::Incoming, Vec) { let (left, right) = tokio::io::duplex(1 << 20); - let (handle, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); - let (_peer, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); + let config = MuxConfig { + stream_window_bytes: 4 << 20, + connection_window_bytes: 8 << 20, + outbound_frames: 512, + }; + let (handle, _) = MuxHandle::start(left, config).unwrap(); + let (peer, mut incoming) = MuxHandle::start(right, config).unwrap(); let mut streams = Vec::new(); - let mut peers = Vec::new(); - - for flow_id in 1..4 { - streams.push(handle.open_stream(flow_id).await.unwrap()); - peers.push(incoming.accept().await.unwrap().unwrap()); + for id in 1..=2 { + let mut stream = handle.open_stream(id).await.unwrap(); + streams.push(incoming.accept().await.unwrap().unwrap()); + if pressured { + stream.write_all(&vec![1; 7 << 19]).await.unwrap(); + stream.flush().await.unwrap(); + } + streams.push(stream); } - let shard = shard(handle.clone()); + (handle, peer, incoming, streams) +} + +#[tokio::test] +async fn cold_reservations_balance_across_eight_connecting_slots() { + let mut pool = Vec::new(); + let pending: Vec<_> = (0..16).map(|_| reserve_mux(&mut pool)).collect(); + assert_eq!(pool.len(), 8); assert!( - select_available_mux(std::slice::from_ref(&shard)) - .unwrap() - .handle - .same_carrier(&handle) + pool.iter() + .all(|slot| slot.pending.load(Ordering::Relaxed) == 2) + ); + drop(pending); + assert!( + pool.iter() + .all(|slot| slot.pending.load(Ordering::Relaxed) == 0) ); - - streams.push(handle.open_stream(4).await.unwrap()); - peers.push(incoming.accept().await.unwrap().unwrap()); - assert!(select_available_mux(std::slice::from_ref(&shard)).is_none()); } #[tokio::test] -async fn shard_selection_uses_the_least_loaded_carrier() { - let (left_a, right_a) = tokio::io::duplex(1 << 20); - let (handle_a, _) = MuxHandle::start(left_a, MuxConfig::default()).unwrap(); - let (_peer_a, mut incoming_a) = MuxHandle::start(right_a, MuxConfig::default()).unwrap(); - let (left_b, right_b) = tokio::io::duplex(1 << 20); - let (handle_b, _) = MuxHandle::start(left_b, MuxConfig::default()).unwrap(); - let (_peer_b, mut incoming_b) = MuxHandle::start(right_b, MuxConfig::default()).unwrap(); - - let _stream_a1 = handle_a.open_stream(1).await.unwrap(); - let _peer_a1 = incoming_a.accept().await.unwrap().unwrap(); - let _stream_a2 = handle_a.open_stream(2).await.unwrap(); - let _peer_a2 = incoming_a.accept().await.unwrap().unwrap(); - let _stream_b = handle_b.open_stream(3).await.unwrap(); - let _peer_b = incoming_b.accept().await.unwrap().unwrap(); - - let selected = select_available_mux(&[shard(handle_a), shard(handle_b.clone())]).unwrap(); - assert!(selected.handle.same_carrier(&handle_b)); +async fn idle_carrier_is_reused_before_new_connections() { + let (handle, peer, _incoming, streams) = carrier(false).await; + drop(streams); + let mut pool = vec![slot(handle.clone())]; + let selected = reserve_mux(&mut pool); + assert_eq!(pool.len(), 1); + assert!(selected.0.handle.get().unwrap().same_carrier(&handle)); + handle.close(); + peer.close(); } #[tokio::test] -async fn full_pool_reuses_the_least_loaded_shard() { - let mut shards = Vec::new(); - let mut peer_handles = Vec::new(); - let mut streams = Vec::new(); +async fn full_pool_prefers_lower_pressure_and_still_transfers_new_flows() { + let mut pool = Vec::new(); let mut peers = Vec::new(); - for shard_index in 0..TLS_MUX_MAX_SHARDS_PER_DIRECTION { - let (left, right) = tokio::io::duplex(1 << 20); - let (handle, _) = MuxHandle::start(left, MuxConfig::default()).unwrap(); - let (peer_handle, mut incoming) = MuxHandle::start(right, MuxConfig::default()).unwrap(); - for index in 0..4 { - let flow_id = (shard_index * 4 + index + 1) as u32; - streams.push(handle.open_stream(flow_id).await.unwrap()); - peers.push(incoming.accept().await.unwrap().unwrap()); - } - shards.push(shard(handle)); - peer_handles.push(peer_handle); + let mut streams = Vec::new(); + let mut incoming = Vec::new(); + for index in 0..8 { + let (handle, peer, receiver, held) = carrier(index != 7).await; + pool.push(slot(handle)); + peers.push(peer); + streams.extend(held); + incoming.push(receiver); + } + let selected = reserve_mux(&mut pool); + assert!(Arc::ptr_eq(&selected.0, &pool[7])); + let handle = selected.0.handle.get().unwrap(); + let mut stream = handle.open_stream(99).await.unwrap(); + let mut accepted = incoming[7].accept().await.unwrap().unwrap(); + stream.write_all(b"new").await.unwrap(); + let mut bytes = [0; 3]; + accepted.read_exact(&mut bytes).await.unwrap(); + assert_eq!(&bytes, b"new"); + assert_eq!(pool.len(), 8); + for slot in &pool { + slot.handle.get().unwrap().close(); } - assert!(select_available_mux(&shards).is_some()); - drop((peer_handles, streams, peers)); + for peer in &peers { + peer.close(); + } + drop((streams, stream)); } -#[test] -fn setup_latency_adapts_target_density() { - assert_eq!(mux_target_density(Duration::from_millis(10)), 16); - assert_eq!(mux_target_density(Duration::from_millis(50)), 8); - assert_eq!(mux_target_density(Duration::from_millis(100)), 4); - assert_eq!(mux_target_density(Duration::from_millis(600)), 2); +#[tokio::test] +async fn cancelling_initializer_releases_reservation_and_allows_retry() { + let mut pool = Vec::new(); + let pending = reserve_mux(&mut pool); + let (started, ready) = tokio::sync::oneshot::channel(); + let task = tokio::spawn(async move { + let _pending = pending; + _pending + .0 + .handle + .get_or_try_init(|| async { + let _ = started.send(()); + std::future::pending::>().await + }) + .await + .map(|_| ()) + }); + ready.await.unwrap(); + task.abort(); + assert!(task.await.unwrap_err().is_cancelled()); + assert_eq!(pool[0].pending.load(Ordering::Relaxed), 0); + let retry = reserve_mux(&mut pool); + assert_eq!(pool.len(), 1); + let (handle, peer, _incoming, streams) = carrier(false).await; + retry + .0 + .handle + .get_or_try_init(|| async { Ok::<_, anyhow::Error>(handle.clone()) }) + .await + .unwrap(); + assert!(retry.0.handle.get().unwrap().same_carrier(&handle)); + handle.close(); + peer.close(); + drop(streams); } #[tokio::test] -async fn closing_one_shard_does_not_affect_another() { - let (left_a, right_a) = tokio::io::duplex(1 << 20); - let (handle_a, _) = MuxHandle::start(left_a, MuxConfig::default()).unwrap(); - let (_peer_a, mut incoming_a) = MuxHandle::start(right_a, MuxConfig::default()).unwrap(); - let (left_b, right_b) = tokio::io::duplex(1 << 20); - let (handle_b, _) = MuxHandle::start(left_b, MuxConfig::default()).unwrap(); - let (_peer_b, mut incoming_b) = MuxHandle::start(right_b, MuxConfig::default()).unwrap(); - - let mut stream_a = handle_a.open_stream(1).await.unwrap(); - let _peer_stream_a = incoming_a.accept().await.unwrap().unwrap(); - let mut stream_b = handle_b.open_stream(2).await.unwrap(); - let mut peer_stream_b = incoming_b.accept().await.unwrap().unwrap(); - - handle_a.close(); - assert!(stream_a.write_all(b"closed").await.is_err()); - - stream_b.write_all(b"live").await.unwrap(); - let mut payload = [0_u8; 4]; - peer_stream_b.read_exact(&mut payload).await.unwrap(); - assert_eq!(&payload, b"live"); +async fn closed_carrier_is_replaced_with_a_reusable_slot() { + let (handle, peer, _incoming, streams) = carrier(false).await; + let mut pool = vec![slot(handle.clone())]; + let old = pool[0].clone(); + handle.close(); + let pending = reserve_mux(&mut pool); + assert_eq!(pool.len(), 1); + assert!(!Arc::ptr_eq(&pending.0, &old)); + peer.close(); + drop(streams); } diff --git a/src/vector/flow.rs b/src/vector/flow.rs index 764559e..378afe6 100644 --- a/src/vector/flow.rs +++ b/src/vector/flow.rs @@ -33,7 +33,7 @@ use crate::telemetry::{AccessOutcome, AccessSpan, RuntimeEvent, RuntimeKind, Run use super::config::CarrierMode; use super::flow_id::FlowLease; use super::route::{ResolvedRoute, RoutePlan}; -use super::session::{LinkGuard, MuxDirection, OpenedTls, QuicSession}; +use super::session::{LinkGuard, OpenedTls, QuicSession}; use super::{PortalClient, VectorInner}; mod tcp; @@ -87,22 +87,17 @@ pub(super) async fn open_lane( client: Arc, carrier: Carrier, flow_id: u32, - direction: MuxDirection, ) -> Result { match carrier { Carrier::TlsTcp => { - let opened = client - .tls_manager - .open(flow_id, direction) - .await - .map_err(|error| { - client.telemetry.emit_runtime(RuntimeEvent::new( - RuntimeLevel::Warn, - RuntimeKind::Carrier, - format!("TLS carrier connection failed: {error}"), - )); - error - })?; + let opened = client.tls_manager.open(flow_id).await.map_err(|error| { + client.telemetry.emit_runtime(RuntimeEvent::new( + RuntimeLevel::Warn, + RuntimeKind::Carrier, + format!("TLS carrier connection failed: {error}"), + )); + error + })?; match opened { OpenedTls::Mux(stream) => { let (reader, writer) = stream.into_split(); @@ -173,14 +168,12 @@ pub(super) async fn prepare_lanes( flow_id: u32, ) -> Result> { if !route.split() { - return Ok(vec![ - open_lane(client, route.uplink, flow_id, MuxDirection::Up).await?, - ]); + return Ok(vec![open_lane(client, route.uplink, flow_id).await?]); } let (uplink, downlink) = tokio::join!( - open_lane(client.clone(), route.uplink, flow_id, MuxDirection::Up,), - open_lane(client, route.downlink, flow_id, MuxDirection::Down), + open_lane(client.clone(), route.uplink, flow_id), + open_lane(client, route.downlink, flow_id), ); match (uplink, downlink) { (Ok(uplink), Ok(downlink)) => Ok(vec![uplink, downlink]), diff --git a/src/vector/session.rs b/src/vector/session.rs index 2656587..483ec5b 100644 --- a/src/vector/session.rs +++ b/src/vector/session.rs @@ -37,7 +37,7 @@ use super::config::PortalClientConfig; use super::tls::{ClientTls, EXPORTER_LABEL, require_quic_nw2}; const QUIC_DATAGRAM_BUFFER_SIZE: usize = 4 * 1024 * 1024; -const TLS_MUX_MAX_SHARDS_PER_DIRECTION: usize = 4; +const TLS_MUX_MAX_CARRIERS: usize = 8; #[derive(Clone)] pub(super) struct ClientSignals { @@ -147,7 +147,7 @@ mod quic; mod tls; pub(super) use self::quic::{QueuedDatagram, QuicManager, QuicSession}; -pub(super) use self::tls::{MuxDirection, OpenedTls, TlsManager}; +pub(super) use self::tls::{OpenedTls, TlsManager}; #[cfg(test)] #[path = "../tests/vector/session.rs"] diff --git a/src/vector/session/tls.rs b/src/vector/session/tls.rs index 1bea38a..78dd3a3 100644 --- a/src/vector/session/tls.rs +++ b/src/vector/session/tls.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: GPL-3.0-only use super::*; +use std::sync::atomic::AtomicUsize; +use tokio::sync::OnceCell; pub(in crate::vector) struct TlsManager { endpoint: Option<(String, crate::common::AddressFamily)>, @@ -12,28 +14,27 @@ pub(in crate::vector) struct TlsManager { stats: Arc, telemetry: Arc, latency: Arc, - up_mux: Mutex>, - down_mux: Mutex>, - up_mux_connect: Mutex<()>, - down_mux_connect: Mutex<()>, + mux: Mutex>>, mux_enabled: bool, } -#[derive(Clone, Copy)] -pub(in crate::vector) enum MuxDirection { - Up, - Down, -} - pub(in crate::vector) enum OpenedTls { Dedicated(Box), Mux(MuxStream), } -#[derive(Clone)] +#[derive(Default)] struct TlsMux { - handle: MuxHandle, - target_density: usize, + handle: OnceCell, + pending: AtomicUsize, +} + +struct PendingMux(Arc); + +impl Drop for PendingMux { + fn drop(&mut self) { + self.0.pending.fetch_sub(1, Ordering::Relaxed); + } } impl TlsManager { @@ -55,19 +56,12 @@ impl TlsManager { stats: signals.stats, telemetry: signals.telemetry, latency: signals.latency, - up_mux: Mutex::new(Vec::new()), - down_mux: Mutex::new(Vec::new()), - up_mux_connect: Mutex::new(()), - down_mux_connect: Mutex::new(()), + mux: Mutex::new(Vec::new()), mux_enabled: config.mux.enabled(), }) } - pub(in crate::vector) async fn open( - self: &Arc, - flow_id: u32, - direction: MuxDirection, - ) -> Result { + pub(in crate::vector) async fn open(self: &Arc, flow_id: u32) -> Result { if !self.mux_enabled { return self .connect_lane() @@ -75,33 +69,57 @@ impl TlsManager { .map(Box::new) .map(OpenedTls::Dedicated); } - // Serializing stream admission per direction makes the C1 decision - // exact: the selected shard records its new stream before the next - // opener observes load. - let _opening = self.mux_connect(direction).lock().await; - if let Some(shard) = self.available_mux(direction).await { - return shard - .handle - .open_stream(flow_id) - .await - .map(OpenedTls::Mux) - .map_err(Into::into); - } - if self.mux(direction).lock().await.len() >= TLS_MUX_MAX_SHARDS_PER_DIRECTION { - return Err(std::io::Error::new( - std::io::ErrorKind::WouldBlock, - "TLS mux pool has no stream capacity", - ) - .into()); - } - let connect_started = Instant::now(); - let lane = self.connect_lane().await?; + let pending = reserve_mux(&mut *self.mux.lock().await); + // Reservations include connecting slots, avoiding a cold-start stampede + // onto the first handshake to finish. OnceCell shares one initializer; + // cancellation lets another waiter retry without leaking a pool slot. + let handle = pending + .0 + .handle + .get_or_try_init(|| self.connect_mux(pending.0.clone())) + .await; + let handle = match handle { + Ok(handle) => handle.clone(), + Err(error) => { + let pool = self.mux.lock().await; + let Some(handle) = pool + .iter() + .filter_map(|carrier| carrier.handle.get()) + .filter(|handle| !handle.is_closed()) + .min_by_key(|handle| (handle.pressure(), handle.active_streams())) + .cloned() + else { + return Err(error); + }; + let stream = handle.prepare_stream(flow_id)?; + drop(pending); + drop(pool); + return handle + .open_prepared(stream) + .await + .map(OpenedTls::Mux) + .map_err(Into::into); + } + }; + // Registration and reservation release exclude idle retirement. + let pool = self.mux.lock().await; + let stream = handle.prepare_stream(flow_id)?; + drop(pending); + drop(pool); + handle + .open_prepared(stream) + .await + .map(OpenedTls::Mux) + .map_err(Into::into) + } + + async fn connect_mux(self: &Arc, slot: Arc) -> Result { let TlsLane { mut stream, pending_auth, _link, latency, - } = lane; + } = self.connect_lane().await?; stream .write_all(&pending_auth.expect("new TLS carrier has pending auth")) .await @@ -113,79 +131,39 @@ impl TlsManager { stream.flush().await?; let (handle, incoming) = MuxHandle::start(stream, MuxConfig::default())?; drop(incoming); - let shard = TlsMux { - handle, - target_density: mux_target_density(connect_started.elapsed()), - }; - self.mux(direction).lock().await.push(shard.clone()); + // No await after start until ownership is handed to the lifetime task. let manager = self.clone(); - let lifetime = shard.clone(); + let lifetime = handle.clone(); tokio::spawn(async move { - manager - .monitor_mux(direction, lifetime, _link, latency) - .await; + manager.monitor_mux(slot, lifetime, _link, latency).await; }); - shard - .handle - .open_stream(flow_id) - .await - .map(OpenedTls::Mux) - .map_err(Into::into) - } - - fn mux(&self, direction: MuxDirection) -> &Mutex> { - match direction { - MuxDirection::Up => &self.up_mux, - MuxDirection::Down => &self.down_mux, - } - } - - fn mux_connect(&self, direction: MuxDirection) -> &Mutex<()> { - match direction { - MuxDirection::Up => &self.up_mux_connect, - MuxDirection::Down => &self.down_mux_connect, - } - } - - async fn available_mux(&self, direction: MuxDirection) -> Option { - let mut muxes = self.mux(direction).lock().await; - muxes.retain(|shard| !shard.handle.is_closed()); - select_available_mux(&muxes) + Ok(handle) } async fn monitor_mux( self: Arc, - direction: MuxDirection, - shard: TlsMux, + slot: Arc, + carrier: MuxHandle, _link: LinkGuard, _latency: LatencyGuard, ) { loop { tokio::select! { - _ = shard.handle.closed() => break, - idle = shard.handle.idle_for(MUX_IDLE_TIMEOUT) => { - if !idle { - break; - } - } + _ = carrier.closed() => break, + idle = carrier.idle_for(MUX_IDLE_TIMEOUT) => { if !idle { break; } } } - let _opening = self.mux_connect(direction).lock().await; - if shard.handle.active_streams() != 0 { + let mut pool = self.mux.lock().await; + if carrier.active_streams() != 0 || slot.pending.load(Ordering::Relaxed) != 0 { continue; } - self.remove_mux(direction, &shard).await; - shard.handle.close(); + pool.retain(|candidate| !Arc::ptr_eq(candidate, &slot)); + carrier.close(); return; } - let _opening = self.mux_connect(direction).lock().await; - self.remove_mux(direction, &shard).await; - } - - async fn remove_mux(&self, direction: MuxDirection, shard: &TlsMux) { - self.mux(direction) + self.mux .lock() .await - .retain(|candidate| !candidate.handle.same_carrier(&shard.handle)); + .retain(|candidate| !Arc::ptr_eq(candidate, &slot)); } async fn connect_lane(&self) -> Result { @@ -214,32 +192,30 @@ impl TlsManager { } } -fn select_available_mux(muxes: &[TlsMux]) -> Option { - let available = muxes +fn reserve_mux(pool: &mut Vec>) -> PendingMux { + pool.retain(|carrier| !carrier.handle.get().is_some_and(MuxHandle::is_closed)); + let selected = pool .iter() - .filter(|shard| !shard.handle.is_closed()) - .min_by_key(|shard| shard.handle.active_streams()) - .cloned()?; - let active = available.handle.active_streams(); - if !available.handle.has_stream_capacity() { - return None; - } - if (active < available.target_density && !available.handle.is_send_congested()) - || muxes.len() >= TLS_MUX_MAX_SHARDS_PER_DIRECTION - { - Some(available) - } else { - None - } -} - -fn mux_target_density(setup_latency: Duration) -> usize { - match setup_latency.as_millis() { - 0..30 => 16, - 30..75 => 8, - 75..200 => 4, - _ => 2, - } + .map(|carrier| { + let handle = carrier.handle.get(); + let active = carrier.pending.load(Ordering::Relaxed) + + handle.map_or(0, MuxHandle::active_streams); + let pressure = handle.map_or(0, MuxHandle::pressure); + (carrier, active, pressure) + }) + .min_by_key(|(_, active, pressure)| (*active != 0, *pressure, *active)); + let carrier = match selected { + Some((carrier, active, _)) if active == 0 || pool.len() >= TLS_MUX_MAX_CARRIERS => { + carrier.clone() + } + _ => { + let carrier = Arc::new(TlsMux::default()); + pool.push(carrier.clone()); + carrier + } + }; + carrier.pending.fetch_add(1, Ordering::Relaxed); + PendingMux(carrier) } #[cfg(test)] diff --git a/tests/mux-bench-local.py b/tests/mux-bench-local.py index 5ec329a..9461f52 100755 --- a/tests/mux-bench-local.py +++ b/tests/mux-bench-local.py @@ -69,7 +69,7 @@ def run_flow(port, target_port, byte_count, barrier, results, index): try: sock = socket.create_connection(("127.0.0.1", port), timeout=60) sock.sendall(b"\x05\x01\x00") - if sock.recv(2) != b"\x05\x00": + if receive_bytes(sock, 2) != b"\x05\x00": raise RuntimeError("SOCKS authentication failed") sock.sendall(b"\x05\x01\x00\x01\x7f\x00\x00\x01" + struct.pack("!H", target_port)) reply = receive_reply(sock) @@ -147,8 +147,12 @@ def main(): parser.add_argument("--profile", default="throughput") parser.add_argument("--proxy-host", default="127.0.0.1") parser.add_argument("--external-proxy", action="store_true") + parser.add_argument("--direct", action="store_true", help="loopback without Toxiproxy; RTT must be zero") parser.add_argument("--debug", action="store_true") args = parser.parse_args() + if args.direct and args.rtt_ms != 0: + parser.error("--direct requires --rtt-ms 0") + proxy_port = 2000 if args.direct else 2001 env = os.environ.copy() env.update({"NOW_TRANSPORT_MEMORY_PROFILE": args.profile, "RUST_LOG": "off"}) @@ -171,13 +175,13 @@ def main(): server.start() try: wait_port(2000) - if not args.external_proxy: + if not args.external_proxy and not args.direct: api("DELETE", "/proxies/nowhere") except urllib.error.HTTPError as error: if error.code != 404: raise try: - if not args.external_proxy: + if not args.external_proxy and not args.direct: api("POST", "/proxies", { "name": "nowhere", "listen": "0.0.0.0:2001", "upstream": "192.168.64.1:2000", }) @@ -187,12 +191,12 @@ def main(): "name": f"latency-{stream}", "type": "latency", "stream": stream, "toxicity": 1.0, "attributes": {"latency": latency, "jitter": 0}, }) - wait_port(2001, args.proxy_host) + wait_port(proxy_port, args.proxy_host) # Let a just-updated proxy path complete one RTT before the measured # client opens its first carrier. Transfer timing starts later. time.sleep(max(args.rtt_ms * 2 / 1000, 1.0)) vector = subprocess.Popen( - [args.binary, f"vector://secret@{args.proxy_host}:2001?mux={args.mux}&socks=127.0.0.1:1080&log={log}"], + [args.binary, f"vector://secret@{args.proxy_host}:{proxy_port}?mux={args.mux}&socks=127.0.0.1:1080&log={log}"], env=env, stdout=output, stderr=output, ) wait_port(1080) @@ -236,6 +240,7 @@ def sample(): total = byte_count * args.flows transfer = max(durations) result = { + "binary": args.binary, "rtt_ms": args.rtt_ms, "flows": args.flows, "mux": int(args.mux), "profile": args.profile, "mib": total / 1024 / 1024, "seconds": round(transfer, 3), "mbps": round(total * 8 / transfer / 1_000_000, 2), From 34f8caa6c2073b915d54b2352dd978b0ee2a3243 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Tue, 8 Sep 2026 18:55:15 +0800 Subject: [PATCH 30/62] Remove fixed application flow limits and grow QUIC stream credit --- docs/configuration.md | 16 +-- docs/mux-performance.md | 3 +- docs/operations.md | 17 ++- docs/protocol.md | 19 ++- docs/security.md | 35 ++---- src/common/config.rs | 14 --- src/common/mod.rs | 12 +- src/main.rs | 3 - src/portal/config.rs | 48 +------- src/portal/conn.rs | 8 +- src/portal/conn/session.rs | 18 ++- src/portal/mod.rs | 1 - src/portal/pairing.rs | 103 ++++++---------- src/portal/pairing/link.rs | 8 +- src/portal/pairing/state.rs | 28 ++--- src/portal/pairing/tcp.rs | 7 +- src/portal/pairing/udp.rs | 38 +----- src/portal/setup.rs | 4 - src/tests/main.rs | 12 +- src/tests/portal/config.rs | 44 +++---- src/tests/portal/conn/quic.rs | 74 ++++------- src/tests/portal/conn/support.rs | 3 - src/tests/portal/pairing.rs | 36 +----- src/tests/portal/pairing/lifecycle.rs | 156 +++++++++++------------- src/tests/portal/pairing/rejection.rs | 6 +- src/tests/portal/pairing/replacement.rs | 6 +- src/tests/portal/pairing/udp.rs | 143 ++++++---------------- src/tests/vector/flow_id.rs | 7 +- src/vector/flow.rs | 1 - src/vector/flow/tcp.rs | 20 +-- src/vector/flow_id.rs | 12 +- src/vector/mod.rs | 16 +-- src/vector/socks/server.rs | 13 -- src/vector/socks/server/udp.rs | 7 +- src/vector/udp_flow.rs | 8 -- 35 files changed, 306 insertions(+), 640 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 8209cd2..84f0025 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -289,11 +289,8 @@ Durations use humantime syntax such as `250ms`, `15s`, `2m`, or `1h`. | Variable | Default | Purpose | |---|---:|---| -| `NOW_MAX_TCP_FLOWS` | `1024` | TCP flows per authenticated client session | -| `NOW_MAX_UDP_FLOWS` | `256` | UDP flows per authenticated client session | | `NOW_QUIC_UDP_QUEUE_BYTES` | `4 MiB` | QUIC datagram and reassembly byte budget | | `NOW_TRANSPORT_MEMORY_PROFILE` | `throughput` | QUIC and TLS Mux profile: `memory`, `balanced`, or `throughput` | -| `NOW_MAX_PENDING_PAIRS` | `1024` | Pending split-flow pairs per Portal session | | `NOW_FLOW_PAIR_TIMEOUT` | `15s` | Portal split-flow pairing deadline | | `NOW_FLOW_SETUP_TIMEOUT` | `20s` | Client wait for `SetupResult` | | `NOW_MIX_FALLBACK_TIMEOUT` | `1s` | Primary Mix route preparation budget before fallback | @@ -317,11 +314,14 @@ window. Each flow has at most one DATA frame queued or being written, so a bulk writer cannot fill the shared queue. Receive queues are bounded by byte credit without blocking unrelated flows on per-flow frame counts. The application shares at most eight carriers across both directions and retires -fully idle shards after 30 seconds. `NOW_MAX_TCP_FLOWS` is the hard -per-session logical TCP limit shared by TLS and QUIC. `NOW_MAX_UDP_FLOWS` is the -corresponding UDP limit shared by UoT and QUIC DATAGRAM. Excess flows fail -without waiting for capacity. QUIC internally admits the sum of both limits as -bidirectional streams; this derived capacity has no separate setting. +fully idle shards after 30 seconds. There are no fixed application flow-count +limits for TCP, UDP, SOCKS associations, or pending split pairs. The former +`NOW_MAX_TCP_FLOWS`, `NOW_MAX_UDP_FLOWS`, and `NOW_MAX_PENDING_PAIRS` settings +are removed and ignored. QUIC stream credit grows with live and pending QUIC +flows, reserving setup headroom of at least 64 streams or 25% of that count. +This avoids a fixed active-flow ceiling and excessive eager stream allocation. +Byte budgets and setup, pairing, and idle deadlines still apply; per-flow +metadata and target sockets grow with concurrency. Portal and Vector use the same QUIC profile regardless of the client Mux setting. The stream/connection/send windows are respectively 4/8/8 MiB for `memory`, diff --git a/docs/mux-performance.md b/docs/mux-performance.md index 0e0a17e..7fbc69b 100644 --- a/docs/mux-performance.md +++ b/docs/mux-performance.md @@ -129,7 +129,8 @@ more. At capacity, placement uses credit/queue occupancy, then live plus pending streams. Connecting slots accept reservations and share one initializer, so a cold burst is balanced before handshakes finish. There is no fixed Mux stream count limit, setup-latency density rule, or scheduling timer. Application session -admission limits remain separate. +admission limits were still present in these measurements; the subsequent +consistent-admission cleanup removes them without changing pool placement. Each stream may have one queued/in-progress DATA frame. Receive queues rely on byte credit rather than a per-stream packet count, removing the slow-small-packet diff --git a/docs/operations.md b/docs/operations.md index bf9e762..7998639 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -53,20 +53,19 @@ the other family. ## Capacity -The important memory bounds are the 1,024 concurrent TCP flows and 256 UDP flows -per authenticated client session, the selected 4/8, 8/16, or 16/32 MiB -per-stream/per-Mux receive -windows, bounded reusable relay-buffer caches, and QUIC UDP -queue/reassembly limits. UoT and QUIC DATAGRAM share the UDP flow limit. TLS +Payload memory is controlled by the selected 4/8, 8/16, or 16/32 MiB +per-stream/per-Mux receive windows, bounded reusable relay-buffer caches, and +QUIC UDP queue/reassembly limits. Logical TCP, UDP, SOCKS, and pending-pair +counts have no fixed application cap; metadata and sockets grow with concurrency. +TLS shards originated with `mux=1` by Vector or a Portal `next` client adapt their pool to concurrent flow demand, stop at eight carriers per session across both directions, use lowest-occupancy placement, and close after 30 seconds fully idle. Frame queue slots do not bypass byte credit. Windows are granted as permits and payload is admitted incrementally. -At a session flow limit, TCP setup returns a failure immediately. A SOCKS5 UDP -packet whose logical route cannot be admitted receives no UDP response; the -association remains available for existing routes. +QUIC stream credit grows with live and pending QUIC flows plus setup headroom. +Pairing and setup deadlines still reclaim incomplete requests. QUIC uses the shared `throughput` memory profile by default. Select `balanced` or `memory` when connection density matters more than a single flow's @@ -75,7 +74,7 @@ bandwidth-delay product. ### TLS Shard placement An originating client shares one full-duplex TLS carrier pool across directions. -Mux imposes no fixed stream count limit; session application admission is separate. +Mux and application sessions impose no fixed logical-flow count limit. ```text new flow --> idle carrier? --> reuse diff --git a/docs/protocol.md b/docs/protocol.md index 30f4ff4..fb60f84 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -289,8 +289,7 @@ Receive queues use byte-credit admission rather than blocking the carrier reader on a per-flow frame count. Every DATA frame consumes at least one KiB of credit, bounding queued payload and DATA metadata across the carrier. Stream and lifecycle metadata still grow with live/pending streams; no fixed stream limit does not mean -constant process memory. Authentication and application-level admission policies -remain separate from Mux placement. A fully idle carrier closes after 30 +constant process memory. Authentication remains separate from Mux placement. A fully idle carrier closes after 30 seconds. Portal applies the same timeout to an authenticated Mux carrier with no active streams. Sharding is runtime placement and does not add wire fields. @@ -427,7 +426,7 @@ SetupResult - 1 byte | `0x01` | INVALID_REQUEST | malformed or carrier-inconsistent setup | | `0x02` | METADATA_CONFLICT | OPEN and ATTACH metadata conflict | | `0x03` | PAIR_TIMEOUT | the matching split lane did not arrive | -| `0x04` | FLOW_LIMIT | admission, session flow, or forwarding limit reached | +| `0x04` | FLOW_LIMIT | admission or forwarding limit reached | | `0x05` | DIAL_FAILED | target or upstream connection failed | | `0x06` | SESSION_REPLACED | a newer authenticated carrier replaced this session state | | `0x07` | INTERNAL_ERROR | local processing failure | @@ -543,14 +542,14 @@ ATTACH. ## 11. Runtime limits and failure scope -One authenticated client session admits 1,024 concurrent logical TCP flows and -256 concurrent logical UDP flows by default. Pending flows count toward the -same limits. A full-duplex flow counts once regardless of its carrier -combination. Admission at the limit returns FLOW_LIMIT without waiting. +Application sessions impose no fixed TCP, UDP, or pending-pair count limit. +Flow IDs remain unique within their wire identifier space. Byte flow control, +queue budgets, and pairing/setup timeouts remain enforced. -The QUIC bidirectional-stream ceiling is derived from both flow limits: 1,280 -by default. A QUIC TCP flow owns one reliable stream. A QUIC UDP flow owns one -reliable control stream plus its DATAGRAM route. +QUIC bidirectional-stream credit grows with live and pending QUIC flows, with +setup headroom of max(64, live / 4). A QUIC TCP flow owns one reliable stream; +a QUIC UDP flow owns one reliable control stream plus its DATAGRAM route. +This is sliding transport credit, not a fixed application concurrency ceiling. Failure scope follows the physical carrier: diff --git a/docs/security.md b/docs/security.md index 1013f99..12c9269 100644 --- a/docs/security.md +++ b/docs/security.md @@ -78,38 +78,21 @@ and closes a fully idle carrier after 30 seconds. Stream and pending lifecycle metadata remain proportional to admitted streams; byte credit bounds DATA, not arbitrary OPEN traffic. One authenticated inbound Mux carrier is subject to the same fully idle timeout. -One authenticated client session admits at most 1,024 concurrent logical TCP -flows and 256 logical UDP flows across all of its carriers. UoT and QUIC -DATAGRAM flows share the UDP limit. +Authenticated sessions have no fixed logical-flow count limit. Stream metadata, +pending pairs, and target sockets grow with concurrency; byte windows do not +bound their total memory use. Per-stream and connection credit plus bounded channel admission limit how much one stream can occupy. The finite frame queue has 512 slots, but payload admission is capped by the selected connection window; empty -SYN/FIN/WINDOW frames cannot turn those slots into +OPEN/CLOSE/WINDOW frames cannot turn those slots into retained application payload. These are credit ceilings rather than eagerly allocated payload buffers. -```text -authenticated client session - | - +-- TCP budget: 1,024 active flows - | | - | +-- dedicated TLS lane - | +-- Mux stream --> adaptive TLS Shard - | +-- QUIC reliable stream - | - +-- UDP budget: 256 active flows - | - +-- UoT stream --> dedicated TLS lane or Mux Shard - +-- QUIC control stream + DATAGRAM route -``` - -The TCP and UDP budgets are per authenticated session rather than process-wide. -Multiple sessions using the same shared key receive independent flow budgets. -All Shards from one session share its TCP or UDP admission budget. The shared -key is a credential, not a stable user identity, so Portal does not aggregate -these limits across every client that knows the same key. Operators control -aggregate exposure through key distribution, host resource limits, and -network-level admission policy. +TCP, UoT, and QUIC flows all follow the same policy: byte budgets and lifecycle +timeouts remain, without fixed application flow-count admission. QUIC expands +stream credit with actual demand instead of preallocating a huge stream ceiling. +Operators control aggregate exposure through key distribution, host resource +limits, and network-level admission policy. Relay scratch buffers use bounded reuse caches: each process retains at most 64 TCP buffers and 32 UDP buffers. A short-lived concurrency spike therefore diff --git a/src/common/config.rs b/src/common/config.rs index a22fbb8..2d8664a 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -21,10 +21,6 @@ pub const DEFAULT_TELEMETRY_INTERVAL: Duration = Duration::from_secs(1); pub const MIN_TELEMETRY_INTERVAL: Duration = Duration::from_millis(250); /// Slowest supported structured telemetry cadence. pub const MAX_TELEMETRY_INTERVAL: Duration = Duration::from_secs(60); -/// Default concurrent logical TCP flows per authenticated client session. -pub const DEFAULT_MAX_TCP_FLOWS: u32 = 1024; -/// Default concurrent logical UDP flows per authenticated client session. -pub const DEFAULT_MAX_UDP_FLOWS: usize = 256; /// Parses the first value of each recognized URL query key without treating /// `+` as a space. Unknown keys and later duplicates are ignored. @@ -110,16 +106,6 @@ pub fn rate_limit_bytes_per_second(mbps: i32) -> u64 { if mbps <= 0 { 0 } else { mbps as u64 * 125_000 } } -/// Maximum concurrent logical TCP flows in one authenticated client session. -pub fn max_tcp_flows() -> u32 { - env_int("NOW_MAX_TCP_FLOWS", DEFAULT_MAX_TCP_FLOWS as i32) as u32 -} - -/// Maximum concurrent logical UDP flows in one authenticated client session. -pub fn max_udp_flows() -> usize { - env_int("NOW_MAX_UDP_FLOWS", DEFAULT_MAX_UDP_FLOWS as i32).max(1) as usize -} - /// Per-direction TCP relay buffer size. pub fn tcp_data_buf_size() -> usize { env_int("NOW_TCP_DATA_BUF_SIZE", 32 * 1024) as usize diff --git a/src/common/mod.rs b/src/common/mod.rs index 9253579..c6fff61 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -17,12 +17,12 @@ mod tls; pub(crate) use alpn::MUX_MARKER; pub(crate) use config::first_raw_query_value; pub use config::{ - DEFAULT_DIALER_IP, DEFAULT_MAX_TCP_FLOWS, DEFAULT_MAX_UDP_FLOWS, DEFAULT_RATE_LIMIT, - DEFAULT_TELEMETRY_INTERVAL, MAX_TELEMETRY_INTERVAL, MIN_TELEMETRY_INTERVAL, env_duration, - env_int, flow_setup_timeout, handshake_timeout, init_dialer_ip, max_tcp_flows, max_udp_flows, - mix_fallback_timeout, query_first, rate_limit_bytes_per_second, reload_interval, - report_interval, service_cooldown, shutdown_timeout, tcp_data_buf_size, tcp_dial_timeout, - tcp_read_timeout, telemetry_interval, udp_data_buf_size, udp_dial_timeout, udp_idle_timeout, + DEFAULT_DIALER_IP, DEFAULT_RATE_LIMIT, DEFAULT_TELEMETRY_INTERVAL, MAX_TELEMETRY_INTERVAL, + MIN_TELEMETRY_INTERVAL, env_duration, env_int, flow_setup_timeout, handshake_timeout, + init_dialer_ip, mix_fallback_timeout, query_first, rate_limit_bytes_per_second, + reload_interval, report_interval, service_cooldown, shutdown_timeout, tcp_data_buf_size, + tcp_dial_timeout, tcp_read_timeout, telemetry_interval, udp_data_buf_size, udp_dial_timeout, + udp_idle_timeout, }; pub(crate) use datagram::{ BudgetedDatagram, UdpDatagramSend, reserve_udp_budget, send_quic_udp_packet, diff --git a/src/main.rs b/src/main.rs index 81b803f..dc53f3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -139,11 +139,8 @@ SOCKS5 inbound: SOCKS5 UDP fragmentation is not supported. Environment: - NOW_MAX_TCP_FLOWS TCP flows per authenticated client session. - NOW_MAX_UDP_FLOWS UDP flows per authenticated client session. NOW_QUIC_UDP_QUEUE_BYTES Maximum queued/reassembling UDP bytes per QUIC connection. NOW_TRANSPORT_MEMORY_PROFILE memory, balanced, or throughput. Default: throughput. - NOW_MAX_PENDING_PAIRS Maximum pending logical-flow IDs per session. NOW_FLOW_PAIR_TIMEOUT Timeout for completing a split logical flow. NOW_FLOW_SETUP_TIMEOUT Timeout for waiting for a logical flow to become ready. NOW_MIX_FALLBACK_TIMEOUT Primary Mix route preparation budget. Default: 1s. diff --git a/src/portal/config.rs b/src/portal/config.rs index 47e7384..ffd278e 100644 --- a/src/portal/config.rs +++ b/src/portal/config.rs @@ -8,10 +8,7 @@ use std::time::Duration; use anyhow::{Context, Result, bail}; use tokio::sync::Semaphore; -use crate::common::{ - DEFAULT_MAX_TCP_FLOWS, DEFAULT_MAX_UDP_FLOWS, DEFAULT_TELEMETRY_INTERVAL, - MAX_TELEMETRY_INTERVAL, MIN_TELEMETRY_INTERVAL, -}; +use crate::common::{DEFAULT_TELEMETRY_INTERVAL, MAX_TELEMETRY_INTERVAL, MIN_TELEMETRY_INTERVAL}; use super::DEFAULT_QUIC_UDP_QUEUE_BYTES; @@ -25,13 +22,10 @@ const DEFAULT_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(5); const DEFAULT_REPORT_INTERVAL: Duration = Duration::from_secs(5); const DEFAULT_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5); const DEFAULT_RELOAD_INTERVAL: Duration = Duration::from_secs(60 * 60); -const DEFAULT_MAX_PENDING_PAIRS: usize = 1024; const DEFAULT_FLOW_PAIR_TIMEOUT: Duration = Duration::from_secs(15); #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(super) struct PortalRuntimeConfig { - pub(super) max_tcp_flows: u32, - pub(super) max_udp_flows: usize, pub(super) udp_queue_bytes: usize, pub(super) tcp_data_buf_size: usize, pub(super) udp_data_buf_size: usize, @@ -44,15 +38,10 @@ pub(super) struct PortalRuntimeConfig { pub(super) telemetry_interval: Duration, pub(super) shutdown_timeout: Duration, pub(super) reload_interval: Duration, - pub(super) max_pending_pairs: usize, pub(super) flow_pair_timeout: Duration, } impl PortalRuntimeConfig { - pub(super) fn quic_bidi_stream_capacity(&self) -> u32 { - self.max_tcp_flows + self.max_udp_flows as u32 - } - pub(super) fn from_env() -> Result { Self::from_source(|name| match std::env::var(name) { Ok(value) => Ok(Some(value)), @@ -67,16 +56,6 @@ impl PortalRuntimeConfig { where F: FnMut(&str) -> Result>, { - let max_tcp_flows = read_u32(&mut source, "NOW_MAX_TCP_FLOWS", DEFAULT_MAX_TCP_FLOWS)?; - let max_udp_flows = read_usize( - &mut source, - "NOW_MAX_UDP_FLOWS", - DEFAULT_MAX_UDP_FLOWS, - (u32::MAX as usize).min(Semaphore::MAX_PERMITS), - )?; - max_tcp_flows - .checked_add(max_udp_flows as u32) - .context("portal::config: NOW_MAX_TCP_FLOWS + NOW_MAX_UDP_FLOWS exceeds u32")?; let udp_queue_bytes = read_usize( &mut source, "NOW_QUIC_UDP_QUEUE_BYTES", @@ -143,12 +122,6 @@ impl PortalRuntimeConfig { )?; let reload_interval = read_duration(&mut source, "NOW_RELOAD_INTERVAL", DEFAULT_RELOAD_INTERVAL)?; - let max_pending_pairs = read_usize( - &mut source, - "NOW_MAX_PENDING_PAIRS", - DEFAULT_MAX_PENDING_PAIRS, - isize::MAX as usize, - )?; let flow_pair_timeout = read_duration( &mut source, "NOW_FLOW_PAIR_TIMEOUT", @@ -156,8 +129,6 @@ impl PortalRuntimeConfig { )?; Ok(Self { - max_tcp_flows, - max_udp_flows, udp_queue_bytes, tcp_data_buf_size, udp_data_buf_size, @@ -170,28 +141,11 @@ impl PortalRuntimeConfig { telemetry_interval, shutdown_timeout, reload_interval, - max_pending_pairs, flow_pair_timeout, }) } } -fn read_u32(source: &mut F, name: &str, default: u32) -> Result -where - F: FnMut(&str) -> Result>, -{ - let Some(raw) = source(name)? else { - return Ok(default); - }; - let value = raw - .parse::() - .with_context(|| format!("portal::config: invalid {name}={raw:?}"))?; - if value == 0 { - bail!("portal::config: {name} must be greater than zero: {raw:?}"); - } - Ok(value) -} - fn read_usize(source: &mut F, name: &str, default: usize, max: usize) -> Result where F: FnMut(&str) -> Result>, diff --git a/src/portal/conn.rs b/src/portal/conn.rs index 65c484f..a3f8428 100644 --- a/src/portal/conn.rs +++ b/src/portal/conn.rs @@ -121,9 +121,11 @@ async fn handle_connection( } }; conn.set_receive_window(VarInt::from_u32(flow_control.connection_receive_window)); - conn.set_max_concurrent_bi_streams(VarInt::from_u32( - portal.runtime.quic_bidi_stream_capacity(), - )); + conn.set_max_concurrent_bi_streams( + portal + .pairing + .quic_stream_credit(authenticated.session.session_key), + ); drop(admission); let session = authenticated.session; let link_replaced = CancellationToken::new(); diff --git a/src/portal/conn/session.rs b/src/portal/conn/session.rs index 137659d..cc09834 100644 --- a/src/portal/conn/session.rs +++ b/src/portal/conn/session.rs @@ -222,12 +222,15 @@ impl PortalSession { FlowRole::Attach => (None, Some(Box::pin(send) as _)), FlowRole::Duplex => (Some(Box::pin(recv) as _), Some(Box::pin(send) as _)), }; - match self + let paired = self .portal .pairing .submit_tcp(self.session_key, header, target, link, reader, writer, None) - .await - { + .await; + self.conn.set_max_concurrent_bi_streams( + self.portal.pairing.quic_stream_credit(self.session_key), + ); + match paired { Ok(Some(paired)) => { let relay = super::relay::relay_paired_tcp(self.portal.clone(), paired); if let Some(relay) = self.portal.relay_tasks.spawn_or_return(relay) { @@ -278,12 +281,15 @@ impl PortalSession { } } }; - match self + let paired = self .portal .pairing .submit_udp(self.session_key, header, target, link, half) - .await - { + .await; + self.conn.set_max_concurrent_bi_streams( + self.portal.pairing.quic_stream_credit(self.session_key), + ); + match paired { Ok(Some(paired)) => { let relay = super::relay::relay_paired_udp(self.portal.clone(), paired); if let Some(relay) = self.portal.relay_tasks.spawn_or_return(relay) { diff --git a/src/portal/mod.rs b/src/portal/mod.rs index 84ee744..b32a843 100644 --- a/src/portal/mod.rs +++ b/src/portal/mod.rs @@ -32,7 +32,6 @@ const DEFAULT_QUIC_UDP_QUEUE_BYTES: usize = 4 * 1024 * 1024; #[derive(Clone, Copy, Debug)] struct UdpFlowLimits { - max_flows: usize, queue_bytes: usize, } diff --git a/src/portal/pairing.rs b/src/portal/pairing.rs index 3e66c35..6340722 100644 --- a/src/portal/pairing.rs +++ b/src/portal/pairing.rs @@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::time::{Duration, Instant}; use tokio::io::AsyncWriteExt; -use tokio::sync::{Mutex, OwnedSemaphorePermit}; +use tokio::sync::Mutex; use crate::protocol::{ Carrier, FlowErrorCode, FlowHeader, FlowKind, FlowResult, FlowRole, Target, write_flow_result, @@ -74,19 +74,14 @@ pub(super) struct PairingRegistry { accepting: AtomicBool, pub(super) next_quic_generation: AtomicU64, next_epoch: AtomicU64, - pub(super) max_pending: usize, pub(super) timeout: Duration, - pub(super) max_tcp_flows: usize, - pub(super) max_udp_flows: usize, } +// Bounded failure-history cache, independent of live/pending flow admission. +const MAX_REJECTION_TOMBSTONES: usize = 1024; + impl PairingRegistry { - pub(super) fn new( - max_tcp_flows: usize, - max_udp_flows: usize, - max_pending: usize, - timeout: Duration, - ) -> Self { + pub(super) fn new(timeout: Duration) -> Self { Self { tcp: Mutex::new(HashMap::new()), udp: Mutex::new(HashMap::new()), @@ -96,10 +91,7 @@ impl PairingRegistry { accepting: AtomicBool::new(true), next_quic_generation: AtomicU64::new(1), next_epoch: AtomicU64::new(1), - max_pending, timeout, - max_tcp_flows, - max_udp_flows, } } @@ -188,6 +180,7 @@ impl PairingRegistry { metadata: Metadata, target: Option, quic_generation: Option, + quic_count: Option>, ) -> Result<(u64, bool), PairingError> { let mut claims = self.claims.lock().expect("flow claim registry poisoned"); // The claims lock is the drain/admission linearization point. Once @@ -224,35 +217,16 @@ impl PairingRegistry { } return Ok((claim.epoch, false)); } - if metadata.kind == FlowKind::Tcp - && claims - .iter() - .filter(|(flow, claim)| { - flow.session_id == key.session_id && claim.metadata.kind == FlowKind::Tcp - }) - .count() - >= self.max_tcp_flows - { - return Err(PairingError::new( - FlowErrorCode::FlowLimit, - "portal::pairing: TCP flow limit reached", - )); - } - if claims - .iter() - .filter(|(flow, claim)| flow.session_id == key.session_id && !claim.active) - .count() - >= self.max_pending - { - return Err(PairingError::new( - FlowErrorCode::FlowLimit, - "portal::pairing: pending flow limit reached", - )); - } let epoch = self.next_epoch.fetch_add(1, Ordering::Relaxed); + let quic_count = quic_count + .filter(|_| metadata.uplink == Carrier::Quic || metadata.downlink == Carrier::Quic); + if let Some(count) = &quic_count { + count.fetch_add(1, Ordering::Relaxed); + } claims.insert( key, FlowClaim { + quic_count, epoch, metadata, target, @@ -264,6 +238,31 @@ impl PairingRegistry { Ok((epoch, true)) } + fn quic_flow_counter( + &self, + session_id: SessionKey, + ) -> Option> { + self.links + .lock() + .expect("link registry poisoned") + .get(&session_id) + .map(|counts| counts.quic_flows.clone()) + } + + pub(super) fn quic_stream_credit(&self, session_id: SessionKey) -> quinn::VarInt { + // Quinn preallocates stream state. Keep a sliding headroom for setup, + // instead of advertising a huge fixed count or capping active flows. + let live = self + .quic_flow_counter(session_id) + .map_or(0, |count| count.load(Ordering::Relaxed)); + // Quinn batches MAX_STREAMS updates at 1/8 of its window. Headroom + // must grow too, otherwise a fixed reserve eventually stalls updates. + quinn::VarInt::from_u32( + live.saturating_add((live / 4).max(64)) + .min(u32::MAX as usize) as u32, + ) + } + fn refresh_claim(&self, key: FlowKey) -> Result { let epoch = self.next_epoch.fetch_add(1, Ordering::Relaxed); let mut claims = self.claims.lock().expect("flow claim registry poisoned"); @@ -292,7 +291,6 @@ impl PairingRegistry { key: FlowKey, epoch: u64, quic_generations: Vec, - udp_permit: Option>, ) -> Result { // `links -> claims` is the linearization barrier shared with QUIC // replacement. A generation cannot become active after it has been @@ -339,31 +337,6 @@ impl PairingRegistry { key, epoch, cancel, - _udp_permit: udp_permit, - }) - } - - fn acquire_udp_permit( - &self, - session_id: SessionKey, - ) -> Result, PairingError> { - let budget = self - .links - .lock() - .expect("link registry poisoned") - .get(&session_id) - .map(|counts| counts.udp_flow_budget.clone()) - .ok_or_else(|| { - PairingError::new( - FlowErrorCode::SessionReplaced, - "portal::pairing: missing authenticated session", - ) - })?; - budget.try_acquire_owned().map(Arc::new).map_err(|_| { - PairingError::new( - FlowErrorCode::FlowLimit, - "portal::pairing: UDP flow limit reached", - ) }) } @@ -414,7 +387,7 @@ impl PairingRegistry { let now = Instant::now(); rejections.retain(|_, rejection| rejection.expires_at > now); if !rejections.contains_key(&key) - && rejections.len() >= self.max_pending + && rejections.len() >= MAX_REJECTION_TOMBSTONES && let Some(oldest) = rejections .iter() .min_by_key(|(_, rejection)| rejection.expires_at) diff --git a/src/portal/pairing/link.rs b/src/portal/pairing/link.rs index e547851..9b27615 100644 --- a/src/portal/pairing/link.rs +++ b/src/portal/pairing/link.rs @@ -137,9 +137,7 @@ impl PairingRegistry { ) -> LinkGuard { let session_id = session_id.into(); let mut links = self.links.lock().expect("link registry poisoned"); - let counts = links - .entry(session_id) - .or_insert_with(|| super::state::LinkCounts::new(self.max_udp_flows)); + let counts = links.entry(session_id).or_default(); counts.tcp += 1; stats.link_tcp.fetch_add(1, Ordering::Relaxed); drop(links); @@ -163,9 +161,7 @@ impl PairingRegistry { let generation = self.next_quic_generation.fetch_add(1, Ordering::Relaxed); let previous = { let mut links = self.links.lock().expect("link registry poisoned"); - let counts = links - .entry(session_id) - .or_insert_with(|| super::state::LinkCounts::new(self.max_udp_flows)); + let counts = links.entry(session_id).or_default(); let previous = counts.udp.replace(ActiveQuic { generation, replacement, diff --git a/src/portal/pairing/state.rs b/src/portal/pairing/state.rs index 3d5e30d..086c3da 100644 --- a/src/portal/pairing/state.rs +++ b/src/portal/pairing/state.rs @@ -5,10 +5,10 @@ use std::pin::Pin; use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use quinn::Connection; -use tokio::sync::{OwnedSemaphorePermit, Semaphore, mpsc, oneshot}; +use tokio::sync::{mpsc, oneshot}; use tokio_util::sync::CancellationToken; use crate::protocol::{FlowKind, SessionId, Target}; @@ -165,7 +165,6 @@ pub(in crate::portal) struct PendingUdp { pub(in crate::portal) target: Option, pub(in crate::portal) uplink: Option, pub(in crate::portal) downlink: Option, - pub(in crate::portal) flow_permit: Option>, pub(in crate::portal) uplink_path: Option, pub(in crate::portal) downlink_path: Option, pub(in crate::portal) uplink_generation: Option, @@ -199,20 +198,11 @@ pub(in crate::portal) struct PairedTcp { pub(in crate::portal) _flow_lease: FlowLease, } +#[derive(Default)] pub(in crate::portal) struct LinkCounts { pub(in crate::portal) tcp: usize, pub(in crate::portal) udp: Option, - pub(in crate::portal) udp_flow_budget: Arc, -} - -impl LinkCounts { - pub(in crate::portal) fn new(max_udp_flows: usize) -> Self { - Self { - tcp: 0, - udp: None, - udp_flow_budget: Arc::new(Semaphore::new(max_udp_flows)), - } - } + pub(in crate::portal) quic_flows: Arc, } pub(in crate::portal) struct ActiveQuic { @@ -221,6 +211,7 @@ pub(in crate::portal) struct ActiveQuic { } pub(in crate::portal) struct FlowClaim { + pub(in crate::portal) quic_count: Option>, pub(in crate::portal) epoch: u64, pub(in crate::portal) metadata: Metadata, pub(in crate::portal) target: Option, @@ -229,12 +220,19 @@ pub(in crate::portal) struct FlowClaim { pub(in crate::portal) quic_generations: Vec, } +impl Drop for FlowClaim { + fn drop(&mut self) { + if let Some(count) = &self.quic_count { + count.fetch_sub(1, Ordering::Relaxed); + } + } +} + pub(in crate::portal) struct FlowLease { pub(in crate::portal) registry: std::sync::Weak, pub(in crate::portal) key: FlowKey, pub(in crate::portal) epoch: u64, pub(in crate::portal) cancel: CancellationToken, - pub(in crate::portal) _udp_permit: Option>, } impl FlowLease { diff --git a/src/portal/pairing/tcp.rs b/src/portal/pairing/tcp.rs index c466ed3..c808df5 100644 --- a/src/portal/pairing/tcp.rs +++ b/src/portal/pairing/tcp.rs @@ -31,6 +31,7 @@ impl PairingRegistry { downlink_liveness: Option, ) -> Result, PairingError> { let session_id = session_id.into(); + let quic_count = self.quic_flow_counter(session_id); if let Err(err) = self.validate_header_and_link(session_id, header, FlowKind::Tcp, target.as_ref(), &link) { @@ -79,6 +80,7 @@ impl PairingRegistry { metadata.clone(), target.clone(), link.quic_generation, + quic_count.clone(), ) { Ok(claim) => claim, Err(err) => { @@ -97,7 +99,7 @@ impl PairingRegistry { let uplink = reader.expect("duplex TCP reader validated"); let downlink = writer.take().expect("duplex TCP writer validated"); let generations = link.quic_generation.into_iter().collect(); - let lease = match self.activate_claim(key, claim_epoch, generations, None) { + let lease = match self.activate_claim(key, claim_epoch, generations) { Ok(lease) => lease, Err(err) => { self.abandon_claim(key, claim_epoch); @@ -193,6 +195,7 @@ impl PairingRegistry { metadata.clone(), target.clone(), link.quic_generation, + quic_count.clone(), ) { Ok(claim) => claim, Err(error) => { @@ -272,7 +275,7 @@ impl PairingRegistry { .collect(); drop(links); drop(guard); - let lease = match self.activate_claim(key, epoch, generations, None) { + let lease = match self.activate_claim(key, epoch, generations) { Ok(lease) => lease, Err(error) => { self.abandon_claim(key, epoch); diff --git a/src/portal/pairing/udp.rs b/src/portal/pairing/udp.rs index c45945b..7993a69 100644 --- a/src/portal/pairing/udp.rs +++ b/src/portal/pairing/udp.rs @@ -25,6 +25,7 @@ impl PairingRegistry { mut half: UdpHalf, ) -> Result, PairingError> { let session_id = session_id.into(); + let quic_count = self.quic_flow_counter(session_id); if let Err(err) = self.validate_header_and_link(session_id, header, FlowKind::Udp, target.as_ref(), &link) { @@ -70,28 +71,13 @@ impl PairingRegistry { reject_udp_half(&mut half, code).await; return Err(err); } - let udp_permit = if matches!(header.role, FlowRole::Open | FlowRole::Duplex) { - match self.acquire_udp_permit(session_id) { - Ok(permit) => Some(permit), - Err(err) => { - if header.role == FlowRole::Open { - self.reject_flow_setup(session_id, header.flow_id, err.code()) - .await; - } - reject_udp_half(&mut half, err.code()).await; - return Err(err); - } - } - } else { - None - }; - if header.role == FlowRole::Duplex { let (claim_epoch, created) = match self.reserve_claim( key, metadata.clone(), target.clone(), link.quic_generation, + quic_count.clone(), ) { Ok(claim) => claim, Err(err) => { @@ -115,7 +101,7 @@ impl PairingRegistry { unreachable!("duplex UDP shape validated") }; let generations = link.quic_generation.into_iter().collect(); - let lease = match self.activate_claim(key, claim_epoch, generations, udp_permit) { + let lease = match self.activate_claim(key, claim_epoch, generations) { Ok(lease) => lease, Err(err) => { self.abandon_claim(key, claim_epoch); @@ -138,7 +124,6 @@ impl PairingRegistry { } let mut half = Some(half); - let mut udp_permit = udp_permit; let outcome = 'install: { let mut guard = self.udp.lock().await; let links = self.links.lock().expect("link registry poisoned"); @@ -160,7 +145,6 @@ impl PairingRegistry { { pending.uplink = None; pending.target = None; - pending.flow_permit = None; pending.uplink_path = None; pending.uplink_generation = None; } @@ -211,6 +195,7 @@ impl PairingRegistry { metadata.clone(), target.clone(), link.quic_generation, + quic_count.clone(), ) { Ok(claim) => claim, Err(error) => { @@ -228,7 +213,6 @@ impl PairingRegistry { target: target.clone(), uplink: None, downlink: None, - flow_permit: None, uplink_path: None, downlink_path: None, uplink_generation: None, @@ -260,7 +244,6 @@ impl PairingRegistry { }; } pending.uplink = Some(uplink); - pending.flow_permit = udp_permit.take(); pending.uplink_path = Some(link.path); pending.uplink_generation = link.quic_generation; } @@ -284,24 +267,13 @@ impl PairingRegistry { if pending.uplink.is_some() && pending.downlink.is_some() { let mut complete = guard.remove(&key).expect("UDP pair exists"); let epoch = complete.epoch; - let Some(permit) = complete.flow_permit.take() else { - self.abandon_claim(key, epoch); - break 'install UdpInstallOutcome::Rejected { - error: PairingError::new( - FlowErrorCode::InternalError, - "portal::pairing: missing UDP flow permit", - ), - downlink: complete.downlink.take(), - abort_pending: false, - }; - }; let generations = [complete.uplink_generation, complete.downlink_generation] .into_iter() .flatten() .collect(); drop(links); drop(guard); - let lease = match self.activate_claim(key, epoch, generations, Some(permit)) { + let lease = match self.activate_claim(key, epoch, generations) { Ok(lease) => lease, Err(error) => { self.abandon_claim(key, epoch); diff --git a/src/portal/setup.rs b/src/portal/setup.rs index 87c0cc9..557dc69 100644 --- a/src/portal/setup.rs +++ b/src/portal/setup.rs @@ -136,7 +136,6 @@ impl Portal { let write_bps = rate_limit_bytes_per_second(etar_limit) as i64; let rate_limiter = RateLimiter::new(read_bps, write_bps).map(Arc::new); let udp_flow_limits = UdpFlowLimits { - max_flows: runtime.max_udp_flows, queue_bytes: runtime.udp_queue_bytes, }; let socks_endpoint = socks @@ -201,9 +200,6 @@ impl Portal { quic_server_config, unauthenticated_admission: Arc::new(admission::UnauthenticatedAdmission::new()), pairing: Arc::new(super::pairing::PairingRegistry::new( - runtime.max_tcp_flows as usize, - udp_flow_limits.max_flows, - runtime.max_pending_pairs, runtime.flow_pair_timeout, )), ready_gate: super::tasks::ReadyGate::default(), diff --git a/src/tests/main.rs b/src/tests/main.rs index a0d7cb6..aec9baf 100644 --- a/src/tests/main.rs +++ b/src/tests/main.rs @@ -33,11 +33,8 @@ fn help_text_documents_usage_and_configuration_surface() { "rate=", "etar=", "UDP-over-TCP (UoT)", - "NOW_MAX_TCP_FLOWS", - "NOW_MAX_UDP_FLOWS", "NOW_QUIC_UDP_QUEUE_BYTES", "NOW_TRANSPORT_MEMORY_PROFILE", - "NOW_MAX_PENDING_PAIRS", "NOW_FLOW_PAIR_TIMEOUT", "NOW_FLOW_SETUP_TIMEOUT", "NOW_MIX_FALLBACK_TIMEOUT", @@ -53,7 +50,14 @@ fn help_text_documents_usage_and_configuration_surface() { "missing help text: {expected}" ); } - for removed in ["alpn=", "pool=", "NOW_QUIC_MAX_UDP_FLOWS"] { + for removed in [ + "NOW_MAX_TCP_FLOWS", + "NOW_MAX_UDP_FLOWS", + "NOW_MAX_PENDING_PAIRS", + "alpn=", + "pool=", + "NOW_QUIC_MAX_UDP_FLOWS", + ] { assert!( !HELP_TEXT.contains(removed), "removed help option: {removed}" diff --git a/src/tests/portal/config.rs b/src/tests/portal/config.rs index b79c3a4..f8d6ed5 100644 --- a/src/tests/portal/config.rs +++ b/src/tests/portal/config.rs @@ -18,12 +18,6 @@ fn parse(values: &[(&str, &str)]) -> anyhow::Result { #[test] fn absent_values_use_the_existing_defaults() { let config = parse(&[]).unwrap(); - assert_eq!(config.max_tcp_flows, DEFAULT_MAX_TCP_FLOWS); - assert_eq!(config.max_udp_flows, DEFAULT_MAX_UDP_FLOWS); - assert_eq!( - config.quic_bidi_stream_capacity(), - DEFAULT_MAX_TCP_FLOWS + DEFAULT_MAX_UDP_FLOWS as u32 - ); assert_eq!(config.udp_queue_bytes, DEFAULT_QUIC_UDP_QUEUE_BYTES); assert_eq!(config.tcp_data_buf_size, DEFAULT_TCP_DATA_BUF_SIZE); assert_eq!(config.udp_data_buf_size, DEFAULT_UDP_DATA_BUF_SIZE); @@ -39,19 +33,15 @@ fn absent_values_use_the_existing_defaults() { ); assert_eq!(config.shutdown_timeout, DEFAULT_SHUTDOWN_TIMEOUT); assert_eq!(config.reload_interval, DEFAULT_RELOAD_INTERVAL); - assert_eq!(config.max_pending_pairs, DEFAULT_MAX_PENDING_PAIRS); assert_eq!(config.flow_pair_timeout, DEFAULT_FLOW_PAIR_TIMEOUT); } #[test] fn all_integer_limits_reject_zero_instead_of_falling_back() { for name in [ - "NOW_MAX_TCP_FLOWS", - "NOW_MAX_UDP_FLOWS", "NOW_QUIC_UDP_QUEUE_BYTES", "NOW_TCP_DATA_BUF_SIZE", "NOW_UDP_DATA_BUF_SIZE", - "NOW_MAX_PENDING_PAIRS", ] { let error = parse(&[(name, "0")]).unwrap_err().to_string(); assert!(error.contains(name), "unexpected error for {name}: {error}"); @@ -60,8 +50,10 @@ fn all_integer_limits_reject_zero_instead_of_falling_back() { #[test] fn removed_quic_specific_udp_limit_is_not_an_input() { - let config = parse(&[("NOW_QUIC_MAX_UDP_FLOWS", "13")]).unwrap(); - assert_eq!(config.max_udp_flows, DEFAULT_MAX_UDP_FLOWS); + assert_eq!( + parse(&[("NOW_QUIC_MAX_UDP_FLOWS", "13")]).unwrap(), + parse(&[]).unwrap() + ); } #[test] @@ -88,8 +80,6 @@ fn all_durations_reject_zero_and_invalid_syntax() { #[test] fn values_are_parsed_once_into_typed_fields() { let config = parse(&[ - ("NOW_MAX_TCP_FLOWS", "77"), - ("NOW_MAX_UDP_FLOWS", "13"), ("NOW_QUIC_UDP_QUEUE_BYTES", "8192"), ("NOW_TCP_DATA_BUF_SIZE", "4096"), ("NOW_UDP_DATA_BUF_SIZE", "8192"), @@ -102,14 +92,10 @@ fn values_are_parsed_once_into_typed_fields() { ("NOW_TELEMETRY_INTERVAL", "1700ms"), ("NOW_SHUTDOWN_TIMEOUT", "1800ms"), ("NOW_RELOAD_INTERVAL", "1900ms"), - ("NOW_MAX_PENDING_PAIRS", "19"), ("NOW_FLOW_PAIR_TIMEOUT", "2s"), ]) .unwrap(); - assert_eq!(config.max_tcp_flows, 77); - assert_eq!(config.max_udp_flows, 13); - assert_eq!(config.quic_bidi_stream_capacity(), 90); assert_eq!(config.udp_queue_bytes, 8192); assert_eq!(config.tcp_data_buf_size, 4096); assert_eq!(config.udp_data_buf_size, 8192); @@ -122,7 +108,6 @@ fn values_are_parsed_once_into_typed_fields() { assert_eq!(config.telemetry_interval, Duration::from_millis(1700)); assert_eq!(config.shutdown_timeout, Duration::from_millis(1800)); assert_eq!(config.reload_interval, Duration::from_millis(1900)); - assert_eq!(config.max_pending_pairs, 19); assert_eq!(config.flow_pair_timeout, Duration::from_secs(2)); } @@ -146,14 +131,19 @@ fn telemetry_interval_enforces_dashboard_bounds() { #[test] fn overflow_is_a_startup_error() { - assert!(parse(&[("NOW_MAX_TCP_FLOWS", "4294967296")]).is_err()); - assert!( - parse(&[ - ("NOW_MAX_TCP_FLOWS", "4294967295"), - ("NOW_MAX_UDP_FLOWS", "1"), - ]) - .is_err() - ); assert!(parse(&[("NOW_TCP_DATA_BUF_SIZE", "999999999999999999999999")]).is_err()); assert!(parse(&[("NOW_HANDSHAKE_TIMEOUT", "999999999999999999999999h")]).is_err()); } + +#[test] +fn removed_application_count_limits_are_ignored() { + for name in [ + "NOW_MAX_TCP_FLOWS", + "NOW_MAX_UDP_FLOWS", + "NOW_MAX_PENDING_PAIRS", + ] { + for value in ["0", "1", "invalid"] { + assert_eq!(parse(&[(name, value)]).unwrap(), parse(&[]).unwrap()); + } + } +} diff --git a/src/tests/portal/conn/quic.rs b/src/tests/portal/conn/quic.rs index 78ca7cb..2ff9781 100644 --- a/src/tests/portal/conn/quic.rs +++ b/src/tests/portal/conn/quic.rs @@ -10,7 +10,7 @@ use bytes::Bytes; use tokio::net::UdpSocket; use tokio::time::timeout; -use crate::portal::{Portal, UdpFlowLimits}; +use crate::portal::Portal; use crate::protocol::{ Carrier, FlowErrorCode, FlowHeader, FlowKind, FlowResult, FlowRole, UdpFrame, decode_udp_frame, encode_udp_close, encode_udp_data_fragments, read_flow_result, write_flow_header, @@ -18,8 +18,7 @@ use crate::protocol::{ }; use super::support::{ - connect_test_quic, connect_test_quic_to, connect_test_quic_with_url_and_limits, - quic_auth_frame, stop_test_quic, test_target, + connect_test_quic, connect_test_quic_to, quic_auth_frame, stop_test_quic, test_target, }; async fn authenticate_test_connection(portal: &Portal, connection: &quinn::Connection) { @@ -322,59 +321,30 @@ async fn zero_length_udp_packet_round_trips_as_data() { } #[tokio::test] -async fn udp_close_releases_the_session_global_flow_permit() { - let limits = UdpFlowLimits { - max_flows: 1, - queue_bytes: 64 * 1024, - }; +async fn quic_udp_grows_past_former_session_and_stream_limits() { let (portal, server_endpoint, client_endpoint, connection, shutdown, server_task) = - connect_test_quic_with_url_and_limits( - "portal://secret@127.0.0.1:0?log=none&net=udp", - Some(limits), - ) - .await; + connect_test_quic().await; authenticate_test_connection(&portal, &connection).await; - let first_target = UdpSocket::bind("127.0.0.1:0").await.unwrap(); - let second_target = UdpSocket::bind("127.0.0.1:0").await.unwrap(); - - assert_eq!( - setup_quic_udp( - &portal, - &connection, - 90, - &first_target.local_addr().unwrap().to_string(), - ) - .await - .0, - FlowResult::Ready - ); - let (result, mut rejected) = setup_quic_udp( - &portal, - &connection, - 91, - &second_target.local_addr().unwrap().to_string(), - ) - .await; - assert_eq!(result, FlowResult::Reject(FlowErrorCode::FlowLimit)); - let mut eof = [0u8; 1]; - assert_eq!(rejected.read(&mut eof).await.unwrap(), None); - + let target = UdpSocket::bind("127.0.0.1:0").await.unwrap(); + let address = target.local_addr().unwrap().to_string(); + let mut streams = Vec::new(); + timeout(Duration::from_secs(20), async { + for id in 1..=1400 { + let (result, stream) = setup_quic_udp(&portal, &connection, id, &address).await; + assert_eq!(result, FlowResult::Ready, "flow {id}"); + streams.push(stream); + } + }) + .await + .expect("MAX_STREAMS must grow without a fixed active-flow cap"); + wait_for_udp_active(&portal, 1400).await; connection - .send_datagram(Bytes::copy_from_slice(&encode_udp_close(90).unwrap())) + .send_datagram(Bytes::copy_from_slice(&encode_udp_close(1).unwrap())) .unwrap(); - wait_for_udp_active(&portal, 0).await; - assert_eq!( - setup_quic_udp( - &portal, - &connection, - 91, - &second_target.local_addr().unwrap().to_string(), - ) - .await - .0, - FlowResult::Ready - ); - + wait_for_udp_active(&portal, 1399).await; + let (result, stream) = setup_quic_udp(&portal, &connection, 1401, &address).await; + assert_eq!(result, FlowResult::Ready); + streams.push(stream); connection.close(quinn::VarInt::from_u32(0), b""); stop_test_quic(server_endpoint, client_endpoint, shutdown, server_task).await; } diff --git a/src/tests/portal/conn/support.rs b/src/tests/portal/conn/support.rs index 924cb86..1f63b12 100644 --- a/src/tests/portal/conn/support.rs +++ b/src/tests/portal/conn/support.rs @@ -186,9 +186,6 @@ pub(super) async fn connect_test_quic_with_url_and_limits( let inner = Arc::get_mut(&mut portal.inner).unwrap(); inner.udp_flow_limits = limits; inner.pairing = Arc::new(crate::portal::pairing::PairingRegistry::new( - inner.runtime.max_tcp_flows as usize, - limits.max_flows, - inner.runtime.max_pending_pairs, inner.runtime.flow_pair_timeout, )); } diff --git a/src/tests/portal/pairing.rs b/src/tests/portal/pairing.rs index 893b2bf..339eed0 100644 --- a/src/tests/portal/pairing.rs +++ b/src/tests/portal/pairing.rs @@ -5,19 +5,17 @@ use super::*; use crate::protocol::{ - Carrier, FlowKind, FlowResult, FlowRole, SESSION_ID_LEN, SessionId, Target, encode_flow_result, + Carrier, FlowKind, FlowResult, FlowRole, SESSION_ID_LEN, Target, encode_flow_result, read_flow_result, }; use crate::transport::Stats; -use std::collections::HashMap; use std::pin::Pin; use std::sync::Arc; -use std::sync::Mutex as StdMutex; -use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::task::{Context, Poll}; use std::time::Duration; use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt}; -use tokio::sync::{Mutex, mpsc}; +use tokio::sync::mpsc; impl PairingRegistry { fn is_accepting(&self) -> bool { @@ -25,21 +23,8 @@ impl PairingRegistry { } } -fn registry(max_udp_flows: usize, timeout: Duration) -> Arc { - Arc::new(PairingRegistry { - tcp: Mutex::new(HashMap::new()), - udp: Mutex::new(HashMap::new()), - links: StdMutex::new(HashMap::new()), - claims: StdMutex::new(HashMap::new()), - rejections: StdMutex::new(HashMap::new()), - accepting: AtomicBool::new(true), - next_quic_generation: AtomicU64::new(1), - next_epoch: AtomicU64::new(1), - max_pending: 16, - timeout, - max_tcp_flows: 16, - max_udp_flows, - }) +fn registry(timeout: Duration) -> Arc { + Arc::new(PairingRegistry::new(timeout)) } fn header( @@ -78,17 +63,6 @@ fn quic_half(label: &str, generation: u64) -> LinkHalf { LinkHalf::quic(path(label), generation) } -fn available_udp_permits(registry: &PairingRegistry, session_id: SessionId) -> usize { - registry - .links - .lock() - .expect("link registry poisoned") - .get(&session_id) - .expect("registered session") - .udp_flow_budget - .available_permits() -} - struct PendingWriter; trait PairingResultExt { diff --git a/src/tests/portal/pairing/lifecycle.rs b/src/tests/portal/pairing/lifecycle.rs index 243629b..60726db 100644 --- a/src/tests/portal/pairing/lifecycle.rs +++ b/src/tests/portal/pairing/lifecycle.rs @@ -4,92 +4,44 @@ use super::*; #[tokio::test] -async fn tcp_flow_limit_is_scoped_to_each_authenticated_session() { - let mut registry = registry(8, Duration::from_secs(30)); - Arc::get_mut(&mut registry).unwrap().max_tcp_flows = 1; - let stats = Arc::new(Stats::default()); - let first_session = [0x31; SESSION_ID_LEN]; - let second_session = [0x32; SESSION_ID_LEN]; - let _first_guard = registry.register_tcp_link(first_session, stats.clone()); - let _second_guard = registry.register_tcp_link(second_session, stats); - - let (first_io, _) = tokio::io::duplex(64); - let (first_down, _) = tokio::io::duplex(64); - let first = registry - .submit_tcp( - first_session, - header( - FlowRole::Duplex, - 1, - FlowKind::Tcp, - Carrier::TlsTcp, - Carrier::TlsTcp, - ), - Some(target("target.test:443")), - tcp_half("first"), - Some(Box::pin(first_io)), - Some(Box::pin(first_down)), - None, - ) - .await - .unwrap() - .unwrap(); - - let (excess_io, _) = tokio::io::duplex(64); - let (excess_down, mut excess_peer) = tokio::io::duplex(64); - let error = registry - .submit_tcp( - first_session, - header( - FlowRole::Duplex, - 2, - FlowKind::Tcp, - Carrier::TlsTcp, - Carrier::TlsTcp, - ), - Some(target("target.test:443")), - tcp_half("excess"), - Some(Box::pin(excess_io)), - Some(Box::pin(excess_down)), - None, - ) - .await - .unwrap_pairing_error(); - assert_eq!(error.code(), FlowErrorCode::FlowLimit); - assert_eq!( - read_flow_result(&mut excess_peer).await.unwrap(), - FlowResult::Reject(FlowErrorCode::FlowLimit) - ); - - let (other_io, _) = tokio::io::duplex(64); - let (other_down, _) = tokio::io::duplex(64); - let other = registry - .submit_tcp( - second_session, - header( - FlowRole::Duplex, - 1, - FlowKind::Tcp, - Carrier::TlsTcp, - Carrier::TlsTcp, - ), - Some(target("target.test:443")), - tcp_half("other"), - Some(Box::pin(other_io)), - Some(Box::pin(other_down)), - None, - ) - .await - .unwrap(); - assert!(other.is_some()); - - drop(first); - drop(other); +async fn tcp_sessions_admit_more_than_the_former_flow_limit() { + let registry = registry(Duration::from_secs(30)); + let session = [0x31; SESSION_ID_LEN]; + let _guard = registry.register_tcp_link(session, Arc::new(Stats::default())); + let mut held = Vec::new(); + for id in 1..=2048 { + let (up, _) = tokio::io::duplex(64); + let (down, _) = tokio::io::duplex(64); + held.push( + registry + .submit_tcp( + session, + header( + FlowRole::Duplex, + id, + FlowKind::Tcp, + Carrier::TlsTcp, + Carrier::TlsTcp, + ), + Some(target("target.test:443")), + tcp_half("live"), + Some(Box::pin(up)), + Some(Box::pin(down)), + None, + ) + .await + .unwrap() + .unwrap(), + ); + } + assert_eq!(registry.claims.lock().unwrap().len(), 2048); + drop(held); + assert!(registry.claims.lock().unwrap().is_empty()); } #[tokio::test] async fn drain_rejects_pending_and_new_flows_but_preserves_active_claims() { - let registry = registry(8, Duration::from_secs(30)); + let registry = registry(Duration::from_secs(30)); let stats = Arc::new(Stats::default()); let session_id = [0x5a; SESSION_ID_LEN]; let _tcp_guard = registry.register_tcp_link(session_id, stats); @@ -201,7 +153,7 @@ async fn drain_rejects_pending_and_new_flows_but_preserves_active_claims() { #[tokio::test] async fn cancel_all_cancels_active_flows_without_waiting_for_pending_writer() { - let registry = registry(8, Duration::from_secs(60)); + let registry = registry(Duration::from_secs(60)); let stats = Arc::new(Stats::default()); let session_id = [5; SESSION_ID_LEN]; let tcp_guard = registry.register_tcp_link(session_id, stats.clone()); @@ -285,3 +237,41 @@ async fn cancel_all_cancels_active_flows_without_waiting_for_pending_writer() { drop(quic_guard); drop(tcp_guard); } + +#[tokio::test] +async fn pending_pairs_exceed_former_limit_and_release_quic_credit_on_drain() { + let registry = registry(Duration::from_secs(30)); + let session = [0x32; SESSION_ID_LEN]; + let _guard = registry.register_tcp_link(session, Arc::new(Stats::default())); + let mut peers = Vec::new(); + for id in 1..=2048 { + let (up, peer) = tokio::io::duplex(64); + peers.push(peer); + assert!( + registry + .submit_tcp( + session, + header( + FlowRole::Open, + id, + FlowKind::Tcp, + Carrier::TlsTcp, + Carrier::Quic + ), + Some(target("target.test:443")), + tcp_half("pending"), + Some(Box::pin(up)), + None, + None, + ) + .await + .unwrap() + .is_none() + ); + } + assert_eq!(registry.claims.lock().unwrap().len(), 2048); + assert_eq!(registry.quic_stream_credit(session).into_inner(), 2560); + registry.begin_drain().await; + assert!(registry.claims.lock().unwrap().is_empty()); + assert_eq!(registry.quic_stream_credit(session).into_inner(), 64); +} diff --git a/src/tests/portal/pairing/rejection.rs b/src/tests/portal/pairing/rejection.rs index 13c6506..62a3b00 100644 --- a/src/tests/portal/pairing/rejection.rs +++ b/src/tests/portal/pairing/rejection.rs @@ -5,7 +5,7 @@ use super::*; #[tokio::test] async fn initially_stale_udp_open_leaves_exact_rejection_for_uot_attach() { - let registry = registry(8, Duration::from_secs(30)); + let registry = registry(Duration::from_secs(30)); let stats = Arc::new(Stats::default()); let session_id = [7; SESSION_ID_LEN]; let tcp_guard = registry.register_tcp_link(session_id, stats.clone()); @@ -83,7 +83,7 @@ async fn initially_stale_udp_open_leaves_exact_rejection_for_uot_attach() { #[tokio::test] async fn late_attach_receives_original_open_pair_timeout() { - let registry = registry(8, Duration::from_millis(10)); + let registry = registry(Duration::from_millis(10)); let stats = Arc::new(Stats::default()); let tcp_session = [8; SESSION_ID_LEN]; @@ -213,7 +213,7 @@ async fn late_attach_receives_original_open_pair_timeout() { #[tokio::test] async fn tombstones_deliver_exact_reject_on_selected_downlink() { - let registry = registry(8, Duration::from_secs(30)); + let registry = registry(Duration::from_secs(30)); let stats = Arc::new(Stats::default()); let tcp_session = [3; SESSION_ID_LEN]; diff --git a/src/tests/portal/pairing/replacement.rs b/src/tests/portal/pairing/replacement.rs index 186bb08..c9d23d1 100644 --- a/src/tests/portal/pairing/replacement.rs +++ b/src/tests/portal/pairing/replacement.rs @@ -5,7 +5,7 @@ use super::*; #[tokio::test] async fn old_quic_guard_does_not_cancel_flow_on_replacement_generation() { - let registry = registry(8, Duration::from_secs(30)); + let registry = registry(Duration::from_secs(30)); let stats = Arc::new(Stats::default()); let session_id = [1; SESSION_ID_LEN]; let _tcp_guard = registry.register_tcp_link(session_id, stats.clone()); @@ -81,7 +81,7 @@ async fn old_quic_guard_does_not_cancel_flow_on_replacement_generation() { #[tokio::test] async fn quic_replacement_immediately_rejects_pending_split_flows() { - let registry = registry(8, Duration::from_secs(30)); + let registry = registry(Duration::from_secs(30)); let stats = Arc::new(Stats::default()); let session_id = [0x33; SESSION_ID_LEN]; let _tcp_guard = registry.register_tcp_link(session_id, stats.clone()); @@ -185,7 +185,7 @@ async fn quic_replacement_immediately_rejects_pending_split_flows() { #[tokio::test] async fn stale_open_after_map_lock_leaves_exact_rejection_for_tcp_attach() { - let registry = registry(8, Duration::from_secs(30)); + let registry = registry(Duration::from_secs(30)); let stats = Arc::new(Stats::default()); let session_id = [2; SESSION_ID_LEN]; let tcp_guard = registry.register_tcp_link(session_id, stats.clone()); diff --git a/src/tests/portal/pairing/udp.rs b/src/tests/portal/pairing/udp.rs index 28bba6f..4f957ac 100644 --- a/src/tests/portal/pairing/udp.rs +++ b/src/tests/portal/pairing/udp.rs @@ -4,109 +4,42 @@ use super::*; #[tokio::test] -async fn udp_permit_is_shared_by_quic_and_uot_and_released_by_cancel() { - let registry = registry(1, Duration::from_secs(60)); - let stats = Arc::new(Stats::default()); - let session_id = [6; SESSION_ID_LEN]; - let tcp_guard = registry.register_tcp_link(session_id, stats.clone()); - let quic_guard = registry - .register_quic_link( - session_id, - stats, - tokio_util::sync::CancellationToken::new(), - ) - .await; - - let (_datagram_tx, datagram_rx) = mpsc::channel(1); - assert!( - registry - .submit_udp( - session_id, - header( - FlowRole::Open, - 13, - FlowKind::Udp, - Carrier::Quic, - Carrier::TlsTcp, - ), - Some(target("target.test:53")), - quic_half("udp-up", quic_guard.quic_generation()), - UdpHalf::Uplink { - uplink: UdpUp::Quic(QuicUdpReceiver::new_without_barrier( - datagram_rx, - Arc::new(AtomicBool::new(false)), - || {}, - )), - }, - ) - .await - .unwrap() - .is_none() - ); - assert_eq!(available_udp_permits(®istry, session_id), 0); - - let (rejected_uplink, _rejected_uplink_peer) = tokio::io::duplex(64); - let (rejected_downlink, mut rejected_peer) = tokio::io::duplex(64); - let rejected = registry - .submit_udp( - session_id, - header( - FlowRole::Duplex, - 14, - FlowKind::Udp, - Carrier::TlsTcp, - Carrier::TlsTcp, - ), - Some(target("target.test:53")), - tcp_half("uot-limited"), - UdpHalf::Duplex { - uplink: UdpUp::TlsTcp(Box::pin(rejected_uplink)), - downlink: UdpDown::TlsTcp { - writer: Box::pin(rejected_downlink), - liveness: None, - }, - }, - ) - .await - .unwrap_pairing_error(); - assert_eq!(rejected.code(), FlowErrorCode::FlowLimit); - assert_eq!( - read_flow_result(&mut rejected_peer).await.unwrap(), - FlowResult::Reject(FlowErrorCode::FlowLimit) - ); - - registry.cancel_udp(session_id, 13).await; - assert_eq!(available_udp_permits(®istry, session_id), 1); - - let (uot_uplink, _uot_uplink_peer) = tokio::io::duplex(64); - let (uot_downlink, _uot_downlink_peer) = tokio::io::duplex(64); - let paired = registry - .submit_udp( - session_id, - header( - FlowRole::Duplex, - 14, - FlowKind::Udp, - Carrier::TlsTcp, - Carrier::TlsTcp, - ), - Some(target("target.test:53")), - tcp_half("uot-accepted"), - UdpHalf::Duplex { - uplink: UdpUp::TlsTcp(Box::pin(uot_uplink)), - downlink: UdpDown::TlsTcp { - writer: Box::pin(uot_downlink), - liveness: None, - }, - }, - ) - .await - .unwrap() - .expect("released permit should admit UoT flow"); - assert_eq!(available_udp_permits(®istry, session_id), 0); - drop(paired); - assert_eq!(available_udp_permits(®istry, session_id), 1); - - drop(quic_guard); - drop(tcp_guard); +async fn uot_flows_exceed_the_former_udp_limit_without_reserving_quic_credit() { + let registry = registry(Duration::from_secs(60)); + let session = [6; SESSION_ID_LEN]; + let _guard = registry.register_tcp_link(session, Arc::new(Stats::default())); + let mut held = Vec::new(); + for id in 1..=512 { + let (up, _) = tokio::io::duplex(64); + let (down, _) = tokio::io::duplex(64); + held.push( + registry + .submit_udp( + session, + header( + FlowRole::Duplex, + id, + FlowKind::Udp, + Carrier::TlsTcp, + Carrier::TlsTcp, + ), + Some(target("target.test:53")), + tcp_half("uot"), + UdpHalf::Duplex { + uplink: UdpUp::TlsTcp(Box::pin(up)), + downlink: UdpDown::TlsTcp { + writer: Box::pin(down), + liveness: None, + }, + }, + ) + .await + .unwrap() + .unwrap(), + ); + } + assert_eq!(registry.claims.lock().unwrap().len(), 512); + assert_eq!(registry.quic_stream_credit(session).into_inner(), 64); + drop(held); + assert!(registry.claims.lock().unwrap().is_empty()); } diff --git a/src/tests/vector/flow_id.rs b/src/tests/vector/flow_id.rs index ac03f4e..77a2a96 100644 --- a/src/tests/vector/flow_id.rs +++ b/src/tests/vector/flow_id.rs @@ -2,11 +2,12 @@ use super::*; #[test] fn allocator_never_reuses_an_active_id() { - let allocator = FlowIdAllocator::new(2); + let allocator = FlowIdAllocator::new(); let first = allocator.allocate().unwrap(); let second = allocator.allocate().unwrap(); assert_ne!(first.id(), second.id()); - assert!(allocator.allocate().is_err()); + let extra: Vec<_> = (0..4096).map(|_| allocator.allocate().unwrap()).collect(); + assert_eq!(extra.len(), 4096); let released = first.id(); drop(first); let third = allocator.allocate().unwrap(); @@ -16,7 +17,7 @@ fn allocator_never_reuses_an_active_id() { #[test] fn allocator_skips_zero_at_wrap() { - let allocator = FlowIdAllocator::new(2); + let allocator = FlowIdAllocator::new(); allocator.next.store(u32::MAX, Ordering::Relaxed); let max = allocator.allocate().unwrap(); let wrapped = allocator.allocate().unwrap(); diff --git a/src/vector/flow.rs b/src/vector/flow.rs index 378afe6..5153547 100644 --- a/src/vector/flow.rs +++ b/src/vector/flow.rs @@ -13,7 +13,6 @@ use std::time::Duration; use anyhow::{Context, Result, anyhow}; use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; use tokio::net::TcpStream; -use tokio::sync::OwnedSemaphorePermit; use tokio::time::timeout; use crate::common::socks::{ diff --git a/src/vector/flow/tcp.rs b/src/vector/flow/tcp.rs index 01b5579..99f6413 100644 --- a/src/vector/flow/tcp.rs +++ b/src/vector/flow/tcp.rs @@ -13,13 +13,11 @@ pub(crate) struct TcpTunnel { _lease: Option, uplink: Carrier, downlink: Carrier, - _flow_permit: Option, } pub(crate) struct TcpTunnelGuard { _lanes: Vec, _lease: Option, - _flow_permit: Option, } impl TcpTunnel { @@ -39,17 +37,8 @@ impl TcpTunnel { _lease, uplink: _, downlink: _, - _flow_permit, } = self; - ( - reader, - writer, - TcpTunnelGuard { - _lanes, - _lease, - _flow_permit, - }, - ) + (reader, writer, TcpTunnelGuard { _lanes, _lease }) } } @@ -89,11 +78,6 @@ pub(crate) async fn open_tcp( target: &Target, hops: u8, ) -> std::result::Result { - let flow_permit = client - .tcp_flow_permits - .clone() - .try_acquire_owned() - .map_err(|_| OpenFlowError::Setup(SetupResult::FlowLimit))?; let lease = client .flow_ids .allocate() @@ -146,7 +130,6 @@ pub(crate) async fn open_tcp( _lease: Some(lease), uplink, downlink, - _flow_permit: Some(flow_permit), }); } @@ -196,7 +179,6 @@ pub(crate) async fn open_tcp( _lease: Some(lease), uplink, downlink, - _flow_permit: Some(flow_permit), }) } diff --git a/src/vector/flow_id.rs b/src/vector/flow_id.rs index 9bec8fb..8c76247 100644 --- a/src/vector/flow_id.rs +++ b/src/vector/flow_id.rs @@ -15,24 +15,22 @@ use crate::protocol::FlowId; pub(super) struct FlowIdAllocator { next: AtomicU32, active: Mutex>, - limit: usize, } impl FlowIdAllocator { - pub(super) fn new(limit: usize) -> Arc { + pub(super) fn new() -> Arc { Arc::new(Self { next: AtomicU32::new(1), - active: Mutex::new(HashSet::with_capacity(limit.min(4_096))), - limit, + active: Mutex::new(HashSet::new()), }) } pub(super) fn allocate(self: &Arc) -> Result { let mut active = self.active.lock().unwrap_or_else(|lock| lock.into_inner()); - if active.len() >= self.limit { - bail!("vector::flow_id: active flow limit reached"); + if active.len() == u32::MAX as usize { + bail!("vector::flow_id: flow identifier space exhausted"); } - for _ in 0..=self.limit { + for _ in 0..=active.len() { let id = self.next.fetch_add(1, Ordering::Relaxed); let id = if id == 0 { self.next.fetch_add(1, Ordering::Relaxed) diff --git a/src/vector/mod.rs b/src/vector/mod.rs index 7713d1f..b474b4e 100644 --- a/src/vector/mod.rs +++ b/src/vector/mod.rs @@ -29,8 +29,8 @@ use self::session::{ClientSignals, QuicManager, TlsManager}; use self::tls::ClientTls; use crate::common::{ LatencyTracker, LifeMode, LifeReason, LifeState, Lifecycle, Logger, ShutdownSignals, - max_tcp_flows, max_udp_flows, rate_limit_bytes_per_second, shutdown_timeout, tcp_data_buf_size, - telemetry_interval, udp_data_buf_size, + rate_limit_bytes_per_second, shutdown_timeout, tcp_data_buf_size, telemetry_interval, + udp_data_buf_size, }; use crate::protocol::{Credentials, SESSION_ID_LEN}; use crate::telemetry::TelemetryServer; @@ -53,7 +53,6 @@ pub(super) struct VectorInner { rate_limiter: Option>, client: Arc, local_udp_budget: Arc, - socks_admission: Arc, shutdown: CancellationToken, } @@ -66,8 +65,6 @@ pub(crate) struct PortalClient { account_stats: bool, latency: Arc, flow_ids: Arc, - tcp_flow_permits: Arc, - udp_flow_permits: Arc, tls_manager: Arc, quic: Arc, route_seed: u64, @@ -129,17 +126,13 @@ impl PortalClient { signals, shutdown.clone(), ); - let tcp_limit = max_tcp_flows().max(1) as usize; - let udp_limit = max_udp_flows(); Ok(Arc::new(Self { config, telemetry, stats, account_stats, latency, - flow_ids: FlowIdAllocator::new(tcp_limit.saturating_add(udp_limit)), - tcp_flow_permits: Arc::new(Semaphore::new(tcp_limit)), - udp_flow_permits: Arc::new(Semaphore::new(udp_limit)), + flow_ids: FlowIdAllocator::new(), tls_manager, quic, route_seed, @@ -223,8 +216,6 @@ impl Vector { ); let stats = Arc::new(Stats::default()); let shutdown = CancellationToken::new(); - let tcp_limit = max_tcp_flows().max(1) as usize; - let udp_limit = max_udp_flows(); let read_bps = rate_limit_bytes_per_second(config.rate_mbps) as i64; let write_bps = rate_limit_bytes_per_second(config.etar_mbps) as i64; let rate_limiter = RateLimiter::new(read_bps, write_bps).map(Arc::new); @@ -250,7 +241,6 @@ impl Vector { rate_limiter, client, local_udp_budget: Arc::new(Semaphore::new(udp_queue_bytes)), - socks_admission: Arc::new(Semaphore::new(tcp_limit.saturating_add(udp_limit))), shutdown, }), }) diff --git a/src/vector/socks/server.rs b/src/vector/socks/server.rs index cf4ebdc..cb7e5de 100644 --- a/src/vector/socks/server.rs +++ b/src/vector/socks/server.rs @@ -78,22 +78,9 @@ pub(in crate::vector) async fn serve_listener( _ = shutdown.cancelled() => break, accepted = listener.accept() => match accepted { Ok((stream, peer)) => { - let Ok(admission) = vector.socks_admission.clone().try_acquire_owned() else { - vector.telemetry.emit_runtime( - RuntimeEvent::new( - RuntimeLevel::Warn, - RuntimeKind::Listener, - "SOCKS client limit exceeded", - ) - .with_client(peer.to_string()), - ); - drop(stream); - continue; - }; let vector = vector.clone(); let shutdown = shutdown.clone(); clients.spawn(async move { - let _admission = admission; if let Err(error) = handle_client(vector.clone(), stream, peer, shutdown).await { vector.logger.debug(format_args!( "vector::socks::handle_client: {peer}: {error}" diff --git a/src/vector/socks/server/udp.rs b/src/vector/socks/server/udp.rs index ce80f2f..78a705c 100644 --- a/src/vector/socks/server/udp.rs +++ b/src/vector/socks/server/udp.rs @@ -54,9 +54,7 @@ pub(super) async fn run_udp_association( let client_endpoint = Arc::new(StdMutex::new( requested_port.map(|port| SocketAddr::new(control_peer.ip(), port)), )); - let max_flows = crate::common::max_udp_flows(); - let mut flows: HashMap> = - HashMap::with_capacity(max_flows.min(64)); + let mut flows: HashMap> = HashMap::new(); let mut tasks = JoinSet::new(); let mut packet = vec![0u8; SOCKS_UDP_PACKET_MAX]; let mut control_byte = [0u8; 1]; @@ -106,9 +104,6 @@ pub(super) async fn run_udp_association( } flows.remove(&target); } - if flows.len() >= max_flows { - continue; - } let (sender, receiver) = mpsc::channel(64); if sender.try_send(payload).is_err() { continue; diff --git a/src/vector/udp_flow.rs b/src/vector/udp_flow.rs index bf664a4..63caf57 100644 --- a/src/vector/udp_flow.rs +++ b/src/vector/udp_flow.rs @@ -9,7 +9,6 @@ use std::sync::atomic::Ordering; use anyhow::{Context, Result, bail}; use bytes::Bytes; use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use tokio::sync::OwnedSemaphorePermit; use tokio::sync::mpsc; use tokio::time::timeout; @@ -34,7 +33,6 @@ pub(crate) struct UdpTunnel { _lanes: Vec, _lease: Option, _session: Option, - _flow_permit: Option, } impl UdpTunnel { @@ -186,11 +184,6 @@ pub(crate) async fn open_udp( target: &Target, hops: u8, ) -> std::result::Result { - let flow_permit = client - .udp_flow_permits - .clone() - .try_acquire_owned() - .map_err(|_| OpenFlowError::Setup(crate::protocol::SetupResult::FlowLimit))?; let lease = client .flow_ids .allocate() @@ -271,7 +264,6 @@ pub(crate) async fn open_udp( _session: client .account_stats .then(|| SessionGuard::new(client.stats.clone(), true)), - _flow_permit: Some(flow_permit), _lanes: lanes, _lease: Some(lease), }) From 68fc872870acfaa79f8c870de0bbcf660b77ec4c Mon Sep 17 00:00:00 2001 From: yosebyte Date: Tue, 8 Sep 2026 19:53:21 +0800 Subject: [PATCH 31/62] Remove completed Mux benchmark artifacts --- docs/mux-performance.md | 177 ------------------------- tests/bench-mux-local.sh | 61 --------- tests/bench-mux-matrix.py | 106 --------------- tests/bench-mux-netem.sh | 64 --------- tests/mux-bench-local.py | 266 -------------------------------------- tests/mux-bench-worker.py | 173 ------------------------- 6 files changed, 847 deletions(-) delete mode 100644 docs/mux-performance.md delete mode 100755 tests/bench-mux-local.sh delete mode 100755 tests/bench-mux-matrix.py delete mode 100755 tests/bench-mux-netem.sh delete mode 100755 tests/mux-bench-local.py delete mode 100755 tests/mux-bench-worker.py diff --git a/docs/mux-performance.md b/docs/mux-performance.md deleted file mode 100644 index 7fbc69b..0000000 --- a/docs/mux-performance.md +++ /dev/null @@ -1,177 +0,0 @@ -# TLS Mux performance baseline - -The auxiliary macOS benchmark runs Vector, Toxiproxy, Portal, and a TCP target. -Toxiproxy terminates and re-originates TCP, so its latency results are useful -for quick development comparisons but are not packet-level TCP network -emulation. - -```sh -cargo build --release --locked -tests/bench-mux-local.sh 100 1 64 1 -``` - -Hard throughput and RSS comparisons use `tests/bench-mux-netem.sh` on a Linux -host with network namespaces and the `netem` qdisc. The harness delays only the -physical carrier veth and samples RSS every 10 ms. - -```sh -sudo tests/bench-mux-matrix.py \ - --baseline /path/to/baseline/nowhere \ - --current target/release/nowhere \ - --output mux-matrix.json -``` - -The fixed matrix is RTT 0/30/100/200/300 ms by 1/4/16 flows, with three serial -samples per cell and median comparison. One-flow payloads are 64 MiB except -32 MiB at 300 ms; four flows total 64 MiB and sixteen flows total 128 MiB. -Throughput may not regress by more than 3%. Per-process peak RSS may not rise by -more than the larger of 5% or 2 MiB. The runner also records non-gating 0.1% -loss observations at 100 ms/1 flow and 300 ms/16 flows. - -## 2026-09-07 development baseline - -Host: Apple silicon macOS development host, Apple Container Toxiproxy 2.12.0, -`throughput` profile, fixed delay without artificial bandwidth or loss. These -numbers compare implementations on this host; they are not production capacity -claims. - -| Implementation | RTT | Flows | Payload | Throughput | Portal peak RSS | Vector peak RSS | -|---|---:|---:|---:|---:|---:|---:| -| Previous 512 KiB Mux | 100 ms | 1 | 64 MiB | 37.86 Mbps | 9.77 MiB | 8.95 MiB | -| Profile-window Mux | 100 ms | 1 | 64 MiB | 959.71 Mbps | 17.56 MiB | 10.52 MiB | -| Previous 512 KiB Mux | 300 ms | 1 | 32 MiB | 13.08 Mbps | 9.86 MiB | 9.16 MiB | -| Profile-window Mux | 300 ms | 1 | 32 MiB | 288.04 Mbps | 19.38 MiB | 10.27 MiB | -| One Mux Shard | 100 ms | 16 | 128 MiB | 1.81 Gbps | 26.83 MiB | 10.38 MiB | -| Four Mux Shards | 100 ms | 16 | 128 MiB | 4.38 Gbps | 71.66 MiB | 11.86 MiB | -| One Mux Shard | 300 ms | 16 | 128 MiB | 670.71 Mbps | 26.72 MiB | 10.19 MiB | -| Four Mux Shards | 300 ms | 16 | 128 MiB | 1.69 Gbps | 61.61 MiB | 11.88 MiB | - -In these historical proxy samples, the larger profile removes the dominant high-RTT collapse. Four Shards improve -aggregate 16-flow throughput by about 2.5 times at both measured RTTs, while -raising peak RSS substantially. That implementation adapted target density to -TLS setup latency and live carrier pressure, and caps the directional pool at -four. A single flow remains on one Shard. - -At 300 ms, a 16 MiB stream window has a theoretical ceiling near 447 Mbps. The -measured 288.04 Mbps is about 64% of that ceiling, leaving room to improve credit -return timing, writer batching, allocation reuse, and TLS scheduling before -increasing the profile budgets. - -## 2026-09-08 adaptive pass - -These historical results used the former 8-byte STREAM/WINDOW development -format and the 1/8-window credit-return threshold. A 1/16 threshold did not -improve throughput and would -send more control frames. Generic TCP relay reads now transfer a pooled buffer -lease into `Bytes`, so the allocation returns to the existing transport pool -when the last payload owner drops. Mux-to-relay reads continue to hand off their -owned frame payload directly. - -| RTT | Flows | Payload | TLS carriers | Throughput | Portal peak RSS | Vector peak RSS | -|---:|---:|---:|---:|---:|---:|---:| -| 10 ms | 16 | 48 MiB | 1 | 6.77 Gbps | 26.77 MiB | 10.84 MiB | -| 100 ms | 1 | 64 MiB | 1 | 937.75 Mbps | 20.08 MiB | 10.47 MiB | -| 300 ms | 1 | 32 MiB | 1 | 283.82 Mbps | 20.36 MiB | 12.56 MiB | -| 100 ms | 16 | 128 MiB | 4 | 4.74 Gbps | 57.48 MiB | 12.78 MiB | -| 300 ms | 16 | 128 MiB | 4 | 1.66 Gbps | 75.64 MiB | 26.30 MiB | - -That historical adaptive policy kept 16 simultaneous low-RTT flows on one carrier, while -100 and 300 ms samples expand to four. The high-RTT aggregate results match or -slightly exceed the fixed-four baseline within normal local-run variation, so -these observations motivated the former four-carrier cap. They do not validate -the current eight-carrier pool or prove an optimal pool size. - -Multi-DATA vectored batching was tested and rejected. Tokio-rustls can complete -a vectored write after consuming only part of a header/payload pair; extending -that state machine across several frames caused boundary corruption under -concurrency. The retained writer issues one frame at a time but already sends -its 8-byte header and owned payload through one vectored-write path without -coalescing them into a copy buffer. Reader and writer loops now also observe a -closed flag before waiting, and the reader yields after each 32 DATA frames so -WINDOW production cannot be starved by a continuously ready TLS carrier. - -The Apple Container Toxiproxy path became unreliable for sustained 10 ms bursts -above 48 MiB in this run, while 48 MiB crossed the 32 MiB connection window and -completed. The 100 and 300 ms sustained samples were run from fresh proxy -containers; the benchmark now records carrier topology alongside throughput and -RSS so future comparisons can distinguish pool-size changes from data-path -changes. - -## Current Nowhere 2 frame boundary - -The current Mux header remains 8 bytes but uses separate OPEN, DATA, WINDOW, -and CLOSE kinds. It is wire-incompatible with every earlier development Mux -format. Historical results above remain useful as performance baselines; they -do not demonstrate current wire interoperability. - -## 2026-09-08 nw2 frame smoke comparison - -The macOS Toxiproxy runner compared the saved `cc64358` binary with the -OPEN/DATA/WINDOW/CLOSE implementation. Each cell is the median of three serial -runs using the throughput profile. - -| Implementation | RTT | Flows | Payload | Throughput | Portal peak RSS | Vector peak RSS | -|---|---:|---:|---:|---:|---:|---:| -| `cc64358` | 100 ms | 1 | 64 MiB | 900.17 Mbps | 21.53 MiB | 9.78 MiB | -| nw2 frames | 100 ms | 1 | 64 MiB | 918.02 Mbps | 22.16 MiB | 9.98 MiB | -| `cc64358` | 300 ms | 16 | 128 MiB | 1.690 Gbps | 69.77 MiB | 13.11 MiB | -| nw2 frames | 300 ms | 16 | 128 MiB | 1.689 Gbps | 69.20 MiB | 12.61 MiB | - -Both smoke cells satisfy the 3% throughput and RSS thresholds. These results -exercise the real binaries and carrier topology, but Toxiproxy's TCP -termination means the Linux netem matrix remains the gating measurement. - -## 2026-09-08 eight-carrier pool and stream admission - -The current implementation shares at most eight connecting/established TLS -slots per session across directions. Idle carriers are reused before creating -more. At capacity, placement uses credit/queue occupancy, then live plus pending -streams. Connecting slots accept reservations and share one initializer, so a -cold burst is balanced before handshakes finish. There is no fixed Mux stream -count limit, setup-latency density rule, or scheduling timer. Application session -admission limits were still present in these measurements; the subsequent -consistent-admission cleanup removes them without changing pool placement. - -Each stream may have one queued/in-progress DATA frame. Receive queues rely on -byte credit rather than a per-stream packet count, removing the slow-small-packet -reader stall. No wire fields were added. The 30-second fully idle timeout remains. - -The saved pre-change release is `cc5623a` (`/tmp/nowhere-mux-before-pool8`). Both -implementations used `NOW_TRANSPORT_MEMORY_PROFILE=throughput`. No builds or tests -ran concurrently with the samples below. - -| Measurement | Implementation | TLS carriers | Throughput | Portal peak RSS | Vector peak RSS | -|---|---|---:|---:|---:|---:| -| Toxiproxy 300 ms, 16 flows, 128 MiB; median of 3 | Before | 4 | 1.668 Gbps | 75.56 MiB | 18.33 MiB | -| Toxiproxy 300 ms, 16 flows, 128 MiB; median of 3 | Current | 8 | 2.833 Gbps | 12.73 MiB | 10.66 MiB | -| Direct loopback, 16 flows, 24 GiB; one sample | Before | 1 | 13.086 Gbps | 26.61 MiB | 10.45 MiB | -| Direct loopback, 16 flows, 24 GiB; one sample | Current | 8 | 11.224 Gbps | 13.23 MiB | 12.78 MiB | - -The final delayed samples were 2833.47, 2836.52, and 2766.98 Mbps; baseline samples -were 1667.70, 1668.21, and 1662.83 Mbps. The delayed median improves 69.9%, while -the loopback point regresses 14.2%. Loopback transfers took 15.755 and 18.367 -seconds respectively. The fixed eight-slot policy therefore favors delayed -parallel transfers, not universal throughput improvement. This is not a completed -no-regression acceptance; the Linux netem matrix is still outstanding. - -```sh -tests/bench-mux-local.sh 300 16 8 1 target/release/nowhere -python3 tests/mux-bench-local.py --direct --rtt-ms 0 \ - --flows 16 --mib-per-flow 1536 --binary target/release/nowhere -``` - -Rejected experiments: - -- Pressure-only background expansion left the initial 16-flow burst on one - carrier and reached only 671.63 Mbps at the same proxy delay. -- Expanding without reservations for connecting carriers produced a 1094.90 Mbps - outlier among otherwise roughly 2.9 Gbps samples. Connecting-slot reservations - remove that first-completed-carrier placement bias. -- Allowing four queued DATA frames per flow gave only 11.368 Gbps in the loopback - point, with Portal/Vector RSS increasing to 14.89/15.38 MiB. The one-frame bound - was retained. - -A longer 300 ms proxy attempt (16 x 256 MiB) failed on the saved baseline with -early EOF and timeouts, so no sustained delayed comparison was obtained. This -does not establish whether the baseline or proxy path caused the failure. Short -proxy samples cannot substitute for packet-level loss/RTT testing. diff --git a/tests/bench-mux-local.sh b/tests/bench-mux-local.sh deleted file mode 100755 index 8d33248..0000000 --- a/tests/bench-mux-local.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/sh -# Copyright (C) 2026 NodePassProject -# SPDX-License-Identifier: GPL-3.0-only - -set -eu - -rtt_ms=${1:-100} -flows=${2:-1} -mib_per_flow=${3:-64} -mux=${4:-1} -binary=${5:-target/release/nowhere} -container_name=nowhere-toxiproxy - -# A timed-out high-rate sample can leave Toxiproxy links draining long after -# both Nowhere processes exit. Use a fresh proxy process so the next sample is -# independent instead of blocking while the old proxy is reconfigured. -container delete -f "$container_name" >/dev/null 2>&1 || true -while container list | awk 'NR > 1 { print $1 }' | grep -Fqx "$container_name"; do - sleep 0.1 -done -container run --rm -d --name "$container_name" \ - ghcr.io/shopify/toxiproxy:2.12.0 >/dev/null - -proxy_ip=$(container list | awk -v name="$container_name" ' - NR == 1 { - for (column = 1; column <= NF; column++) { - if ($column == "IP") ip_column = column - } - next - } - $1 == name && ip_column != 0 { - value = $ip_column - sub("/24", "", value) - print value - } -') -if [ -z "$proxy_ip" ]; then - echo "failed to discover Toxiproxy address" >&2 - exit 1 -fi - -if container exec "$container_name" /toxiproxy-cli list | grep -q '^nowhere'; then - container exec "$container_name" /toxiproxy-cli delete nowhere >/dev/null -fi -container exec "$container_name" /toxiproxy-cli create \ - -l 0.0.0.0:2001 -u 192.168.64.1:2000 nowhere >/dev/null - -one_way_ms=$((rtt_ms / 2)) -container exec "$container_name" /toxiproxy-cli toxic add \ - -n latency_upstream -t latency -a "latency=$one_way_ms" -u nowhere >/dev/null -container exec "$container_name" /toxiproxy-cli toxic add \ - -n latency_downstream -t latency -a "latency=$one_way_ms" -d nowhere >/dev/null - -python3 tests/mux-bench-local.py \ - --binary "$binary" \ - --rtt-ms "$rtt_ms" \ - --flows "$flows" \ - --mib-per-flow "$mib_per_flow" \ - --mux "$mux" \ - --proxy-host "$proxy_ip" \ - --external-proxy diff --git a/tests/bench-mux-matrix.py b/tests/bench-mux-matrix.py deleted file mode 100755 index 48528b3..0000000 --- a/tests/bench-mux-matrix.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env python3 -"""Run the fixed RTT/flow matrix and enforce throughput/RSS regression limits.""" - -import argparse -import json -import statistics -import subprocess -from pathlib import Path - - -RTTS = (0, 30, 100, 200, 300) -FLOWS = (1, 4, 16) - - -def mib_per_flow(rtt, flows): - if flows == 1: - return 32 if rtt == 300 else 64 - if flows == 4: - return 16 - return 8 - - -def sample(script, binary, rtt, flows, loss=0): - output = subprocess.check_output( - [ - str(script), - str(rtt), - str(flows), - str(mib_per_flow(rtt, flows)), - "1", - str(binary), - str(loss), - ], - text=True, - ) - return json.loads(output) - - -def median_sample(samples): - keys = ("mbps", "portal_peak_rss_mib", "vector_peak_rss_mib") - result = {key: statistics.median(item[key] for item in samples) for key in keys} - result["samples"] = samples - return result - - -def run_matrix(script, binary, repeats): - return { - f"{rtt}ms/{flows}": median_sample( - [sample(script, binary, rtt, flows) for _ in range(repeats)] - ) - for rtt in RTTS - for flows in FLOWS - } - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("--baseline", required=True) - parser.add_argument("--current", required=True) - parser.add_argument("--repeats", type=int, default=3) - parser.add_argument("--output") - parser.add_argument("--skip-loss-observation", action="store_true") - args = parser.parse_args() - if args.repeats < 3: - parser.error("--repeats must be at least 3") - script = Path(__file__).with_name("bench-mux-netem.sh").resolve() - baseline = run_matrix(script, Path(args.baseline).resolve(), args.repeats) - current = run_matrix(script, Path(args.current).resolve(), args.repeats) - failures = [] - for cell, before in baseline.items(): - after = current[cell] - if after["mbps"] < before["mbps"] * 0.97: - failures.append( - f"{cell}: throughput {after['mbps']:.2f} < 97% of {before['mbps']:.2f}" - ) - for process in ("portal", "vector"): - key = f"{process}_peak_rss_mib" - limit = before[key] + max(before[key] * 0.05, 2.0) - if after[key] > limit: - failures.append( - f"{cell}: {process} RSS {after[key]:.2f} MiB > {limit:.2f} MiB" - ) - loss = {} - if not args.skip_loss_observation: - for rtt, flows in ((100, 1), (300, 16)): - cell = f"{rtt}ms/{flows}" - loss[cell] = median_sample( - [sample(script, Path(args.current).resolve(), rtt, flows, 0.1) - for _ in range(args.repeats)] - ) - report = { - "baseline": baseline, - "current": current, - "loss_0_1_percent_observation": loss, - "failures": failures, - } - encoded = json.dumps(report, indent=2, sort_keys=True) - if args.output: - Path(args.output).write_text(encoded + "\n", encoding="utf-8") - print(encoded) - if failures: - raise SystemExit(1) - - -if __name__ == "__main__": - main() diff --git a/tests/bench-mux-netem.sh b/tests/bench-mux-netem.sh deleted file mode 100755 index 38e895a..0000000 --- a/tests/bench-mux-netem.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/sh -# Packet-level RTT/loss and RSS benchmark for a Linux host with iproute2. -set -eu - -rtt_ms=${1:-100} -flows=${2:-1} -mib_per_flow=${3:-64} -mux=${4:-1} -binary=${5:-target/release/nowhere} -loss=${6:-0} - -[ "$(uname -s)" = Linux ] || { echo "Linux is required" >&2; exit 2; } -[ "$(id -u)" -eq 0 ] || { echo "root is required for network namespaces" >&2; exit 2; } -command -v ip >/dev/null -command -v tc >/dev/null - -root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -binary=$(realpath "$binary") -worker="$root/tests/mux-bench-worker.py" -suffix=$$ -portal_ns=nwp-$suffix -vector_ns=nwv-$suffix -portal_pid= -vector_pid= -target_pid= - -cleanup() { - [ -z "$vector_pid" ] || kill "$vector_pid" 2>/dev/null || true - [ -z "$portal_pid" ] || kill "$portal_pid" 2>/dev/null || true - [ -z "$target_pid" ] || kill "$target_pid" 2>/dev/null || true - ip netns delete "$vector_ns" 2>/dev/null || true - ip netns delete "$portal_ns" 2>/dev/null || true -} -trap cleanup EXIT INT TERM - -ip netns add "$portal_ns" -ip netns add "$vector_ns" -ip link add nwpv-$suffix type veth peer name nwvp-$suffix -ip link set nwpv-$suffix netns "$portal_ns" -ip link set nwvp-$suffix netns "$vector_ns" -ip -n "$portal_ns" addr add 10.203.0.1/30 dev nwpv-$suffix -ip -n "$vector_ns" addr add 10.203.0.2/30 dev nwvp-$suffix -ip -n "$portal_ns" link set lo up -ip -n "$vector_ns" link set lo up -ip -n "$portal_ns" link set nwpv-$suffix up -ip -n "$vector_ns" link set nwvp-$suffix up - -one_way=$(awk -v rtt="$rtt_ms" 'BEGIN { printf "%.3f", rtt / 2 }') -ip netns exec "$portal_ns" tc qdisc add dev nwpv-$suffix root netem delay "${one_way}ms" loss "${loss}%" -ip netns exec "$vector_ns" tc qdisc add dev nwvp-$suffix root netem delay "${one_way}ms" loss "${loss}%" - -ip netns exec "$portal_ns" python3 "$worker" target --port 19000 & -target_pid=$! -ip netns exec "$portal_ns" env NOW_TRANSPORT_MEMORY_PROFILE=throughput "$binary" 'portal://secret@10.203.0.1/tcp:2000?log=none' & -portal_pid=$! -ip netns exec "$portal_ns" python3 "$worker" wait --port 2000 --host 10.203.0.1 - -ip netns exec "$vector_ns" env NOW_TRANSPORT_MEMORY_PROFILE=throughput "$binary" "vector://secret@10.203.0.1/tcp:2000?mux=$mux&socks=127.0.0.1:1080&log=none" & -vector_pid=$! -ip netns exec "$vector_ns" python3 "$worker" wait --port 1080 - -bytes=$((mib_per_flow * 1024 * 1024)) -result=$(ip netns exec "$vector_ns" python3 "$worker" client --flows "$flows" --bytes "$bytes" --socks-port 1080 --target-port 19000 --portal-pid "$portal_pid" --vector-pid "$vector_pid") -python3 -c 'import json,sys; d=json.loads(sys.argv[1]); d.update(rtt_ms=float(sys.argv[2]), loss_percent=float(sys.argv[3]), mux=int(sys.argv[4])); print(json.dumps(d, sort_keys=True))' "$result" "$rtt_ms" "$loss" "$mux" diff --git a/tests/mux-bench-local.py b/tests/mux-bench-local.py deleted file mode 100755 index 9461f52..0000000 --- a/tests/mux-bench-local.py +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/env python3 -"""Run an auxiliary TLS Mux throughput/RSS sample through Toxiproxy.""" - -import argparse -import json -import os -import signal -import socket -import struct -import subprocess -import threading -import time -import urllib.error -import urllib.request - - -def api(method, path, body=None): - data = None if body is None else json.dumps(body).encode() - request = urllib.request.Request( - f"http://127.0.0.1:8474{path}", data=data, method=method, - headers={"Content-Type": "application/json"}, - ) - with urllib.request.urlopen(request) as response: - return response.read() - - -def receive_exact(sock, size): - received = 0 - while received < size: - block = sock.recv(min(256 * 1024, size - received)) - if not block: - raise RuntimeError(f"early EOF after {received} bytes") - received += len(block) - return received - - -def target_server(stop, listener): - listener.settimeout(0.2) - - def serve(conn): - try: - request = b"" - while len(request) < 8: - part = conn.recv(8 - len(request)) - if not part: - return - request += part - remaining = struct.unpack("!Q", request)[0] - payload = bytes(256 * 1024) - while remaining: - count = min(remaining, len(payload)) - conn.sendall(payload[:count]) - remaining -= count - finally: - conn.close() - - try: - while not stop.is_set(): - try: - conn, _ = listener.accept() - except socket.timeout: - continue - threading.Thread(target=serve, args=(conn,), daemon=True).start() - finally: - listener.close() - - -def run_flow(port, target_port, byte_count, barrier, results, index): - try: - sock = socket.create_connection(("127.0.0.1", port), timeout=60) - sock.sendall(b"\x05\x01\x00") - if receive_bytes(sock, 2) != b"\x05\x00": - raise RuntimeError("SOCKS authentication failed") - sock.sendall(b"\x05\x01\x00\x01\x7f\x00\x00\x01" + struct.pack("!H", target_port)) - reply = receive_reply(sock) - if reply != 0: - raise RuntimeError(f"SOCKS CONNECT failed: {reply}") - barrier.wait(timeout=60) - started = time.monotonic() - sock.sendall(struct.pack("!Q", byte_count)) - receive_exact(sock, byte_count) - results[index] = time.monotonic() - started - sock.close() - except Exception as error: - results[index] = str(error) - barrier.abort() - - -def receive_reply(sock): - head = receive_bytes(sock, 4) - if head[3] == 1: - address_len = 4 - elif head[3] == 3: - address_len = receive_bytes(sock, 1)[0] - elif head[3] == 4: - address_len = 16 - else: - raise RuntimeError(f"unknown SOCKS address type: {head[3]}") - receive_bytes(sock, address_len + 2) - return head[1] - - -def receive_bytes(sock, size): - chunks = bytearray() - while len(chunks) < size: - chunk = sock.recv(size - len(chunks)) - if not chunk: - raise RuntimeError("early EOF") - chunks.extend(chunk) - return bytes(chunks) - - -def rss_kib(pid): - output = subprocess.check_output(["ps", "-o", "rss=", "-p", str(pid)], text=True) - return int(output.strip()) - - -def established_tcp(pid): - try: - output = subprocess.check_output( - ["lsof", "-nP", "-a", "-p", str(pid), "-iTCP", "-sTCP:ESTABLISHED"], - text=True, stderr=subprocess.DEVNULL, - ) - except (FileNotFoundError, subprocess.CalledProcessError): - return None - return max(0, len(output.splitlines()) - 1) - - -def wait_port(port, host="127.0.0.1", deadline=10): - until = time.monotonic() + deadline - while time.monotonic() < until: - try: - with socket.create_connection((host, port), timeout=0.5): - return - except OSError: - time.sleep(0.05) - raise RuntimeError(f"port {host}:{port} did not open") - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("--binary", default="target/release/nowhere") - parser.add_argument("--rtt-ms", type=int, required=True) - parser.add_argument("--flows", type=int, required=True) - parser.add_argument("--mib-per-flow", type=int, default=64) - parser.add_argument("--mux", choices=("0", "1"), default="1") - parser.add_argument("--profile", default="throughput") - parser.add_argument("--proxy-host", default="127.0.0.1") - parser.add_argument("--external-proxy", action="store_true") - parser.add_argument("--direct", action="store_true", help="loopback without Toxiproxy; RTT must be zero") - parser.add_argument("--debug", action="store_true") - args = parser.parse_args() - if args.direct and args.rtt_ms != 0: - parser.error("--direct requires --rtt-ms 0") - proxy_port = 2000 if args.direct else 2001 - - env = os.environ.copy() - env.update({"NOW_TRANSPORT_MEMORY_PROFILE": args.profile, "RUST_LOG": "off"}) - log = "debug" if args.debug else "none" - output = None if args.debug else subprocess.DEVNULL - portal = subprocess.Popen( - [args.binary, f"portal://secret@:2000?log={log}"], env=env, - stdout=output, stderr=output, - ) - vector = None - stop = threading.Event() - target_listener = socket.socket() - target_listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - target_listener.bind(("127.0.0.1", 0)) - target_listener.listen() - target_port = target_listener.getsockname()[1] - server = threading.Thread( - target=target_server, args=(stop, target_listener), daemon=True, - ) - server.start() - try: - wait_port(2000) - if not args.external_proxy and not args.direct: - api("DELETE", "/proxies/nowhere") - except urllib.error.HTTPError as error: - if error.code != 404: - raise - try: - if not args.external_proxy and not args.direct: - api("POST", "/proxies", { - "name": "nowhere", "listen": "0.0.0.0:2001", "upstream": "192.168.64.1:2000", - }) - latency = args.rtt_ms // 2 - for stream in ("upstream", "downstream"): - api("POST", f"/proxies/nowhere/toxics", { - "name": f"latency-{stream}", "type": "latency", "stream": stream, - "toxicity": 1.0, "attributes": {"latency": latency, "jitter": 0}, - }) - wait_port(proxy_port, args.proxy_host) - # Let a just-updated proxy path complete one RTT before the measured - # client opens its first carrier. Transfer timing starts later. - time.sleep(max(args.rtt_ms * 2 / 1000, 1.0)) - vector = subprocess.Popen( - [args.binary, f"vector://secret@{args.proxy_host}:{proxy_port}?mux={args.mux}&socks=127.0.0.1:1080&log={log}"], - env=env, stdout=output, stderr=output, - ) - wait_port(1080) - topology = {} - - def capture_topology(): - connections = established_tcp(vector.pid) - if connections is not None: - topology["tls_carriers"] = max(0, connections - args.flows) - - barrier = threading.Barrier(args.flows, action=capture_topology) - durations = [0.0] * args.flows - byte_count = args.mib_per_flow * 1024 * 1024 - peak = {"portal": rss_kib(portal.pid), "vector": rss_kib(vector.pid)} - sampling = True - - def sample(): - while sampling: - peak["portal"] = max(peak["portal"], rss_kib(portal.pid)) - peak["vector"] = max(peak["vector"], rss_kib(vector.pid)) - time.sleep(0.1) - - monitor = threading.Thread(target=sample, daemon=True) - monitor.start() - threads = [threading.Thread( - target=run_flow, - args=(1080, target_port, byte_count, barrier, durations, index), - daemon=True, - ) for index in range(args.flows)] - wall_start = time.monotonic() - for thread in threads: - thread.start() - for thread in threads: - thread.join() - setup_and_transfer = time.monotonic() - wall_start - sampling = False - monitor.join() - errors = [result for result in durations if not isinstance(result, float)] - if errors: - raise RuntimeError(f"flow failures: {errors}") - total = byte_count * args.flows - transfer = max(durations) - result = { - "binary": args.binary, - "rtt_ms": args.rtt_ms, "flows": args.flows, "mux": int(args.mux), - "profile": args.profile, "mib": total / 1024 / 1024, - "seconds": round(transfer, 3), "mbps": round(total * 8 / transfer / 1_000_000, 2), - "setup_and_transfer_seconds": round(setup_and_transfer, 3), - "flow_max_seconds": round(max(durations), 3), - "portal_peak_rss_mib": round(peak["portal"] / 1024, 2), - "vector_peak_rss_mib": round(peak["vector"] / 1024, 2), - } - result.update(topology) - print(json.dumps(result, sort_keys=True)) - finally: - stop.set() - for process in (vector, portal): - if process is not None and process.poll() is None: - process.send_signal(signal.SIGTERM) - try: - process.wait(timeout=3) - except subprocess.TimeoutExpired: - process.kill() - - -if __name__ == "__main__": - main() diff --git a/tests/mux-bench-worker.py b/tests/mux-bench-worker.py deleted file mode 100755 index d4626eb..0000000 --- a/tests/mux-bench-worker.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python3 -"""Namespace-local target and load generator for the TLS Mux netem benchmark.""" - -import argparse -import json -import socket -import struct -import threading -import time - - -def receive_exact(sock, size): - received = 0 - while received < size: - block = sock.recv(min(256 * 1024, size - received)) - if not block: - raise RuntimeError(f"early EOF after {received} bytes") - received += len(block) - - -def target(port): - listener = socket.create_server(("127.0.0.1", port), reuse_port=False) - while True: - conn, _ = listener.accept() - - def serve(stream): - try: - request = b"" - while len(request) < 8: - block = stream.recv(8 - len(request)) - if not block: - return - request += block - remaining = struct.unpack("!Q", request)[0] - payload = bytes(256 * 1024) - while remaining: - count = min(remaining, len(payload)) - stream.sendall(payload[:count]) - remaining -= count - finally: - stream.close() - - threading.Thread(target=serve, args=(conn,), daemon=True).start() - - -def receive_socks_reply(sock): - head = sock.recv(4) - if len(head) != 4 or head[1] != 0: - raise RuntimeError(f"SOCKS CONNECT failed: {head!r}") - lengths = {1: 4, 4: 16} - length = lengths.get(head[3]) - if head[3] == 3: - length = receive_one(sock) - if length is None: - raise RuntimeError("invalid SOCKS address type") - receive_exact(sock, length + 2) - - -def receive_one(sock): - value = sock.recv(1) - if len(value) != 1: - raise RuntimeError("early SOCKS EOF") - return value[0] - - -def rss_kib(pid, field): - try: - with open(f"/proc/{pid}/status", encoding="ascii") as status: - for line in status: - if line.startswith(field + ":"): - return int(line.split()[1]) - except (FileNotFoundError, ProcessLookupError): - pass - return 0 - - -def client(args): - barrier = threading.Barrier(args.flows) - durations = [None] * args.flows - peak = {"portal": 0, "vector": 0} - sampling = threading.Event() - sampling.set() - - def monitor(): - while sampling.is_set(): - peak["portal"] = max(peak["portal"], rss_kib(args.portal_pid, "VmRSS")) - peak["vector"] = max(peak["vector"], rss_kib(args.vector_pid, "VmRSS")) - time.sleep(0.01) - - def flow(index): - try: - sock = socket.create_connection(("127.0.0.1", args.socks_port), timeout=60) - sock.sendall(b"\x05\x01\x00") - if sock.recv(2) != b"\x05\x00": - raise RuntimeError("SOCKS authentication failed") - sock.sendall( - b"\x05\x01\x00\x01\x7f\x00\x00\x01" - + struct.pack("!H", args.target_port) - ) - receive_socks_reply(sock) - barrier.wait(timeout=60) - started = time.monotonic() - sock.sendall(struct.pack("!Q", args.bytes)) - receive_exact(sock, args.bytes) - durations[index] = time.monotonic() - started - sock.close() - except Exception as error: - durations[index] = str(error) - barrier.abort() - - monitor_thread = threading.Thread(target=monitor, daemon=True) - monitor_thread.start() - threads = [threading.Thread(target=flow, args=(index,)) for index in range(args.flows)] - for thread in threads: - thread.start() - for thread in threads: - thread.join() - sampling.clear() - monitor_thread.join() - errors = [value for value in durations if not isinstance(value, float)] - if errors: - raise RuntimeError(str(errors)) - peak["portal"] = max(peak["portal"], rss_kib(args.portal_pid, "VmHWM")) - peak["vector"] = max(peak["vector"], rss_kib(args.vector_pid, "VmHWM")) - elapsed = max(durations) - total = args.bytes * args.flows - print(json.dumps({ - "flows": args.flows, - "mib": total / 1024 / 1024, - "seconds": round(elapsed, 3), - "mbps": round(total * 8 / elapsed / 1_000_000, 2), - "portal_peak_rss_mib": round(peak["portal"] / 1024, 2), - "vector_peak_rss_mib": round(peak["vector"] / 1024, 2), - }, sort_keys=True)) - - -def wait_port(port, host): - deadline = time.monotonic() + 10 - while time.monotonic() < deadline: - try: - with socket.create_connection((host, port), timeout=0.2): - return - except OSError: - time.sleep(0.05) - raise RuntimeError(f"port {port} did not open") - - -def main(): - parser = argparse.ArgumentParser() - sub = parser.add_subparsers(dest="role", required=True) - target_parser = sub.add_parser("target") - target_parser.add_argument("--port", type=int, required=True) - wait_parser = sub.add_parser("wait") - wait_parser.add_argument("--port", type=int, required=True) - wait_parser.add_argument("--host", default="127.0.0.1") - client_parser = sub.add_parser("client") - client_parser.add_argument("--flows", type=int, required=True) - client_parser.add_argument("--bytes", type=int, required=True) - client_parser.add_argument("--socks-port", type=int, required=True) - client_parser.add_argument("--target-port", type=int, required=True) - client_parser.add_argument("--portal-pid", type=int, required=True) - client_parser.add_argument("--vector-pid", type=int, required=True) - args = parser.parse_args() - if args.role == "target": - target(args.port) - elif args.role == "wait": - wait_port(args.port, args.host) - else: - client(args) - - -if __name__ == "__main__": - main() From ae7653b492d5112e24fa68a482dd6273ebcbc558 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Tue, 8 Sep 2026 20:04:36 +0800 Subject: [PATCH 32/62] Restructure current protocol documentation --- docs/README.md | 26 ++++++++--------- docs/compatibility.md | 48 ------------------------------- docs/configuration.md | 11 ++++---- docs/interoperability.md | 61 ++++++++++++++++++++++++++++++++++++++++ docs/operations.md | 2 +- docs/protocol.md | 12 ++++---- docs/quick-start.md | 3 +- docs/security.md | 2 +- src/main.rs | 2 +- 9 files changed, 89 insertions(+), 78 deletions(-) delete mode 100644 docs/compatibility.md create mode 100644 docs/interoperability.md diff --git a/docs/README.md b/docs/README.md index 7c34fa1..712386c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,21 +1,21 @@ # Documentation -The documentation has one source of truth for each concern: +Each document owns one part of the Nowhere interface. | Need | Document | |---|---| -| Run a local Portal and Vector | [Quick start](quick-start.md) | -| Choose and operate a supported platform | [Platforms](platforms.md) | -| Understand URL and environment options | [Configuration](configuration.md) | -| Implement or inspect the wire format | [Protocol](protocol.md) | -| Deploy and observe the processes | [Operations](operations.md) | -| Review authentication and memory bounds | [Security](security.md) | -| Understand version negotiation and peer interoperability | [Interoperability](compatibility.md) | -| Implement another client or integration | [Integrations](integrations.md) | - -`configuration.md` is authoritative for command URLs and runtime settings. -`protocol.md` is normative for bytes exchanged between peers. Portal and -Vector share one internal bounded TLS Mux engine. +| Start a local Portal and Vector | [Quick start](quick-start.md) | +| Configure URLs and runtime behavior | [Configuration](configuration.md) | +| Deploy and observe processes | [Operations](operations.md) | +| Choose a supported system | [Platforms](platforms.md) | +| Review authentication and resource bounds | [Security](security.md) | +| Understand peer and carrier contracts | [Interoperability](interoperability.md) | +| Implement the wire format | [Protocol](protocol.md) | +| Connect another client or service | [Integrations](integrations.md) | + +[Configuration](configuration.md) defines command URLs and runtime settings. +[Protocol](protocol.md) defines bytes exchanged between peers. The other guides +describe how those interfaces are used and operated. Portal and Vector have the same transport behavior on Linux, macOS, and Windows. Platform-specific packaging, process control, filesystem paths, and diff --git a/docs/compatibility.md b/docs/compatibility.md deleted file mode 100644 index a001c85..0000000 --- a/docs/compatibility.md +++ /dev/null @@ -1,48 +0,0 @@ -# Interoperability - -## Nowhere 2 wire boundary - -Nowhere 2 supports one data-plane protocol. TLS/TCP and QUIC clients must offer -the ALPN `nw2`, and the negotiated ALPN must be exactly `nw2`. A peer that -offers only `now/1`, a custom ALPN, or no ALPN cannot establish a Nowhere 2 -carrier. - -Nowhere 1 wire compatibility is intentionally absent. This includes the -authentication key derivation and TLS Mux framing. Portal and Vector must be -upgraded together. - -The `alpn` URL parameter remains an ignored unknown parameter. It cannot change -the fixed wire protocol. - -## Endpoint configuration - -The compact `portal://key@host:port`, `vector://key@host:port`, and -`next=key@host:port` forms declare TCP and UDP on the same port. The empty -Portal host in `portal://key@:port` is an alias for the wildcard host `*`. - -Explicit carrier paths select listeners and dial targets: - -```text -portal://key@*/tcp:2006 -portal://key@*/udp:2017 -portal://key@*/tcp4:2006/udp6:2017 -``` - -The `net` query follows the unknown-parameter rule and has no effect. Endpoint -syntax is local configuration and is not transmitted on the wire. - -## TLS lane contract - -Vector `mux=0` opens one authenticated TLS connection per flow. Vector `mux=1` -opens marked Mux carriers and assigns logical streams to adaptive shards. -Portal accepts both forms on one TLS listener. After the 32-byte AuthFrame, -`0xff` selects Mux framing; every other valid first byte begins a dedicated -FlowHeader. - -Mux carriers open lazily, choose the least-loaded live shard, and use at most -four physical TLS carriers per logical direction. A symmetric TCP/TCP flow uses -one full-duplex logical stream. A carrier closes after it has remained fully -idle for 30 seconds. - -Dedicated lanes, Mux streams, and QUIC streams use the same FlowHeader, Target, -setup result, pairing, and resource limits. diff --git a/docs/configuration.md b/docs/configuration.md index 84f0025..5abfb6e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -289,8 +289,8 @@ Durations use humantime syntax such as `250ms`, `15s`, `2m`, or `1h`. | Variable | Default | Purpose | |---|---:|---| -| `NOW_QUIC_UDP_QUEUE_BYTES` | `4 MiB` | QUIC datagram and reassembly byte budget | | `NOW_TRANSPORT_MEMORY_PROFILE` | `throughput` | QUIC and TLS Mux profile: `memory`, `balanced`, or `throughput` | +| `NOW_QUIC_UDP_QUEUE_BYTES` | `4 MiB` | QUIC datagram and reassembly byte budget | | `NOW_FLOW_PAIR_TIMEOUT` | `15s` | Portal split-flow pairing deadline | | `NOW_FLOW_SETUP_TIMEOUT` | `20s` | Client wait for `SetupResult` | | `NOW_MIX_FALLBACK_TIMEOUT` | `1s` | Primary Mix route preparation budget before fallback | @@ -314,13 +314,12 @@ window. Each flow has at most one DATA frame queued or being written, so a bulk writer cannot fill the shared queue. Receive queues are bounded by byte credit without blocking unrelated flows on per-flow frame counts. The application shares at most eight carriers across both directions and retires -fully idle shards after 30 seconds. There are no fixed application flow-count -limits for TCP, UDP, SOCKS associations, or pending split pairs. The former -`NOW_MAX_TCP_FLOWS`, `NOW_MAX_UDP_FLOWS`, and `NOW_MAX_PENDING_PAIRS` settings -are removed and ignored. QUIC stream credit grows with live and pending QUIC +fully idle shards after 30 seconds. TCP, UDP, SOCKS associations, and pending +split pairs have no fixed application flow-count setting. QUIC stream credit +grows with live and pending QUIC flows, reserving setup headroom of at least 64 streams or 25% of that count. This avoids a fixed active-flow ceiling and excessive eager stream allocation. -Byte budgets and setup, pairing, and idle deadlines still apply; per-flow +Byte budgets and setup, pairing, and idle deadlines apply; per-flow metadata and target sockets grow with concurrency. Portal and Vector use the same QUIC profile regardless of the client Mux setting. diff --git a/docs/interoperability.md b/docs/interoperability.md new file mode 100644 index 0000000..bc7920c --- /dev/null +++ b/docs/interoperability.md @@ -0,0 +1,61 @@ +# Interoperability + +## Peer contract + +Every TLS/TCP and QUIC/UDP carrier uses TLS 1.3 and the ALPN `nw2`. The client +offers `nw2`, and the server accepts a carrier only when TLS selects it. The +authentication derivation, flow headers, setup results, and Mux frames follow +the [Nowhere wire protocol](protocol.md). + +Portal and Vector use the same wire contract. Alternate clients and native +Portal chains follow it as peers rather than relying on a separately versioned +SDK. + +## Endpoint contract + +Portal listeners, Vector remotes, and Portal `next` endpoints share two forms: + +```text +HOST:PORT +HOST/CARRIER:PORT[/CARRIER:PORT] +``` + +The compact form declares TLS/TCP and QUIC/UDP on one numeric port. The explicit +form declares only the listed carriers and may select separate ports or address +families. An empty Portal host, as in `portal://key@:2000`, selects the wildcard +listener. Vector and `next` endpoints require a dialable host. + +Endpoint syntax selects local sockets and is not transmitted on the wire. The +complete grammar and validation rules are in [Configuration](configuration.md). + +## TLS lane contract + +| Mux setting | TLS behavior | Failure scope | +|---|---|---| +| `mux=0` | One authenticated connection per logical flow | One flow | +| `mux=1` | Logical streams share TLS carriers | Every stream on the failed carrier | + +After the 32-byte AuthFrame, `0xff` selects Mux framing; every other valid first +byte starts a dedicated FlowHeader. Portal accepts both lane forms on the same +TLS listener. + +The Mux pool is full duplex and shared by both logical directions. It opens +carriers lazily, reuses idle carriers, selects the least occupied carrier at +capacity, and contains at most eight connecting or established carriers per +session. A fully idle carrier closes after 30 seconds. Logical stream counts +have no fixed application limit. + +## Route contract + +Uplink and downlink independently use TLS/TCP or QUIC/UDP. Every concrete route +uses the same FlowHeader, Target, pairing, setup-result, and relay semantics. +`mix` is a local client policy that resolves to a concrete route before the +FlowHeader is sent. + +| `up` / `down` | `tcp` | `udp` | `mix` | +|---|---|---|---| +| `tcp` | TT | TQ | TT or TQ | +| `udp` | QT | QQ | QT or QQ | +| `mix` | TT or QT | TQ or QQ | TT or QQ | + +T denotes TLS/TCP and Q denotes QUIC/UDP, with uplink first. diff --git a/docs/operations.md b/docs/operations.md index 7998639..1390f69 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -65,7 +65,7 @@ close after 30 seconds fully idle. Frame queue slots do not bypass byte credit. permits and payload is admitted incrementally. QUIC stream credit grows with live and pending QUIC flows plus setup headroom. -Pairing and setup deadlines still reclaim incomplete requests. +Pairing and setup deadlines reclaim incomplete requests. QUIC uses the shared `throughput` memory profile by default. Select `balanced` or `memory` when connection density matters more than a single flow's diff --git a/docs/protocol.md b/docs/protocol.md index fb60f84..bcebff7 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -21,9 +21,9 @@ otherwise. ## 1. Carrier model -TLS/TCP and QUIC use TLS 1.3 with the sole ALPN `nw2`. A client that does not -offer `nw2`, or a handshake that does not select exactly `nw2`, is rejected -before Nowhere authentication. Nowhere 2 does not implement the V1 wire format. +TLS/TCP and QUIC use TLS 1.3 with the sole ALPN `nw2`. A client offers `nw2`, +and the server requires the handshake to select exactly `nw2` before Nowhere +authentication. ### Command endpoint mapping @@ -288,7 +288,7 @@ number of carriers for many idle logical streams. Receive queues use byte-credit admission rather than blocking the carrier reader on a per-flow frame count. Every DATA frame consumes at least one KiB of credit, bounding queued payload and DATA metadata across the carrier. Stream and lifecycle -metadata still grow with live/pending streams; no fixed stream limit does not mean +metadata grow with live/pending streams; no fixed stream limit does not mean constant process memory. Authentication remains separate from Mux placement. A fully idle carrier closes after 30 seconds. Portal applies the same timeout to an authenticated Mux carrier with no active streams. Sharding is runtime placement and does not add wire fields. @@ -543,8 +543,8 @@ ATTACH. ## 11. Runtime limits and failure scope Application sessions impose no fixed TCP, UDP, or pending-pair count limit. -Flow IDs remain unique within their wire identifier space. Byte flow control, -queue budgets, and pairing/setup timeouts remain enforced. +Flow IDs are unique within their wire identifier space. Byte flow control, +queue budgets, and pairing/setup timeouts apply. QUIC bidirectional-stream credit grows with live and pending QUIC flows, with setup headroom of max(64, live / 4). A QUIC TCP flow owns one reliable stream; diff --git a/docs/quick-start.md b/docs/quick-start.md index 1ef1157..cd47d77 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -124,8 +124,7 @@ automatically: nowhere 'vector://secret@127.0.0.1:2000?up=tcp&down=tcp&mux=1&socks=127.0.0.1:1080' ``` -Nowhere 2 requires the fixed `nw2` ALPN at both ends and does not interoperate -with the V1 wire protocol. There is no configurable ALPN parameter. +Both peers use the fixed `nw2` ALPN. The ALPN is not configurable. `udp/udp&mux=1` is canonicalized to `mux=0` because no TLS lane can use it. ## 3. Use SOCKS5 diff --git a/docs/security.md b/docs/security.md index 12c9269..9056000 100644 --- a/docs/security.md +++ b/docs/security.md @@ -89,7 +89,7 @@ retained application payload. These are credit ceilings rather than eagerly allocated payload buffers. TCP, UoT, and QUIC flows all follow the same policy: byte budgets and lifecycle -timeouts remain, without fixed application flow-count admission. QUIC expands +timeouts apply without fixed application flow-count admission. QUIC expands stream credit with actual demand instead of preallocating a huge stream ceiling. Operators control aggregate exposure through key distribution, host resource limits, and network-level admission policy. diff --git a/src/main.rs b/src/main.rs index dc53f3f..c99a2d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -139,8 +139,8 @@ SOCKS5 inbound: SOCKS5 UDP fragmentation is not supported. Environment: - NOW_QUIC_UDP_QUEUE_BYTES Maximum queued/reassembling UDP bytes per QUIC connection. NOW_TRANSPORT_MEMORY_PROFILE memory, balanced, or throughput. Default: throughput. + NOW_QUIC_UDP_QUEUE_BYTES Maximum queued/reassembling UDP bytes per QUIC connection. NOW_FLOW_PAIR_TIMEOUT Timeout for completing a split logical flow. NOW_FLOW_SETUP_TIMEOUT Timeout for waiting for a logical flow to become ready. NOW_MIX_FALLBACK_TIMEOUT Primary Mix route preparation budget. Default: 1s. From b8c77800b2e53603ed4ab27c1a6f93a77dde8aff Mon Sep 17 00:00:00 2001 From: yosebyte Date: Wed, 9 Sep 2026 23:27:40 +0800 Subject: [PATCH 33/62] Derive Morph transport keys --- Cargo.lock | 22 ++++++++++ Cargo.toml | 1 + src/protocol/auth.rs | 16 +++++++- src/tests/transport/morph.rs | 5 +++ src/tests/transport/morph/keys.rs | 30 ++++++++++++++ src/transport/mod.rs | 1 + src/transport/morph.rs | 68 +++++++++++++++++++++++++++++++ 7 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 src/tests/transport/morph.rs create mode 100644 src/tests/transport/morph/keys.rs create mode 100644 src/transport/morph.rs diff --git a/Cargo.lock b/Cargo.lock index 9e9f682..acd35b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -157,6 +157,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" dependencies = [ "cfg-if", + "cipher", "cpufeatures", "rand_core", ] @@ -172,6 +173,17 @@ dependencies = [ "windows-link", ] +[[package]] +name = "cipher" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +dependencies = [ + "block-buffer", + "crypto-common", + "inout", +] + [[package]] name = "cmov" version = "0.5.4" @@ -704,6 +716,15 @@ dependencies = [ "rustversion", ] +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "hybrid-array", +] + [[package]] name = "instability" version = "0.3.13" @@ -869,6 +890,7 @@ version = "2.0.0-dev" dependencies = [ "anyhow", "bytes", + "chacha20", "chrono", "crossterm", "getrandom 0.4.3", diff --git a/Cargo.toml b/Cargo.toml index af119fa..0215888 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ readme = "README.md" [dependencies] anyhow = "1.0.104" bytes = "1.12.1" +chacha20 = "0.10.1" chrono = { version = "0.4.45", default-features = false, features = ["clock"] } crossterm = "0.29.0" getrandom = "0.4.3" diff --git a/src/protocol/auth.rs b/src/protocol/auth.rs index 068597a..dd31718 100644 --- a/src/protocol/auth.rs +++ b/src/protocol/auth.rs @@ -53,10 +53,24 @@ impl Credentials { if parsed_url.password().is_some() { bail!("password credentials are not supported; put the shared key before '@'"); } - let shared_key = decode_url_username(parsed_url)?; + let shared_key = Self::decode_shared_key(parsed_url)?; Self::from_shared_key(&shared_key) } + pub(crate) fn decode_shared_key(parsed_url: &Url) -> Result> { + if parsed_url.password().is_some() { + bail!("password credentials are not supported; put the shared key before '@'"); + } + let shared_key = decode_url_username(parsed_url)?; + if shared_key.is_empty() { + bail!("missing shared key before '@'"); + } + if shared_key.len() > u8::MAX as usize { + bail!("shared key exceeds the 255-byte limit"); + } + Ok(shared_key) + } + /// Derives credentials directly from non-empty shared-key bytes. pub fn from_shared_key(shared_key: &[u8]) -> Result { if shared_key.is_empty() { diff --git a/src/tests/transport/morph.rs b/src/tests/transport/morph.rs new file mode 100644 index 0000000..f52bf4e --- /dev/null +++ b/src/tests/transport/morph.rs @@ -0,0 +1,5 @@ +// Copyright (C) 2026 NodePassProject +// SPDX-License-Identifier: GPL-3.0-only + +#[path = "morph/keys.rs"] +mod keys; diff --git a/src/tests/transport/morph/keys.rs b/src/tests/transport/morph/keys.rs new file mode 100644 index 0000000..0c66969 --- /dev/null +++ b/src/tests/transport/morph/keys.rs @@ -0,0 +1,30 @@ +// Copyright (C) 2026 NodePassProject +// SPDX-License-Identifier: GPL-3.0-only + +use super::super::*; + +fn hex(value: &str) -> [u8; N] { + assert_eq!(value.len(), N * 2); + let mut bytes = [0; N]; + for (index, byte) in bytes.iter_mut().enumerate() { + *byte = u8::from_str_radix(&value[index * 2..index * 2 + 2], 16).unwrap(); + } + bytes +} + +#[test] +fn derives_fixed_hkdf_sha256_keys() { + let keys = MorphKeys::derive(b"test portal key"); + assert_eq!( + keys.tcp_c2s, + hex("90df47db82553ab6b0489ea77a085593475a70c6a61e957ad3ffe0824bd2126a") + ); + assert_eq!( + keys.tcp_s2c, + hex("20bc17a22d08469e60efd4c6bdda76f190c33946599a0797bab2d52007b97e27") + ); + assert_eq!( + keys.udp, + hex("484b5f06a66e566099da4886e3bb2ec342aedee0ebcd7ea426d68ece4f3f8ea2") + ); +} diff --git a/src/transport/mod.rs b/src/transport/mod.rs index d681c4c..dbf5db4 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -4,6 +4,7 @@ //! Transport support primitives for buffers, rate limits, and counters. mod buffers; +mod morph; mod owned_io; mod quic; mod rate; diff --git a/src/transport/morph.rs b/src/transport/morph.rs new file mode 100644 index 0000000..0c59fdc --- /dev/null +++ b/src/transport/morph.rs @@ -0,0 +1,68 @@ +// Copyright (C) 2026 NodePassProject +// SPDX-License-Identifier: GPL-3.0-only + +//! Optional keyed wire transform below TLS and QUIC. + +use std::fmt; + +use hmac::{Hmac, KeyInit as HmacKeyInit, Mac}; +use sha2::Sha256; +const MORPH_ROOT_SALT: &[u8] = b"nowhere/morph"; +const TCP_C2S_INFO: &[u8] = b"tcp c2s"; +const TCP_S2C_INFO: &[u8] = b"tcp s2c"; +const UDP_INFO: &[u8] = b"udp"; + +type MorphKey = [u8; 32]; + +#[derive(Clone)] +pub(crate) struct MorphKeys { + tcp_c2s: MorphKey, + tcp_s2c: MorphKey, + udp: MorphKey, +} + +impl fmt::Debug for MorphKeys { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("MorphKeys(REDACTED)") + } +} + +impl MorphKeys { + pub(crate) fn from_url(url: &url::Url) -> anyhow::Result { + Ok(Self::derive( + &crate::protocol::Credentials::decode_shared_key(url)?, + )) + } + + pub(crate) fn derive(shared_key: &[u8]) -> Self { + let root = hmac_sha256(MORPH_ROOT_SALT, shared_key); + Self { + tcp_c2s: hkdf_expand_one(root, TCP_C2S_INFO), + tcp_s2c: hkdf_expand_one(root, TCP_S2C_INFO), + udp: hkdf_expand_one(root, UDP_INFO), + } + } + + pub(crate) fn udp_key(&self) -> MorphKey { + self.udp + } +} + +fn hmac_sha256(key: &[u8], data: &[u8]) -> MorphKey { + let mut mac = + as HmacKeyInit>::new_from_slice(key).expect("HMAC accepts any key length"); + mac.update(data); + mac.finalize().into_bytes().into() +} + +fn hkdf_expand_one(root: MorphKey, info: &[u8]) -> MorphKey { + let mut mac = + as HmacKeyInit>::new_from_slice(&root).expect("HMAC accepts a 32-byte key"); + mac.update(info); + mac.update(&[1]); + mac.finalize().into_bytes().into() +} + +#[cfg(test)] +#[path = "../tests/transport/morph.rs"] +mod tests; From 79c2778cbb652c9029fb8c2eba46b3ab9368f032 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Wed, 9 Sep 2026 23:28:04 +0800 Subject: [PATCH 34/62] Add Morph TCP stream transform --- src/tests/transport/morph.rs | 2 + src/tests/transport/morph/tcp.rs | 113 +++++++++++ src/transport/mod.rs | 1 + src/transport/morph.rs | 14 ++ src/transport/morph/tcp.rs | 311 +++++++++++++++++++++++++++++++ 5 files changed, 441 insertions(+) create mode 100644 src/tests/transport/morph/tcp.rs create mode 100644 src/transport/morph/tcp.rs diff --git a/src/tests/transport/morph.rs b/src/tests/transport/morph.rs index f52bf4e..0e7737c 100644 --- a/src/tests/transport/morph.rs +++ b/src/tests/transport/morph.rs @@ -3,3 +3,5 @@ #[path = "morph/keys.rs"] mod keys; +#[path = "morph/tcp.rs"] +mod tcp; diff --git a/src/tests/transport/morph/tcp.rs b/src/tests/transport/morph/tcp.rs new file mode 100644 index 0000000..942ee2f --- /dev/null +++ b/src/tests/transport/morph/tcp.rs @@ -0,0 +1,113 @@ +// Copyright (C) 2026 NodePassProject +// SPDX-License-Identifier: GPL-3.0-only + +use chacha20::ChaCha20; +use chacha20::cipher::KeyIvInit; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; + +use super::super::*; + +fn hex(value: &str) -> [u8; N] { + assert_eq!(value.len(), N * 2); + let mut bytes = [0; N]; + for (index, byte) in bytes.iter_mut().enumerate() { + *byte = u8::from_str_radix(&value[index * 2..index * 2 + 2], 16).unwrap(); + } + bytes +} + +fn set_tcp_nonce(stream: &mut MorphTcpStream, nonce: [u8; NONCE_LEN]) { + let state = stream.morph.as_mut().unwrap(); + state.prefix = nonce; + state.read_cipher = Some(ChaCha20::new( + (&state.read_key).into(), + (&state.prefix).into(), + )); + state.write_cipher = Some(ChaCha20::new( + (&state.write_key).into(), + (&state.prefix).into(), + )); +} + +#[test] +fn chacha20_starts_at_block_zero() { + let mut block = [0u8; 64]; + apply_at(&[0; 32], &[0; 12], 0, &mut block).unwrap(); + assert_eq!( + block, + hex( + "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586" + ) + ); +} + +#[tokio::test] +async fn tcp_uses_one_client_nonce_and_independent_directions() { + let keys = MorphKeys::derive(b"shared"); + let (client_io, server_io) = tokio::io::duplex(4096); + let mut client = MorphTcpStream::client(client_io, Some(keys.clone())).unwrap(); + set_tcp_nonce(&mut client, [7; NONCE_LEN]); + let mut server = MorphTcpStream::server(server_io, Some(keys)); + + client.write_all(b"client hello").await.unwrap(); + let mut request = [0; 12]; + server.read_exact(&mut request).await.unwrap(); + assert_eq!(&request, b"client hello"); + + server.write_all(b"server reply").await.unwrap(); + let mut response = [0; 12]; + client.read_exact(&mut response).await.unwrap(); + assert_eq!(&response, b"server reply"); +} + +#[tokio::test] +async fn tcp_server_waits_for_the_client_nonce_before_writing() { + let keys = MorphKeys::derive(b"shared"); + let (client_io, server_io) = tokio::io::duplex(4096); + let mut client = MorphTcpStream::client(client_io, Some(keys.clone())).unwrap(); + set_tcp_nonce(&mut client, [8; NONCE_LEN]); + let server = MorphTcpStream::server(server_io, Some(keys)); + let (mut server_reader, mut server_writer) = tokio::io::split(server); + + let reply = tokio::spawn(async move { server_writer.write_all(b"reply").await }); + tokio::task::yield_now().await; + assert!(!reply.is_finished()); + + client.write_all(b"hello").await.unwrap(); + let mut request = [0; 5]; + server_reader.read_exact(&mut request).await.unwrap(); + assert_eq!(&request, b"hello"); + reply.await.unwrap().unwrap(); + + let mut response = [0; 5]; + client.read_exact(&mut response).await.unwrap(); + assert_eq!(&response, b"reply"); +} + +#[tokio::test] +async fn tcp_preserves_offsets_across_small_io_chunks() { + let keys = MorphKeys::derive(b"shared"); + let (client_io, server_io) = tokio::io::duplex(1024); + let mut client = MorphTcpStream::client(client_io, Some(keys.clone())).unwrap(); + set_tcp_nonce(&mut client, [9; NONCE_LEN]); + let mut server = MorphTcpStream::server(server_io, Some(keys)); + let payload = vec![0x5a; 65_537]; + let expected = payload.clone(); + + let writer = tokio::spawn(async move { client.write_all(&payload).await }); + let mut received = vec![0; expected.len()]; + server.read_exact(&mut received).await.unwrap(); + writer.await.unwrap().unwrap(); + assert_eq!(received, expected); +} + +#[tokio::test] +async fn tcp_write_processes_the_full_available_buffer() { + let keys = MorphKeys::derive(b"shared"); + let (client_io, _peer_io) = tokio::io::duplex(256 * 1024); + let mut client = MorphTcpStream::client(client_io, Some(keys)).unwrap(); + set_tcp_nonce(&mut client, [10; NONCE_LEN]); + let payload = vec![0x5a; 128 * 1024]; + + assert_eq!(client.write(&payload).await.unwrap(), payload.len()); +} diff --git a/src/transport/mod.rs b/src/transport/mod.rs index dbf5db4..f22d58b 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -11,6 +11,7 @@ mod rate; mod stats; pub use buffers::{BufferLease, Buffers}; +pub(crate) use morph::{MorphKeys, MorphTcpStream}; pub(crate) use owned_io::{ AsyncReadAny, AsyncWriteAny, read_owned, read_owned_from, write_owned, write_owned_to, }; diff --git a/src/transport/morph.rs b/src/transport/morph.rs index 0c59fdc..fb883f3 100644 --- a/src/transport/morph.rs +++ b/src/transport/morph.rs @@ -4,9 +4,16 @@ //! Optional keyed wire transform below TLS and QUIC. use std::fmt; +use std::io; use hmac::{Hmac, KeyInit as HmacKeyInit, Mac}; use sha2::Sha256; + +mod tcp; + +pub(crate) use tcp::MorphTcpStream; + +const NONCE_LEN: usize = 12; const MORPH_ROOT_SALT: &[u8] = b"nowhere/morph"; const TCP_C2S_INFO: &[u8] = b"tcp c2s"; const TCP_S2C_INFO: &[u8] = b"tcp s2c"; @@ -63,6 +70,13 @@ fn hkdf_expand_one(root: MorphKey, info: &[u8]) -> MorphKey { mac.finalize().into_bytes().into() } +fn exhausted() -> io::Error { + io::Error::other("Morph TCP keystream exhausted") +} + +#[cfg(test)] +use tcp::apply_at; + #[cfg(test)] #[path = "../tests/transport/morph.rs"] mod tests; diff --git a/src/transport/morph/tcp.rs b/src/transport/morph/tcp.rs new file mode 100644 index 0000000..3b6f2ac --- /dev/null +++ b/src/transport/morph/tcp.rs @@ -0,0 +1,311 @@ +// Copyright (C) 2026 NodePassProject +// SPDX-License-Identifier: GPL-3.0-only + +use std::fmt; +use std::io; +use std::pin::Pin; +use std::task::{Context, Poll, Waker}; + +use chacha20::ChaCha20; +use chacha20::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek}; +use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; + +use super::{MorphKey, MorphKeys, NONCE_LEN, exhausted}; + +const TCP_STREAM_LIMIT: u64 = (1u64 << 38) - 64; + +#[derive(Clone, Copy)] +enum TcpRole { + Client, + Server, +} + +pub(super) struct TcpMorph { + pub(super) read_key: MorphKey, + pub(super) write_key: MorphKey, + pub(super) read_cipher: Option, + pub(super) write_cipher: Option, + read_offset: u64, + write_offset: u64, + pub(super) prefix: [u8; NONCE_LEN], + read_prefix_pos: usize, + write_prefix_pos: usize, + write_waiter: Option, + write_buffer: Vec, +} + +pub(crate) struct MorphTcpStream { + inner: S, + keys: Option, + pub(super) morph: Option, +} + +impl fmt::Debug for MorphTcpStream +where + S: fmt::Debug, +{ + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("MorphTcpStream") + .field("inner", &self.inner) + .field("enabled", &self.keys.is_some()) + .finish() + } +} + +impl MorphTcpStream { + pub(crate) fn client(inner: S, keys: Option) -> io::Result { + let mut stream = Self { + inner, + keys, + morph: None, + }; + if stream.keys.is_some() { + let mut nonce = [0u8; NONCE_LEN]; + getrandom::fill(&mut nonce).map_err(io::Error::other)?; + stream.morph = Some(stream.new_state(nonce, TcpRole::Client)); + } + Ok(stream) + } + + pub(crate) fn server(inner: S, keys: Option) -> Self { + let mut stream = Self { + inner, + keys, + morph: None, + }; + if stream.keys.is_some() { + stream.morph = Some(stream.new_state([0; NONCE_LEN], TcpRole::Server)); + } + stream + } + + fn new_state(&self, nonce: [u8; NONCE_LEN], role: TcpRole) -> TcpMorph { + let keys = self.keys.as_ref().expect("Morph state requires keys"); + let (read_key, write_key) = match role { + TcpRole::Client => (&keys.tcp_s2c, &keys.tcp_c2s), + TcpRole::Server => (&keys.tcp_c2s, &keys.tcp_s2c), + }; + TcpMorph { + read_key: *read_key, + write_key: *write_key, + read_cipher: match role { + TcpRole::Client => Some(ChaCha20::new(read_key.into(), (&nonce).into())), + TcpRole::Server => None, + }, + write_cipher: match role { + TcpRole::Client => Some(ChaCha20::new(write_key.into(), (&nonce).into())), + TcpRole::Server => None, + }, + read_offset: 0, + write_offset: 0, + prefix: nonce, + read_prefix_pos: match role { + TcpRole::Client => NONCE_LEN, + TcpRole::Server => 0, + }, + write_prefix_pos: match role { + TcpRole::Client => 0, + TcpRole::Server => NONCE_LEN, + }, + write_waiter: None, + write_buffer: Vec::new(), + } + } + + pub(crate) fn get_ref(&self) -> &S { + &self.inner + } +} + +#[cfg(test)] +pub(super) fn apply_at( + key: &MorphKey, + nonce: &[u8; NONCE_LEN], + offset: u64, + bytes: &mut [u8], +) -> io::Result<()> { + let mut cipher = ChaCha20::new(key.into(), nonce.into()); + cipher.try_seek(offset).map_err(|_| exhausted())?; + cipher.try_apply_keystream(bytes).map_err(|_| exhausted()) +} + +impl AsyncRead for MorphTcpStream { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + if self.keys.is_none() { + return Pin::new(&mut self.inner).poll_read(cx, buf); + } + if self.morph.as_ref().unwrap().read_prefix_pos < NONCE_LEN { + while self.morph.as_ref().unwrap().read_prefix_pos < NONCE_LEN { + let filled = self.morph.as_ref().unwrap().read_prefix_pos; + let mut scratch = [0u8; NONCE_LEN]; + let mut nonce_buf = ReadBuf::new(&mut scratch[..NONCE_LEN - filled]); + match Pin::new(&mut self.inner).poll_read(cx, &mut nonce_buf) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Err(error)) => return Poll::Ready(Err(error)), + Poll::Ready(Ok(())) if nonce_buf.filled().is_empty() => { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "truncated Morph TCP nonce", + ))); + } + Poll::Ready(Ok(())) => { + let count = nonce_buf.filled().len(); + let state = self.morph.as_mut().unwrap(); + state.prefix[filled..filled + count].copy_from_slice(nonce_buf.filled()); + state.read_prefix_pos += count; + } + } + } + if let Some(waiter) = self.morph.as_mut().unwrap().write_waiter.take() { + waiter.wake(); + } + let state = self.morph.as_mut().unwrap(); + state.read_cipher = Some(ChaCha20::new( + (&state.read_key).into(), + (&state.prefix).into(), + )); + state.write_cipher = Some(ChaCha20::new( + (&state.write_key).into(), + (&state.prefix).into(), + )); + } + let remaining = TCP_STREAM_LIMIT.saturating_sub(self.morph.as_ref().unwrap().read_offset); + if remaining == 0 && buf.remaining() != 0 { + return Poll::Ready(Err(exhausted())); + } + let before = buf.filled().len(); + let allowed = usize::try_from(remaining.min(buf.remaining() as u64)).unwrap(); + let unfilled = buf.initialize_unfilled_to(allowed); + let mut inner_buf = ReadBuf::new(unfilled); + match Pin::new(&mut self.inner).poll_read(cx, &mut inner_buf) { + Poll::Ready(Ok(())) => { + let count = inner_buf.filled().len(); + let state = self.morph.as_mut().unwrap(); + state + .read_cipher + .as_mut() + .expect("Morph read cipher initialized") + .try_apply_keystream(&mut inner_buf.filled_mut()[..count]) + .map_err(|_| exhausted())?; + state.read_offset += count as u64; + buf.advance(count); + debug_assert_eq!(buf.filled().len(), before + count); + Poll::Ready(Ok(())) + } + other => other, + } + } +} + +impl AsyncWrite for MorphTcpStream { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + input: &[u8], + ) -> Poll> { + if self.keys.is_none() { + return Pin::new(&mut self.inner).poll_write(cx, input); + } + let this = self.as_mut().get_mut(); + if this + .morph + .as_ref() + .expect("Morph state initialized") + .read_prefix_pos + < NONCE_LEN + { + this.morph.as_mut().unwrap().write_waiter = Some(cx.waker().clone()); + return Poll::Pending; + } + let prefix_pos = this + .morph + .as_ref() + .expect("Morph state initialized") + .write_prefix_pos; + if prefix_pos < NONCE_LEN { + let prefix = this.morph.as_ref().unwrap().prefix; + match Pin::new(&mut this.inner).poll_write(cx, &prefix[prefix_pos..]) { + Poll::Ready(Ok(0)) => return Poll::Ready(Err(io::ErrorKind::WriteZero.into())), + Poll::Ready(Ok(n)) => this.morph.as_mut().unwrap().write_prefix_pos += n, + Poll::Ready(Err(error)) => return Poll::Ready(Err(error)), + Poll::Pending => return Poll::Pending, + } + if this.morph.as_ref().unwrap().write_prefix_pos < NONCE_LEN { + cx.waker().wake_by_ref(); + return Poll::Pending; + } + } + if input.is_empty() { + return Poll::Ready(Ok(0)); + } + let state = this.morph.as_mut().unwrap(); + let remaining = TCP_STREAM_LIMIT.saturating_sub(state.write_offset); + if remaining == 0 { + return Poll::Ready(Err(exhausted())); + } + let count = usize::try_from(remaining.min(input.len() as u64)) + .expect("count is bounded by usize input length"); + state.write_buffer.clear(); + state.write_buffer.extend_from_slice(&input[..count]); + state + .write_cipher + .as_mut() + .expect("Morph write cipher initialized") + .try_apply_keystream(&mut state.write_buffer) + .map_err(|_| exhausted())?; + match Pin::new(&mut this.inner).poll_write(cx, &state.write_buffer) { + Poll::Ready(Ok(0)) => { + state + .write_cipher + .as_mut() + .unwrap() + .try_seek(state.write_offset) + .map_err(|_| exhausted())?; + Poll::Ready(Err(io::ErrorKind::WriteZero.into())) + } + Poll::Ready(Ok(n)) => { + state.write_offset += n as u64; + if n != count { + state + .write_cipher + .as_mut() + .expect("Morph write cipher initialized") + .try_seek(state.write_offset) + .map_err(|_| exhausted())?; + } + Poll::Ready(Ok(n)) + } + Poll::Ready(Err(error)) => { + state + .write_cipher + .as_mut() + .unwrap() + .try_seek(state.write_offset) + .map_err(|_| exhausted())?; + Poll::Ready(Err(error)) + } + Poll::Pending => { + state + .write_cipher + .as_mut() + .unwrap() + .try_seek(state.write_offset) + .map_err(|_| exhausted())?; + Poll::Pending + } + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_flush(cx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_shutdown(cx) + } +} From 7dc448debc0e47938e14d5719eefd08dde319c94 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Wed, 9 Sep 2026 23:28:23 +0800 Subject: [PATCH 35/62] Add Morph UDP socket transform --- src/tests/transport/morph.rs | 2 + src/tests/transport/morph/udp.rs | 192 +++++++++++++++++++++++++++ src/transport/mod.rs | 4 +- src/transport/morph.rs | 4 + src/transport/morph/udp.rs | 221 +++++++++++++++++++++++++++++++ 5 files changed, 422 insertions(+), 1 deletion(-) create mode 100644 src/tests/transport/morph/udp.rs create mode 100644 src/transport/morph/udp.rs diff --git a/src/tests/transport/morph.rs b/src/tests/transport/morph.rs index 0e7737c..5e6fb6b 100644 --- a/src/tests/transport/morph.rs +++ b/src/tests/transport/morph.rs @@ -5,3 +5,5 @@ mod keys; #[path = "morph/tcp.rs"] mod tcp; +#[path = "morph/udp.rs"] +mod udp; diff --git a/src/tests/transport/morph/udp.rs b/src/tests/transport/morph/udp.rs new file mode 100644 index 0000000..93c2ab3 --- /dev/null +++ b/src/tests/transport/morph/udp.rs @@ -0,0 +1,192 @@ +// Copyright (C) 2026 NodePassProject +// SPDX-License-Identifier: GPL-3.0-only + +use std::collections::VecDeque; +use std::future::poll_fn; +use std::io::{self, IoSliceMut}; +use std::pin::Pin; +use std::sync::{Arc, Mutex, Mutex as StdMutex}; +use std::task::{Context, Poll}; + +use quinn::udp::{RecvMeta, Transmit}; +use quinn::{AsyncUdpSocket, UdpPoller}; + +use super::super::*; + +#[test] +fn udp_nonce_generator_fills_batches_from_one_stream() { + let mut buffers = UdpBuffers::from_seed([3; 32]); + let mut first = [0; NONCE_LEN]; + let mut second = [0; NONCE_LEN]; + generate_nonce(&mut buffers.nonce_cipher, &mut first).unwrap(); + generate_nonce(&mut buffers.nonce_cipher, &mut second).unwrap(); + + assert_ne!(first, [0; NONCE_LEN]); + assert_ne!(second, first); +} + +#[test] +fn morph_endpoint_reserves_the_udp_nonce_overhead() { + let plain = morph_endpoint_config(false).unwrap(); + let morph = morph_endpoint_config(true).unwrap(); + + assert_eq!( + morph.get_max_udp_payload_size() + NONCE_LEN as u64, + plain.get_max_udp_payload_size() + ); +} + +#[derive(Debug, Default)] +struct FakeUdpSocket { + sent: StdMutex, Option)>>, + receive: StdMutex, RecvMeta)>>, +} + +#[derive(Debug)] +struct ReadyPoller; + +impl std::future::Future for ReadyPoller { + type Output = io::Result<()>; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Ready(Ok(())) + } +} + +impl UdpPoller for ReadyPoller { + fn poll_writable(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +impl AsyncUdpSocket for FakeUdpSocket { + fn create_io_poller(self: Arc) -> Pin> { + Box::pin(ReadyPoller) + } + + fn try_send(&self, transmit: &Transmit<'_>) -> io::Result<()> { + self.sent + .lock() + .unwrap() + .push((transmit.contents.to_vec(), transmit.segment_size)); + Ok(()) + } + + fn poll_recv( + &self, + _cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + meta: &mut [RecvMeta], + ) -> Poll> { + let Some((packet, packet_meta)) = self.receive.lock().unwrap().pop_front() else { + return Poll::Pending; + }; + bufs[0][..packet.len()].copy_from_slice(&packet); + meta[0] = packet_meta; + Poll::Ready(Ok(1)) + } + + fn local_addr(&self) -> io::Result { + Ok("127.0.0.1:1".parse().unwrap()) + } + + fn max_transmit_segments(&self) -> usize { + 2 + } + + fn max_receive_segments(&self) -> usize { + 2 + } +} + +#[tokio::test] +async fn udp_preserves_gso_datagram_boundaries() { + let key = MorphKeys::derive(b"shared").udp_key(); + let raw = Arc::new(FakeUdpSocket::default()); + let socket = MorphUdpSocket { + inner: raw.clone(), + key, + buffers: Mutex::new(UdpBuffers::from_seed([1; 32])), + }; + socket + .try_send(&Transmit { + destination: "127.0.0.1:2".parse().unwrap(), + ecn: None, + contents: b"abcdef", + segment_size: Some(3), + src_ip: None, + }) + .unwrap(); + let (wire, stride) = raw.sent.lock().unwrap().pop().unwrap(); + assert_eq!(stride, Some(15)); + assert_eq!(wire.len(), 30); + + raw.receive.lock().unwrap().push_back(( + wire, + RecvMeta { + addr: "127.0.0.1:2".parse().unwrap(), + len: 30, + stride: 15, + ecn: None, + dst_ip: None, + }, + )); + let mut output = [0u8; 6]; + let mut bufs = [IoSliceMut::new(&mut output)]; + let mut meta = [RecvMeta::default()]; + let count = poll_fn(|cx| socket.poll_recv(cx, &mut bufs, &mut meta)) + .await + .unwrap(); + assert_eq!(count, 1); + assert_eq!(&output, b"abcdef"); + assert_eq!(meta[0].len, 6); + assert_eq!(meta[0].stride, 3); +} + +#[tokio::test] +async fn udp_discards_short_wire_datagrams_before_returning_valid_data() { + let key = MorphKeys::derive(b"shared").udp_key(); + let raw = Arc::new(FakeUdpSocket::default()); + let socket = MorphUdpSocket { + inner: raw.clone(), + key, + buffers: Mutex::new(UdpBuffers::from_seed([2; 32])), + }; + let packet_meta = |len| RecvMeta { + addr: "127.0.0.1:2".parse().unwrap(), + len, + stride: len, + ecn: None, + dst_ip: None, + }; + raw.receive + .lock() + .unwrap() + .push_back((vec![0; NONCE_LEN], packet_meta(NONCE_LEN))); + + socket + .try_send(&Transmit { + destination: "127.0.0.1:2".parse().unwrap(), + ecn: None, + contents: b"valid", + segment_size: None, + src_ip: None, + }) + .unwrap(); + let (wire, _) = raw.sent.lock().unwrap().pop().unwrap(); + let wire_len = wire.len(); + raw.receive + .lock() + .unwrap() + .push_back((wire, packet_meta(wire_len))); + + let mut output = [0u8; 5]; + let mut bufs = [IoSliceMut::new(&mut output)]; + let mut meta = [RecvMeta::default()]; + let count = poll_fn(|cx| socket.poll_recv(cx, &mut bufs, &mut meta)) + .await + .unwrap(); + assert_eq!(count, 1); + assert_eq!(&output, b"valid"); + assert_eq!(meta[0].len, 5); +} diff --git a/src/transport/mod.rs b/src/transport/mod.rs index f22d58b..ece74ce 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -11,7 +11,9 @@ mod rate; mod stats; pub use buffers::{BufferLease, Buffers}; -pub(crate) use morph::{MorphKeys, MorphTcpStream}; +pub(crate) use morph::{ + MorphKeys, MorphTcpStream, configure_morph_mtu, morph_endpoint_config, wrap_morph_udp_socket, +}; pub(crate) use owned_io::{ AsyncReadAny, AsyncWriteAny, read_owned, read_owned_from, write_owned, write_owned_to, }; diff --git a/src/transport/morph.rs b/src/transport/morph.rs index fb883f3..eac09e8 100644 --- a/src/transport/morph.rs +++ b/src/transport/morph.rs @@ -10,8 +10,10 @@ use hmac::{Hmac, KeyInit as HmacKeyInit, Mac}; use sha2::Sha256; mod tcp; +mod udp; pub(crate) use tcp::MorphTcpStream; +pub(crate) use udp::{configure_morph_mtu, morph_endpoint_config, wrap_morph_udp_socket}; const NONCE_LEN: usize = 12; const MORPH_ROOT_SALT: &[u8] = b"nowhere/morph"; @@ -76,6 +78,8 @@ fn exhausted() -> io::Error { #[cfg(test)] use tcp::apply_at; +#[cfg(test)] +use udp::{MorphUdpSocket, UdpBuffers, generate_nonce}; #[cfg(test)] #[path = "../tests/transport/morph.rs"] diff --git a/src/transport/morph/udp.rs b/src/transport/morph/udp.rs new file mode 100644 index 0000000..05d498f --- /dev/null +++ b/src/transport/morph/udp.rs @@ -0,0 +1,221 @@ +// Copyright (C) 2026 NodePassProject +// SPDX-License-Identifier: GPL-3.0-only + +use std::fmt; +use std::io::{self, IoSliceMut}; +use std::pin::Pin; +use std::sync::{Arc, Mutex}; +use std::task::{Context, Poll}; + +use chacha20::ChaCha20; +use chacha20::cipher::{KeyIvInit, StreamCipher}; +use quinn::udp::{RecvMeta, Transmit}; +use quinn::{AsyncUdpSocket, UdpPoller}; + +use super::{MorphKey, NONCE_LEN, exhausted}; + +const QUINN_DEFAULT_MTU_UPPER_BOUND: u16 = 1452; +const MAX_INVALID_RECEIVE_BATCHES: usize = 32; + +pub(crate) fn wrap_morph_udp_socket( + inner: Arc, + key: Option, +) -> io::Result> { + match key { + Some(key) => Ok(Arc::new(MorphUdpSocket { + inner, + key, + buffers: Mutex::new(UdpBuffers::new()?), + })), + None => Ok(inner), + } +} + +pub(super) struct UdpBuffers { + send: Vec, + receive: [Vec; quinn::udp::BATCH_SIZE], + pub(super) nonce_cipher: ChaCha20, +} + +impl UdpBuffers { + fn new() -> io::Result { + let mut seed = [0u8; 32]; + getrandom::fill(&mut seed).map_err(io::Error::other)?; + Ok(Self::from_seed(seed)) + } + + pub(super) fn from_seed(seed: [u8; 32]) -> Self { + Self { + send: Vec::new(), + receive: std::array::from_fn(|_| Vec::new()), + nonce_cipher: ChaCha20::new((&seed).into(), (&[0u8; NONCE_LEN]).into()), + } + } +} + +pub(super) fn generate_nonce(cipher: &mut ChaCha20, nonce: &mut [u8; NONCE_LEN]) -> io::Result<()> { + nonce.fill(0); + cipher + .try_apply_keystream(nonce) + .map_err(|_| io::Error::other("Morph UDP nonce generator exhausted")) +} + +pub(crate) fn morph_endpoint_config(enabled: bool) -> anyhow::Result { + let mut config = quinn::EndpointConfig::default(); + if enabled { + let maximum = config + .get_max_udp_payload_size() + .checked_sub(NONCE_LEN as u64) + .expect("Quinn UDP payload limit exceeds Morph overhead"); + config.max_udp_payload_size(maximum as u16)?; + } + Ok(config) +} + +pub(crate) fn configure_morph_mtu(transport: &mut quinn::TransportConfig, enabled: bool) { + if enabled { + let mut discovery = quinn::MtuDiscoveryConfig::default(); + discovery.upper_bound(QUINN_DEFAULT_MTU_UPPER_BOUND - NONCE_LEN as u16); + transport.mtu_discovery_config(Some(discovery)); + } +} + +pub(super) struct MorphUdpSocket { + pub(super) inner: Arc, + pub(super) key: MorphKey, + pub(super) buffers: Mutex, +} + +impl fmt::Debug for MorphUdpSocket { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("MorphUdpSocket") + .field("inner", &self.inner) + .finish_non_exhaustive() + } +} + +impl AsyncUdpSocket for MorphUdpSocket { + fn create_io_poller(self: Arc) -> Pin> { + self.inner.clone().create_io_poller() + } + + fn try_send(&self, transmit: &Transmit<'_>) -> io::Result<()> { + let plain_stride = transmit.segment_size.unwrap_or(transmit.contents.len()); + if plain_stride == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "empty UDP datagram", + )); + } + let segments = transmit.contents.len().div_ceil(plain_stride); + let mut buffers = self.buffers.lock().unwrap_or_else(|lock| lock.into_inner()); + let UdpBuffers { + send, nonce_cipher, .. + } = &mut *buffers; + send.clear(); + send.reserve(transmit.contents.len() + segments * NONCE_LEN); + for plain in transmit.contents.chunks(plain_stride) { + let start = send.len(); + send.resize(start + NONCE_LEN + plain.len(), 0); + let (nonce, payload) = send[start..].split_at_mut(NONCE_LEN); + let nonce: &mut [u8; NONCE_LEN] = nonce.try_into().expect("fixed nonce prefix"); + generate_nonce(nonce_cipher, nonce)?; + payload.copy_from_slice(plain); + let mut cipher = ChaCha20::new((&self.key).into(), (&*nonce).into()); + cipher + .try_apply_keystream(payload) + .map_err(|_| exhausted())?; + } + let wire_stride = transmit.segment_size.map(|size| size + NONCE_LEN); + let wire = Transmit { + destination: transmit.destination, + ecn: transmit.ecn, + contents: send, + segment_size: wire_stride, + src_ip: transmit.src_ip, + }; + self.inner.try_send(&wire) + } + + fn poll_recv( + &self, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + meta: &mut [RecvMeta], + ) -> Poll> { + let max_segments = self.inner.max_receive_segments().max(1); + let mut buffers = self.buffers.lock().unwrap_or_else(|lock| lock.into_inner()); + for (storage, target) in buffers.receive.iter_mut().zip(bufs.iter()) { + storage.resize(target.len() + NONCE_LEN * max_segments, 0); + } + for _ in 0..MAX_INVALID_RECEIVE_BATCHES { + let receive = buffers.receive.each_mut(); + let mut wire_bufs = receive.map(|value| IoSliceMut::new(value)); + let received = match self.inner.poll_recv(cx, &mut wire_bufs[..bufs.len()], meta) { + Poll::Ready(Ok(received)) => received, + Poll::Ready(Err(error)) => return Poll::Ready(Err(error)), + Poll::Pending => return Poll::Pending, + }; + let mut output = 0; + for index in 0..received { + let wire_meta = meta[index]; + let wire_stride = wire_meta.stride.max(1); + let storage = &mut buffers.receive[index]; + let target = &mut bufs[output]; + let decoded_stride = wire_stride.saturating_sub(NONCE_LEN); + let mut decoded_len = 0; + for wire in storage[..wire_meta.len].chunks_mut(wire_stride) { + if wire.len() <= NONCE_LEN { + continue; + } + let (nonce, encrypted) = wire.split_at_mut(NONCE_LEN); + let nonce: &[u8; NONCE_LEN] = (&*nonce).try_into().expect("fixed nonce prefix"); + let mut cipher = ChaCha20::new((&self.key).into(), nonce.into()); + cipher + .try_apply_keystream(encrypted) + .map_err(|_| exhausted())?; + let end = decoded_len + encrypted.len(); + if end > target.len() { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::InvalidData, + "Morph UDP receive buffer overflow", + ))); + } + target[decoded_len..end].copy_from_slice(encrypted); + decoded_len = end; + } + if decoded_len == 0 { + continue; + } + meta[output] = RecvMeta { + len: decoded_len, + stride: decoded_stride, + ..wire_meta + }; + output += 1; + } + if output != 0 { + return Poll::Ready(Ok(output)); + } + } + cx.waker().wake_by_ref(); + Poll::Pending + } + + fn local_addr(&self) -> io::Result { + self.inner.local_addr() + } + + fn max_transmit_segments(&self) -> usize { + self.inner.max_transmit_segments() + } + + fn max_receive_segments(&self) -> usize { + self.inner.max_receive_segments() + } + + fn may_fragment(&self) -> bool { + self.inner.may_fragment() + } +} From 664eec526afb008e37bd200772edf953e4c8dfb4 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Wed, 9 Sep 2026 23:28:39 +0800 Subject: [PATCH 36/62] Wire Morph into Portal and Vector --- src/portal/conn/tcp/mod.rs | 7 +++-- src/portal/listener.rs | 24 +++++++++++++---- src/portal/mod.rs | 3 ++- src/portal/runtime.rs | 16 ++++++++--- src/portal/setup.rs | 23 +++++++++++++--- src/tests/portal.rs | 34 ++++++++++++++++++----- src/tests/portal/conn/vector.rs | 48 +++++++++++++++++++++++++++++++-- src/tests/vector/config.rs | 38 +++++++++++++++++++++++++- src/vector/config.rs | 25 ++++++++++++++--- src/vector/mod.rs | 3 ++- src/vector/session.rs | 7 ++--- src/vector/session/quic.rs | 25 ++++++++++++++++- src/vector/session/tls.rs | 2 +- src/vector/tls.rs | 6 ++++- 14 files changed, 228 insertions(+), 33 deletions(-) diff --git a/src/portal/conn/tcp/mod.rs b/src/portal/conn/tcp/mod.rs index f386275..05d5b73 100644 --- a/src/portal/conn/tcp/mod.rs +++ b/src/portal/conn/tcp/mod.rs @@ -26,6 +26,7 @@ use crate::portal::admission::UnauthenticatedGuard; use crate::portal::pairing::SessionKey; use crate::protocol::{ALPN, AuthTransport, read_auth_frame}; use crate::telemetry::{RuntimeEvent, RuntimeKind, RuntimeLevel}; +use crate::transport::MorphTcpStream; use self::flow::process_flow; use super::auth::{authentication_deadline, wait_for_auth_deadline}; @@ -91,11 +92,13 @@ pub(super) async fn handle_tcp_incoming_with_timeouts( } let local = stream.local_addr().ok(); let server_config = portal.tls_server_config.clone(); + let morph_keys = portal.morph_keys.clone(); let tls_stream = match tokio::select! { biased; _ = shutdown.cancelled() => return, _ = portal.drain.cancelled() => return, result = timeout(portal.runtime.handshake_timeout, async move { + let stream = MorphTcpStream::server(stream, morph_keys); let start = LazyConfigAcceptor::new(rustls::server::Acceptor::default(), stream).await?; let offers_nw2 = start .client_hello() @@ -174,7 +177,7 @@ pub(super) async fn handle_tcp_incoming_with_timeouts( Err(_) => return, }; let session_key = session_id; - if let Err(err) = SockRef::from(tls_stream.get_ref().0).set_keepalive(true) { + if let Err(err) = SockRef::from(tls_stream.get_ref().0.get_ref()).set_keepalive(true) { portal.logger.debug(format_args!( "portal::conn::tcp: TCP keepalive failed: {err}" )); @@ -225,7 +228,7 @@ pub(super) async fn handle_tcp_incoming_with_timeouts( async fn handle_mux( portal: Arc, - tls_stream: tokio_rustls::server::TlsStream, + tls_stream: tokio_rustls::server::TlsStream>, session_key: SessionKey, peer: SocketAddr, local: Option, diff --git a/src/portal/listener.rs b/src/portal/listener.rs index c28d4e9..9501695 100644 --- a/src/portal/listener.rs +++ b/src/portal/listener.rs @@ -7,14 +7,17 @@ use std::net::SocketAddr; use std::sync::Arc; use anyhow::{Context, Result}; -use quinn::{Endpoint, EndpointConfig, IdleTimeout, ServerConfig, VarInt, default_runtime}; +use quinn::{Endpoint, IdleTimeout, ServerConfig, VarInt, default_runtime}; use socket2::{Domain, Protocol, Socket, Type}; use tokio::net::TcpListener; use tokio::time::{Duration, sleep}; use tokio_util::sync::CancellationToken; use crate::telemetry::{RuntimeEvent, RuntimeKind, RuntimeLevel}; -use crate::transport::transport_flow_control; +use crate::transport::{ + MorphKeys, configure_morph_mtu, morph_endpoint_config, transport_flow_control, + wrap_morph_udp_socket, +}; use super::{PortalInner, conn}; @@ -132,13 +135,22 @@ pub(super) async fn accept_tcp_loop( } /// Opens a Quinn endpoint on an already configured server config. -pub(super) fn listen_endpoint(server_config: ServerConfig, addr: SocketAddr) -> Result { +pub(super) fn listen_endpoint( + server_config: ServerConfig, + addr: SocketAddr, + morph_keys: Option, +) -> Result { let socket = bind_quic_socket(addr) .with_context(|| format!("portal::listen_endpoint: failed to bind UDP socket: {addr}"))?; let runtime = default_runtime() .ok_or_else(|| anyhow::anyhow!("portal::listen_endpoint: no async runtime found"))?; - Endpoint::new( - EndpointConfig::default(), + let socket = runtime + .wrap_udp_socket(socket) + .context("portal::listen_endpoint: failed to initialize UDP runtime socket")?; + let morph_enabled = morph_keys.is_some(); + let socket = wrap_morph_udp_socket(socket, morph_keys.map(|keys| keys.udp_key()))?; + Endpoint::new_with_abstract_socket( + morph_endpoint_config(morph_enabled)?, Some(server_config), socket, runtime, @@ -183,6 +195,7 @@ pub(super) fn configure_transport( server_config: &mut quinn::ServerConfig, udp_idle_timeout: Duration, keep_alive_interval: Option, + morph_enabled: bool, ) -> Result<()> { let flow_control = transport_flow_control()?; let transport = Arc::get_mut(&mut server_config.transport).ok_or_else(|| { @@ -198,6 +211,7 @@ pub(super) fn configure_transport( transport.max_idle_timeout(Some(IdleTimeout::try_from(udp_idle_timeout)?)); transport.keep_alive_interval(keep_alive_interval); transport.congestion_controller_factory(Arc::new(quinn::congestion::BbrConfig::default())); + configure_morph_mtu(transport, morph_enabled); Ok(()) } diff --git a/src/portal/mod.rs b/src/portal/mod.rs index b32a843..4e8ffbf 100644 --- a/src/portal/mod.rs +++ b/src/portal/mod.rs @@ -22,7 +22,7 @@ use tokio_util::sync::CancellationToken; use crate::common::{Lifecycle, Logger, TLSMode}; use crate::protocol::Credentials; use crate::telemetry::TelemetryHub; -use crate::transport::{Buffers, RateLimiter, Stats}; +use crate::transport::{Buffers, MorphKeys, RateLimiter, Stats}; use self::config::PortalRuntimeConfig; pub(crate) use self::mode::NetworkMode; @@ -43,6 +43,7 @@ pub struct Portal { struct PortalInner { credentials: Credentials, + morph_keys: Option, tls_mode: TLSMode, network_mode: NetworkMode, endpoint_addr: String, diff --git a/src/portal/runtime.rs b/src/portal/runtime.rs index d12b22c..fab2570 100644 --- a/src/portal/runtime.rs +++ b/src/portal/runtime.rs @@ -332,12 +332,13 @@ impl Portal { /// Returns the effective startup URL that is logged for operators. pub(super) fn effective_url(&self) -> String { let base = format!( - "portal://{}?tls={}&rate={}&etar={}&dial={}&socks={}&next={}", + "portal://{}?tls={}&rate={}&etar={}&dial={}&morph={}&socks={}&next={}", self.inner.endpoint_addr, self.inner.tls_mode, self.inner.rate_limit, self.inner.etar_limit, self.inner.outbound.dialer_ip(), + u8::from(self.inner.morph_keys.is_some()), self.inner.outbound.socks_endpoint(), self.inner.outbound.next_endpoint(), ); @@ -345,7 +346,12 @@ impl Portal { .outbound .next_transport() .map_or(base.clone(), |transport| { - format!("{base}&{}", transport.replace(' ', "&")) + let upstream = transport + .split_whitespace() + .filter(|option| !option.starts_with("morph=")) + .collect::>() + .join("&"); + format!("{base}&{upstream}") }) } @@ -357,7 +363,11 @@ impl Portal { bind_carrier( &self.inner.udp_bind_addrs, self.inner.allow_udp_family_degrade, - |addr| listen_endpoint(self.inner.quic_server_config.clone(), addr), + |addr| listen_endpoint( + self.inner.quic_server_config.clone(), + addr, + self.inner.morph_keys.clone(), + ), |addr, error| self.inner.logger.warn(format_args!( "portal::listen_endpoints: UDP address family unavailable for {addr}; continuing: {error:#}" )), diff --git a/src/portal/setup.rs b/src/portal/setup.rs index 557dc69..1ad9366 100644 --- a/src/portal/setup.rs +++ b/src/portal/setup.rs @@ -17,6 +17,7 @@ use crate::common::{ }; use crate::protocol::Credentials; use crate::telemetry::{InstanceRole, TelemetryHub}; +use crate::transport::MorphKeys; use crate::transport::{Buffers, RateLimiter, Stats}; use crate::vector::{PortalClient, PortalClientConfig}; @@ -24,7 +25,7 @@ use super::listener::configure_transport; use super::{NetworkMode, Portal, PortalInner, UdpFlowLimits, admission, outbound::PortalOutbound}; const PORTAL_QUERY_PARAMETERS: &[&str] = &[ - "tls", "crt", "key", "rate", "etar", "dial", "socks", "next", "log", + "tls", "crt", "key", "rate", "etar", "dial", "morph", "socks", "next", "log", ]; const PORTAL_UPSTREAM_PARAMETERS: &[&str] = &["up", "down", "mux", "sni", "pin"]; @@ -79,6 +80,10 @@ impl Portal { service_endpoint.host = "*".to_owned(); } let credentials = Credentials::new(&parsed_url)?; + let morph = query.get("morph").is_some_and(|value| value == "1"); + let morph_keys = morph + .then(|| MorphKeys::from_url(&parsed_url)) + .transpose()?; let runtime = super::config::PortalRuntimeConfig::from_env() .map_err(|e| anyhow::anyhow!("Portal configuration: invalid runtime setting: {e}"))?; let network_mode = @@ -130,7 +135,12 @@ impl Portal { .map_err(|e| anyhow::anyhow!("Portal endpoint: failed to resolve UDP address: {e}"))? .unwrap_or_default(); - configure_transport(&mut quic_server_config, runtime.udp_idle_timeout, None)?; + configure_transport( + &mut quic_server_config, + runtime.udp_idle_timeout, + None, + morph_keys.is_some(), + )?; let read_bps = rate_limit_bytes_per_second(rate_limit) as i64; let write_bps = rate_limit_bytes_per_second(etar_limit) as i64; @@ -147,7 +157,8 @@ impl Portal { |(config, _)| format!("next={} {}", config.endpoint(), config.effective_route()), ); let telemetry_summary = format!( - "listen={endpoint_addr} tls={tls_mode} rate={rate_limit} etar={etar_limit} dial={dialer_ip} socks={socks_endpoint} {next_summary}", + "listen={endpoint_addr} tls={tls_mode} rate={rate_limit} etar={etar_limit} dial={dialer_ip} morph={} socks={socks_endpoint} {next_summary}", + u8::from(morph), ); let telemetry = TelemetryHub::for_current_process( InstanceRole::Portal, @@ -170,6 +181,7 @@ impl Portal { Ok(Self { inner: Arc::new(PortalInner { credentials, + morph_keys, tls_mode, network_mode, endpoint_addr, @@ -229,6 +241,11 @@ fn validate_query(query: &std::collections::HashMap) -> Result<( { anyhow::bail!("tls=1 or tls=2 required"); } + if let Some(morph) = query.get("morph") + && !matches!(morph.as_str(), "0" | "1") + { + anyhow::bail!("morph must be 0 or 1"); + } let tls_is_ca = query.get("tls").is_some_and(|value| value == "2"); let has_crt = query.contains_key("crt"); let has_key = query.contains_key("key"); diff --git a/src/tests/portal.rs b/src/tests/portal.rs index c742d0b..f2d7db0 100644 --- a/src/tests/portal.rs +++ b/src/tests/portal.rs @@ -34,7 +34,7 @@ fn empty_host_listens_on_both_wildcard_families() { assert_eq!(portal.inner.network_mode, NetworkMode::Mix); assert_eq!( portal.effective_url(), - "portal://*:2000?tls=1&rate=0&etar=0&dial=127.0.0.1&socks=none&next=none" + "portal://*:2000?tls=1&rate=0&etar=0&dial=127.0.0.1&morph=0&socks=none&next=none" ); } @@ -154,7 +154,7 @@ fn native_next_defaults_to_tcp_without_mux_and_redacts_the_shared_key() { assert_eq!(portal.inner.outbound.next_endpoint(), "relay.example:2080"); assert_eq!( portal.inner.outbound.next_transport().as_deref(), - Some("up=tcp down=tcp mux=0 sni=none pin=none") + Some("up=tcp down=tcp mux=0 sni=none pin=none morph=0") ); let effective = portal.effective_url(); assert!(effective.contains("next=relay.example:2080")); @@ -180,7 +180,9 @@ fn native_next_omitted_directions_keep_independent_tcp_defaults_and_explicit_mux assert_eq!(portal.inner.network_mode, NetworkMode::Udp); assert_eq!( portal.inner.outbound.next_transport().as_deref(), - Some(format!("up={up} down={down} mux={mux} sni=none pin=none").as_str()), + Some( + format!("up={up} down={down} mux={mux} sni=none pin=none morph=0").as_str() + ), "{raw}" ); assert!( @@ -210,7 +212,7 @@ fn native_next_uses_shared_endpoint_grammar_and_single_carrier_defaults() { ); assert_eq!( portal.inner.outbound.next_transport().as_deref(), - Some("up=udp down=udp mux=0 sni=none pin=none") + Some("up=udp down=udp mux=0 sni=none pin=none morph=0") ); } @@ -228,7 +230,7 @@ fn native_next_reuses_transport_identity_and_source_binding() { assert_eq!(portal.inner.outbound.next_endpoint(), "[::1]:2080"); assert_eq!( portal.inner.outbound.next_transport().as_deref(), - Some("up=tcp down=tcp mux=1 sni=origin.example pin=abc") + Some("up=tcp down=tcp mux=1 sni=origin.example pin=abc morph=0") ); assert!( portal @@ -315,7 +317,7 @@ fn native_next_accepts_mix_and_normalizes_pure_udp_mux() { .unwrap(); assert_eq!( portal.inner.outbound.next_transport().as_deref(), - Some(format!("up={up} down={down} mux={mux} sni=none pin=none").as_str()), + Some(format!("up={up} down={down} mux={mux} sni=none pin=none morph=0").as_str()), ); assert!( portal @@ -406,6 +408,8 @@ fn portal_url_contract_rejects_invalid_structure_and_selected_values() { "portal://secret@127.0.0.1:2000?socks=", "portal://secret@127.0.0.1:2000?rate=-1", "portal://secret@127.0.0.1:2000?dial=not-an-ip", + "portal://secret@127.0.0.1:2000?morph=", + "portal://secret@127.0.0.1:2000?morph=2", "portal://secret@127.0.0.1:0", "portal://secret@127.0.0.1", ] { @@ -416,6 +420,24 @@ fn portal_url_contract_rejects_invalid_structure_and_selected_values() { } } +#[test] +fn outer_morph_controls_local_and_next_carriers() { + let portal = Portal::new( + Url::parse( + "portal://local-key@127.0.0.1:2000?morph=1&next=upstream-key@origin.example:2080", + ) + .unwrap(), + test_logger(), + ) + .unwrap(); + assert!(portal.inner.morph_keys.is_some()); + assert_eq!( + portal.inner.outbound.next_transport().as_deref(), + Some("up=tcp down=tcp mux=0 sni=none pin=none morph=1") + ); + assert!(portal.effective_url().contains("&morph=1&")); +} + #[test] fn portal_ignores_unknown_parameters_and_keeps_first_duplicate() { let portal = Portal::new( diff --git a/src/tests/portal/conn/vector.rs b/src/tests/portal/conn/vector.rs index bb23cbb..d01d7e4 100644 --- a/src/tests/portal/conn/vector.rs +++ b/src/tests/portal/conn/vector.rs @@ -119,11 +119,16 @@ async fn reserve_udp_port_except(excluded: u16) -> (u16, UdpSocket) { } async fn start_runtime(up: &str, down: &str, mux: u8) -> TestRuntime { + start_runtime_with_morph(up, down, mux, false).await +} + +async fn start_runtime_with_morph(up: &str, down: &str, mux: u8, morph: bool) -> TestRuntime { let (tcp_port, tcp_reservation) = reserve_tcp_port().await; let (udp_port, udp_reservation) = reserve_udp_port_except(tcp_port).await; let portal = Portal::new( Url::parse(&format!( - "portal://secret@127.0.0.1/tcp:{tcp_port}/udp:{udp_port}?log=none" + "portal://secret@127.0.0.1/tcp:{tcp_port}/udp:{udp_port}?log=none&morph={}", + u8::from(morph) )) .unwrap(), Logger::new(LogLevel::None, false), @@ -150,7 +155,8 @@ async fn start_runtime(up: &str, down: &str, mux: u8) -> TestRuntime { let (socks_port, socks_reservation) = reserve_tcp_port().await; let vector = Vector::new( Url::parse(&format!( - "vector://secret@127.0.0.1/tcp:{tcp_port}/udp:{udp_port}?log=none&up={up}&down={down}&mux={mux}&socks=127.0.0.1:{socks_port}" + "vector://secret@127.0.0.1/tcp:{tcp_port}/udp:{udp_port}?log=none&up={up}&down={down}&mux={mux}&morph={}&socks=127.0.0.1:{socks_port}", + u8::from(morph) )) .unwrap(), Logger::new(LogLevel::None, false), @@ -170,6 +176,44 @@ async fn start_runtime(up: &str, down: &str, mux: u8) -> TestRuntime { } } +#[tokio::test] +async fn morph_relays_every_fixed_tcp_udp_route() { + for (up, down) in [ + ("tcp", "tcp"), + ("tcp", "udp"), + ("udp", "tcp"), + ("udp", "udp"), + ] { + let target = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let target_address = target.local_addr().unwrap(); + let echo = tokio::spawn(async move { + let (mut stream, _) = target.accept().await.unwrap(); + let mut request = [0u8; 5]; + stream.read_exact(&mut request).await.unwrap(); + assert_eq!(&request, b"morph"); + stream.write_all(b"works").await.unwrap(); + }); + let runtime = start_runtime_with_morph(up, down, 0, true).await; + timeout(TEST_TIMEOUT, async { + let mut socks = TcpStream::connect(runtime.socks).await.unwrap(); + negotiate_socks(&mut socks).await; + socks + .write_all(&ip_request(1, target_address)) + .await + .unwrap(); + read_ipv4_reply(&mut socks).await; + socks.write_all(b"morph").await.unwrap(); + let mut response = [0u8; 5]; + socks.read_exact(&mut response).await.unwrap(); + assert_eq!(&response, b"works", "up={up} down={down}"); + }) + .await + .unwrap(); + echo.await.unwrap(); + runtime.stop().await; + } +} + #[tokio::test] async fn mux_symmetric_carriers_relay_tcp_and_fragmented_udp() { for carrier in ["tcp", "udp"] { diff --git a/src/tests/vector/config.rs b/src/tests/vector/config.rs index 138682a..791601d 100644 --- a/src/tests/vector/config.rs +++ b/src/tests/vector/config.rs @@ -201,10 +201,31 @@ fn effective_url_uses_canonical_order_and_prints_identity_options() { .unwrap(); assert_eq!( config.effective_url(), - "vector://example.com:2000?up=tcp&down=tcp&mux=1&sni=relay.example&pin=abc&rate=1&etar=2&socks=:1080" + "vector://example.com:2000?up=tcp&down=tcp&mux=1&sni=relay.example&pin=abc&rate=1&etar=2&morph=0&socks=:1080" ); } +#[test] +fn morph_is_strict_optional_and_uses_the_first_value() { + let disabled = parse("vector://secret@example.com:2000?socks=:1080").unwrap(); + assert!(!disabled.morph); + assert!(disabled.morph_keys.is_none()); + + let enabled = parse("vector://secret@example.com:2000?morph=1&morph=0&socks=:1080").unwrap(); + assert!(enabled.morph); + assert!(enabled.morph_keys.is_some()); + assert!(enabled.effective_url().contains("&morph=1&")); + + for value in ["", "2", "true"] { + assert!( + parse(&format!( + "vector://secret@example.com:2000?morph={value}&socks=:1080" + )) + .is_err() + ); + } +} + #[test] fn ignores_removed_alpn_and_validates_mux_inputs() { for raw in [ @@ -269,6 +290,21 @@ fn upstream_authority_decodes_reserved_key_bytes_and_ipv6() { ); } +#[test] +fn upstream_morph_derives_from_the_nested_shared_key() { + let query = HashMap::from([("morph".to_owned(), "1".to_owned())]); + let (config, _) = PortalClientConfig::from_upstream_authority( + "upstream-key@origin.example:2080", + &query, + "auto", + ) + .unwrap(); + let actual = config.morph_keys.unwrap().udp_key(); + + assert_eq!(actual, MorphKeys::derive(b"upstream-key").udp_key()); + assert_ne!(actual, MorphKeys::derive(b"outer-key").udp_key()); +} + #[test] fn upstream_authority_decodes_the_shared_key_exactly_once() { let query = HashMap::new(); diff --git a/src/vector/config.rs b/src/vector/config.rs index 71ce082..8b3ab33 100644 --- a/src/vector/config.rs +++ b/src/vector/config.rs @@ -15,9 +15,10 @@ use crate::common::socks::{ use crate::common::{ CarrierEndpoint, DEFAULT_DIALER_IP, ServiceEndpoint, query_first, validate_endpoint_url_input, }; +use crate::transport::MorphKeys; const VECTOR_QUERY_KEYS: &[&str] = &[ - "up", "down", "mux", "sni", "pin", "rate", "etar", "socks", "log", + "up", "down", "mux", "sni", "pin", "rate", "etar", "morph", "socks", "log", ]; /// Whether a client originates dedicated or Mux TLS carriers. @@ -83,6 +84,8 @@ pub(crate) struct PortalClientConfig { pub(crate) up: CarrierMode, pub(crate) down: CarrierMode, pub(crate) mux: MuxMode, + pub(crate) morph: bool, + pub(crate) morph_keys: Option, pub(crate) sni: Option, pub(crate) pin: Option, pub(crate) dialer_ip: String, @@ -117,6 +120,12 @@ impl PortalClientConfig { } else { mux }; + let morph = match query.get("morph").map(String::as_str) { + None | Some("0") => false, + Some("1") => true, + Some(_) => bail!("{context}: morph must be 0 or 1"), + }; + let morph_keys = morph.then(|| MorphKeys::from_url(url)).transpose()?; let sni = query .get("sni") .filter(|value| !value.is_empty() && value.as_str() != "none") @@ -140,6 +149,8 @@ impl PortalClientConfig { up, down, mux, + morph, + morph_keys, sni, pin, dialer_ip: dialer_ip.to_owned(), @@ -188,12 +199,13 @@ impl PortalClientConfig { pub(crate) fn effective_route(&self) -> String { format!( - "up={} down={} mux={} sni={} pin={}", + "up={} down={} mux={} sni={} pin={} morph={}", self.up, self.down, self.mux, self.sni.as_deref().unwrap_or("none"), self.pin.as_deref().unwrap_or("none"), + u8::from(self.morph), ) } } @@ -248,6 +260,8 @@ pub(crate) struct VectorConfig { pub(super) up: CarrierMode, pub(super) down: CarrierMode, pub(super) mux: MuxMode, + pub(super) morph: bool, + pub(super) morph_keys: Option, pub(super) sni: Option, pub(super) pin: Option, pub(super) rate_mbps: i32, @@ -280,6 +294,8 @@ impl VectorConfig { up: portal.up, down: portal.down, mux: portal.mux, + morph: portal.morph, + morph_keys: portal.morph_keys, sni: portal.sni, pin: portal.pin, rate_mbps, @@ -294,6 +310,8 @@ impl VectorConfig { up: self.up, down: self.down, mux: self.mux, + morph: self.morph, + morph_keys: self.morph_keys.clone(), sni: self.sni.clone(), pin: self.pin.clone(), dialer_ip: DEFAULT_DIALER_IP.to_owned(), @@ -320,7 +338,7 @@ impl VectorConfig { pub(super) fn effective_url(&self) -> String { format!( - "vector://{}?up={}&down={}&mux={}&sni={}&pin={}&rate={}&etar={}&socks={}", + "vector://{}?up={}&down={}&mux={}&sni={}&pin={}&rate={}&etar={}&morph={}&socks={}", self.portal_endpoint(), self.up, self.down, @@ -329,6 +347,7 @@ impl VectorConfig { self.pin.as_deref().unwrap_or("none"), self.rate_mbps, self.etar_mbps, + u8::from(self.morph), self.socks.endpoint(), ) } diff --git a/src/vector/mod.rs b/src/vector/mod.rs index b474b4e..0a58589 100644 --- a/src/vector/mod.rs +++ b/src/vector/mod.rs @@ -201,11 +201,12 @@ impl Vector { telemetry_interval().context("vector::Vector::new: invalid NOW_TELEMETRY_INTERVAL")?; let credentials = Credentials::new(&parsed_url)?; let telemetry_summary = format!( - "portal={} up={} down={} mux={} socks={}", + "portal={} up={} down={} mux={} morph={} socks={}", config.portal_endpoint(), config.up, config.down, config.mux, + u8::from(config.morph), config.socks.endpoint(), ); let telemetry = TelemetryHub::for_current_process( diff --git a/src/vector/session.rs b/src/vector/session.rs index 483ec5b..f4523fb 100644 --- a/src/vector/session.rs +++ b/src/vector/session.rs @@ -31,6 +31,7 @@ use crate::protocol::{ encode_auth_frame, encode_udp_close, }; use crate::telemetry::{RuntimeEvent, RuntimeKind, RuntimeLevel, TelemetryHub}; +use crate::transport::MorphTcpStream; use crate::transport::{Stats, transport_flow_control}; use super::config::PortalClientConfig; @@ -61,15 +62,15 @@ impl ClientSignals { } pub(super) struct TlsLane { - pub(super) stream: TlsStream, + pub(super) stream: TlsStream>, pending_auth: Option, _link: LinkGuard, latency: LatencyGuard, } pub(super) struct TlsLaneParts { - pub(super) reader: tokio::io::ReadHalf>, - pub(super) writer: tokio::io::WriteHalf>, + pub(super) reader: tokio::io::ReadHalf>>, + pub(super) writer: tokio::io::WriteHalf>>, pub(super) pending_auth: Option, pub(super) link: LinkGuard, pub(super) latency: LatencyGuard, diff --git a/src/vector/session/quic.rs b/src/vector/session/quic.rs index e1d5695..d96a6cd 100644 --- a/src/vector/session/quic.rs +++ b/src/vector/session/quic.rs @@ -121,13 +121,34 @@ impl QuicManager { } None => SocketAddr::new(IpAddr::V6(std::net::Ipv6Addr::UNSPECIFIED), 0), }; - let mut endpoint = Endpoint::client(bind) + let socket = std::net::UdpSocket::bind(bind) .with_context(|| format!("vector::session::QuicManager: bind {bind} failed"))?; + socket + .set_nonblocking(true) + .context("vector::session::QuicManager: failed to configure UDP socket")?; + let runtime = quinn::default_runtime() + .ok_or_else(|| anyhow!("vector::session::QuicManager: no async runtime found"))?; + let socket = runtime + .wrap_udp_socket(socket) + .context("vector::session::QuicManager: failed to initialize UDP runtime socket")?; + let morph_enabled = self.config.morph_keys.is_some(); + let socket = crate::transport::wrap_morph_udp_socket( + socket, + self.config.morph_keys.as_ref().map(|keys| keys.udp_key()), + )?; + let mut endpoint = Endpoint::new_with_abstract_socket( + crate::transport::morph_endpoint_config(morph_enabled)?, + None, + socket, + runtime, + ) + .context("vector::session::QuicManager: failed to create QUIC endpoint")?; let mut client_config = self.tls.quic_client_config()?; configure_quic_transport( &mut client_config, udp_idle_timeout(), Duration::from_secs(15), + morph_enabled, )?; endpoint.set_default_client_config(client_config); let connecting = endpoint @@ -372,6 +393,7 @@ fn configure_quic_transport( config: &mut quinn::ClientConfig, idle_timeout: Duration, keepalive_interval: Duration, + morph_enabled: bool, ) -> Result<()> { let flow_control = transport_flow_control()?; let mut transport = quinn::TransportConfig::default(); @@ -384,6 +406,7 @@ fn configure_quic_transport( transport.max_idle_timeout(Some(quinn::IdleTimeout::try_from(idle_timeout)?)); transport.keep_alive_interval(Some(keepalive_interval)); transport.congestion_controller_factory(Arc::new(quinn::congestion::BbrConfig::default())); + crate::transport::configure_morph_mtu(&mut transport, morph_enabled); config.transport_config(Arc::new(transport)); Ok(()) } diff --git a/src/vector/session/tls.rs b/src/vector/session/tls.rs index 78dd3a3..0067bb1 100644 --- a/src/vector/session/tls.rs +++ b/src/vector/session/tls.rs @@ -176,7 +176,7 @@ impl TlsManager { .connect_tcp(endpoint, &self.dialer_ip, *family) .await?; let latency = self.latency.register(); - latency.update_tcp(stream.get_ref().0); + latency.update_tcp(stream.get_ref().0.get_ref()); let auth = encode_auth_frame( self.auth_key, AuthTransport::TlsTcp, diff --git a/src/vector/tls.rs b/src/vector/tls.rs index a21bddf..f898ce1 100644 --- a/src/vector/tls.rs +++ b/src/vector/tls.rs @@ -23,6 +23,7 @@ use crate::common::{ AddressFamily, certificate_sha256, dial_tcp_from_local_ip_family, handshake_timeout, }; use crate::protocol::{ALPN, TLS_EXPORTER_LEN, TlsExporter}; +use crate::transport::{MorphKeys, MorphTcpStream}; use super::config::PortalClientConfig; @@ -33,6 +34,7 @@ pub(super) const EXPORTER_LABEL: &[u8] = b"EXPORTER-Nowhere-Auth"; pub(super) struct ClientTls { rustls: Arc, server_name: ServerName<'static>, + morph_keys: Option, } impl ClientTls { @@ -86,6 +88,7 @@ impl ClientTls { Ok(Self { rustls: Arc::new(client), server_name, + morph_keys: config.morph_keys.clone(), }) } @@ -108,7 +111,7 @@ impl ClientTls { endpoint: &str, dialer_ip: &str, family: AddressFamily, - ) -> Result<(TlsStream, TlsExporter)> { + ) -> Result<(TlsStream>, TlsExporter)> { let stream = dial_tcp_from_local_ip_family(dialer_ip, endpoint, handshake_timeout(), family) .await @@ -116,6 +119,7 @@ impl ClientTls { stream .set_nodelay(true) .context("vector::tls::connect_tcp: failed to set TCP_NODELAY")?; + let stream = MorphTcpStream::client(stream, self.morph_keys.clone())?; let connector = TlsConnector::from(self.rustls.clone()); let tls = timeout( handshake_timeout(), From 496d526110b2bf94567c301ba789ef1a9c7b9957 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Wed, 9 Sep 2026 23:24:54 +0800 Subject: [PATCH 37/62] Document Morph wire and security contracts --- docs/interoperability.md | 14 ++++++++++ docs/operations.md | 9 +++++++ docs/protocol.md | 57 ++++++++++++++++++++++++++++++++++++++++ docs/security.md | 20 ++++++++++++++ 4 files changed, 100 insertions(+) diff --git a/docs/interoperability.md b/docs/interoperability.md index bc7920c..370624c 100644 --- a/docs/interoperability.md +++ b/docs/interoperability.md @@ -28,6 +28,20 @@ listener. Vector and `next` endpoints require a dialable host. Endpoint syntax selects local sockets and is not transmitted on the wire. The complete grammar and validation rules are in [Configuration](configuration.md). +## Morph contract + +Peers use `morph=1` on both ends of a hop or `morph=0` on both ends. Morph has +no in-band marker or negotiation. TCP has one client-generated 12-byte nonce +and direction-specific keys; UDP has one 12-byte nonce per datagram and one +shared UDP key. The exact HKDF labels, counter origin, byte limits, and wire +layout are normative in [Protocol](protocol.md). + +Implementations must preserve TCP stream offsets across partial I/O and treat +each GSO/GRO segment as a separate UDP datagram. QUIC sees the decoded packet +length; the physical UDP path sees 12 additional bytes. Interoperability tests +should use fixed derivation and ChaCha20 vectors before attempting a live TLS +or QUIC handshake. + ## TLS lane contract | Mux setting | TLS behavior | Failure scope | diff --git a/docs/operations.md b/docs/operations.md index 1390f69..e33561a 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -50,6 +50,8 @@ the other family. | Startup reports no matching address | DNS results and the carrier's `4` or `6` suffix | | Startup reports address in use | Each transport/port pair and any duplicate service instance | | Vector rejects `up`, `down`, or `mix` | The remote endpoint must declare every selected carrier | +| Morph peers cannot handshake | Both ends need the same `morph` value and shared key | +| QUIC fails only with Morph | The UDP path must carry at least 1212-byte payloads and allow MTU probes | ## Capacity @@ -71,6 +73,13 @@ QUIC uses the shared `throughput` memory profile by default. Select `balanced` or `memory` when connection density matters more than a single flow's bandwidth-delay product. +Morph adds 12 bytes once per TCP connection and 12 bytes to every UDP +datagram. It preserves TCP payload length and keeps GSO/GRO batching when the +platform provides it. UDP socket buffers reserve space for the outer nonce; +Quinn measures decoded QUIC datagram sizes and performs path MTU discovery with +12 bytes reserved for the outer nonce. UDP nonce batches come from a user-space +CSPRNG seeded once per socket from the operating system. + ### TLS Shard placement An originating client shares one full-duplex TLS carrier pool across directions. diff --git a/docs/protocol.md b/docs/protocol.md index bcebff7..658f21b 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -54,6 +54,63 @@ so split OPEN and ATTACH lanes can pair across carrier ports and IP families. Address family is never negotiated on the wire; reachability and family filtering complete before TLS or QUIC authentication. +### Morph socket layer + +When the command endpoint has `morph=1`, a keyed transform sits below TLS/TCP +or QUIC/UDP. It changes the socket wire image and is removed before bytes reach +rustls or Quinn. There is no magic, version, negotiation, fallback, padding, +framing protocol, TLS parser, or QUIC parser. + +The decoded shared-key bytes are the HKDF input: + +```text +morph_root = HKDF-Extract-SHA256( + salt = ASCII("nowhere/morph"), + IKM = shared_key +) + +tcp_c2s_key = HKDF-Expand-SHA256(morph_root, ASCII("tcp c2s"), 32) +tcp_s2c_key = HKDF-Expand-SHA256(morph_root, ASCII("tcp s2c"), 32) +udp_key = HKDF-Expand-SHA256(morph_root, ASCII("udp"), 32) +``` + +Labels contain exactly the shown ASCII bytes and no trailing NUL. The cipher +is IETF ChaCha20 with a 256-bit key, 96-bit nonce, and internal block counter +starting at zero. + +For TCP, the active connector generates one nonce and sends it before TLS: + +```text +client -> server: nonce[12] || ChaCha20-XOR(TLS bytes, tcp_c2s_key, nonce) +server -> client: ChaCha20-XOR(TLS bytes, tcp_s2c_key, nonce) +``` + +The server sends no Morph prefix. Each direction has an independent stream +offset. TLS bytes retain their length and the connection adds exactly 12 bytes. +A direction stops before counter exhaustion, after at most `2^38 - 64` +transformed bytes, and never wraps or rekeys. + +For UDP, every socket datagram is independent in either direction: + +```text +wire datagram = nonce[12] || ChaCha20-XOR(QUIC datagram, udp_key, nonce) +``` + +TCP nonces come directly from the operating system CSPRNG. Each UDP socket +seeds a user-space ChaCha20 CSPRNG from the operating system once and generates +nonce batches from that stream. Receivers drop wire datagrams of 12 bytes or +fewer. Morph adds 12 bytes to every QUIC datagram, including Retry, stateless +reset, handshake, application, and MTU-probe packets. QUIC's 1200-byte minimum +therefore requires a path capable of carrying a 1212-byte UDP payload. With +Morph enabled, Quinn's default 1452-byte path-MTU discovery upper bound is +reduced to 1440 bytes, keeping the transformed UDP payload at 1452 bytes. + +The command URL controls Morph for every carrier declared by that endpoint. +For Portal chaining, the outer `morph` value controls both adjacent hops while +each hop derives keys from its own shared key. Morph adds no authentication, +integrity, replay defense, traffic-analysis resistance, or session security; +the TLS/QUIC and AuthFrame layers retain those responsibilities. + One client session has one random 16-byte `session_id`. Every physical carrier is authenticated with that ID, so Portal can pair logical lanes belonging to the same client session. diff --git a/docs/security.md b/docs/security.md index 9056000..e046cea 100644 --- a/docs/security.md +++ b/docs/security.md @@ -22,6 +22,26 @@ TLS is version 1.3. Deployments may use a certificate pin, normal system-root verification with SNI, or the explicitly configured unverified certificate mode used by generated local certificates. +## Morph boundary + +With `morph=1`, HKDF-SHA256 derives separate TCP client-to-server, +TCP server-to-client, and UDP keys from the endpoint shared key. ChaCha20 XOR +then masks the TLS stream or each QUIC datagram below the secure transport. +The nonce is public: TCP carries one 12-byte client nonce per connection and +UDP carries one 12-byte nonce per datagram. + +An observer without the shared key cannot directly recover the bare TLS/QUIC +wire image or feed captured bytes directly to a generic TLS/QUIC parser. Morph +does not authenticate bytes, detect modification, reject replay, hide lengths +or timing, imitate HTTPS, or provide session security. TLS/QUIC and AuthFrame +remain mandatory. Random nonces can collide, UDP maintains no replay state, +and TCP does not remember previously used client nonces. Shared keys therefore +need adequate entropy; HKDF does not make a guessable key expensive to search. + +Morph has no negotiation or downgrade path. A missing setting or wrong key +appears as a TLS/QUIC handshake failure or timeout rather than a distinct +authenticated Morph error. + ## Endpoint exposure The service endpoint is also the network exposure policy. A compact Portal From 5c20e31cba3293645038597c8181253f5a20eb82 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Wed, 9 Sep 2026 23:24:59 +0800 Subject: [PATCH 38/62] Document Morph endpoint configuration --- docs/configuration.md | 23 +++++++++++++++++++---- docs/integrations.md | 9 ++++++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 5abfb6e..87bdba4 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -66,6 +66,7 @@ portal://shared-key@host:port?tls=1&log=info portal://shared-key@*:2000?tls=1&log=info portal://shared-key@*/tcp:2006/udp:2017?tls=1&log=info portal://shared-key@host/tcp4:2006/udp6:2017?tls=1&log=info +portal://shared-key@*:2000?tls=1&morph=1&log=info ``` The compact `host:port` form enables TLS/TCP and QUIC/UDP on the same port. @@ -103,6 +104,7 @@ sockets instead of relying on an operating-system dual-stack default. | `crt`, `key` | PEM paths, required with `tls=2` | — | | `rate`, `etar` | Mbps, `0` disables limit | `0` | | `dial` | `auto` or local IP | `auto` | +| `morph` | `0` bare TLS/QUIC wire, `1` keyed wire transform | `0` | | `socks` | outbound SOCKS5 configuration | disabled | | `next` | `shared-key@host:port` or explicit carrier endpoint | disabled | | `up`, `down` | native next-hop policy: `tcp`, `udp`, or `mix` | only carrier, otherwise `tcp` | @@ -116,6 +118,10 @@ upstream hop. Protocol version is negotiated independently with the next Portal. These upstream options are ignored when `next` is absent or `none`. `socks` and `next` are mutually exclusive outbound paths. +`morph=1` controls both the Portal listener and its native `next` client. The +listener derives Morph keys from the outer Portal key; the `next` client derives +them from the key inside `next`. The nested value never carries an inner query. + ## Vector URL ```text @@ -123,6 +129,7 @@ vector://shared-key@host:port?up=tcp&down=tcp&socks=127.0.0.1:1080 vector://shared-key@host/tcp:2006?socks=127.0.0.1:1080 vector://shared-key@host/udp6:2017?socks=127.0.0.1:1080 vector://shared-key@host/tcp:2006/udp:2017?up=tcp&down=udp&socks=127.0.0.1:1080 +vector://shared-key@host:2000?morph=1&socks=127.0.0.1:1080 ``` Vector uses the TCP carrier port only for TLS and the UDP carrier port only for @@ -144,6 +151,7 @@ traffic. The transport default does not enable Mux; omitted `mux` remains `0`. | `sni` | verified DNS name, or `none` | `none` | | `pin` | certificate SHA-256 pin, or `none` | `none` | | `rate`, `etar` | Mbps, `0` disables limit | `0` | +| `morph` | `0` bare TLS/QUIC wire, `1` keyed wire transform | `0` | | `socks` | required local listen address, optionally credentials | — | | `log` | logging threshold | `info` | @@ -162,7 +170,7 @@ over IPv6. A carrier or family chosen locally does not constrain the next hop. `next` must contain exactly one encoded shared key, `@`, and one endpoint. Its host must be concrete; `*` is invalid. It has no inner query or fragment. -`up`, `down`, `mux`, `sni`, and `pin` remain query parameters of the outer +`up`, `down`, `mux`, `sni`, `pin`, and `morph` remain query parameters of the outer Portal URL. Reserved bytes in the nested key are percent-encoded once and are decoded once when the upstream credentials are built. @@ -176,18 +184,18 @@ family boundary to recover from a failure. ```text Portal URL | - +-- listener: endpoint path, tls, crt, key + +-- listener: endpoint path, tls, crt, key, morph +-- relay: rate, etar, dial, log | +-- outbound path | +-- direct target access +-- socks --> SOCKS5 proxy --> target - +-- next --> {up, down, mux, sni, pin} --> Portal + +-- next --> {up, down, mux, sni, pin, morph} --> Portal Vector URL | - +-- Portal client: up, down, mux, sni, pin + +-- Portal client: up, down, mux, sni, pin, morph +-- SOCKS5 edge: socks +-- relay: rate, etar, log ``` @@ -240,6 +248,13 @@ normal unknown-parameter rule. Portal's `mux` option controls only its `next` client. Inbound Portal connections accept a `0xff`-marked Mux carrier or an unmarked dedicated lane on the same listener. +Morph is hop-local and has no negotiation or fallback. Both endpoints must +configure the same value. Compact `HOST:PORT` endpoints apply it to TCP and +UDP on the shared port; explicit paths apply it only to the carrier entries +present in the path. Values other than `0` and `1`, including an empty value, +are configuration errors. Duplicate `morph` keys follow the general rule that +the first recognized value wins. + For `tls=2`, `crt` and `key` are native filesystem paths. Quote the complete URL when a Windows path, space, `&`, or another shell-significant character is present. diff --git a/docs/integrations.md b/docs/integrations.md index b6e2621..e2419fd 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -50,6 +50,8 @@ Configuration integrations should preserve these invariants: - reserve `*` and compact empty hosts for Portal listeners; - keep `next` policy in the outer Portal query rather than adding an inner query to the nested endpoint. +- emit `morph=1` only when both peers on that hop implement the Morph wire + transform; omission and `morph=0` are equivalent. Portal accepts `portal://key@:2000` as the compact wildcard alias. Explicit Portal listeners use `portal://key@*/tcp:2006/udp:2017`. Vector and native @@ -78,6 +80,7 @@ An alternate client provides: - nonzero Flow IDs unique among active flows in that session; - matching OPEN and ATTACH metadata for split-carrier flows; - bounded retry and reconnection behavior after carrier failure. +- the exact Morph HKDF and ChaCha20 socket wrapper when `morph=1` is selected. The command URL is not transmitted. It selects the remote socket used for each physical carrier; the negotiated ALPN, AuthFrame transport byte, and FlowHeader @@ -99,7 +102,11 @@ dedicated or Mux TLS when TCP can be selected and defaults to `0`; it has no effect without `next` and canonicalizes to `0` for `udp/udp`. Authentication, flow setup, bounds, and failure semantics are identical at every hop. +The outer `morph=0|1` applies to both the inbound listener and the native next +client. The two sides derive from their respective endpoint keys, so a relay +does not reuse its inbound Morph keys on the next hop. + The nested value contains no scheme, query, or fragment. Percent-encoded key -bytes are decoded exactly once. `up`, `down`, `mux`, `sni`, and `pin` stay on +bytes are decoded exactly once. `up`, `down`, `mux`, `sni`, `pin`, and `morph` stay on the outer Portal URL, while the outer `dial` address also constrains the local family used for upstream TCP and UDP sockets. From 60cd912ac69de384a5a34dc636a996077c7b18e2 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Wed, 9 Sep 2026 23:25:04 +0800 Subject: [PATCH 39/62] Refine project overview and data path --- README.md | 185 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 96 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 3a1735c..8e99762 100644 --- a/README.md +++ b/README.md @@ -3,48 +3,34 @@

    - Two-transport encrypted relay with independently split directions + One relay. Two carriers. Independent directions.

    - A cross-platform encrypted relay that composes TLS/TCP and QUIC/UDP
    - independently for upload and download. + A cross-platform relay that composes TLS/TCP and QUIC/UDP
    + independently for every flow.

    -Nowhere gives one service edge two encrypted carrier families. A local -**Vector** accepts SOCKS5 traffic; a remote **Portal** authenticates carriers, -opens targets, and relays data. Every logical flow chooses its uplink and -downlink independently instead of forcing both directions onto one transport. +Nowhere joins TLS/TCP and QUIC/UDP behind one service edge. **Vector** accepts +local SOCKS5 traffic; **Portal** authenticates carriers and reaches the target. +Each flow selects its uplink and downlink independently. | Core property | What it means | | --- | --- | -| One service edge | TLS/TCP and QUIC/UDP share one host, credential, and lifecycle, with independent ports and address families | -| Split directions | Uplink and downlink independently select TLS/TCP or QUIC/UDP | -| Complete ingress | SOCKS5 CONNECT carries TCP; UDP ASSOCIATE carries UDP | -| Native chaining | A Portal can forward directly to another Portal without a loopback SOCKS5 conversion | -| Local observability | The same binary discovers running instances and renders live telemetry metrics | - -## Live operations - -

    - Nowhere TUI showing live traffic histories, connection and carrier metrics, privacy-aware access logs, runtime events, filtering, pause, and help -

    - -The read-only TUI discovers Portal and Vector instances for the current user. -It presents traffic, carriers, process metrics, Access logs, and Runtime logs -without owning the service lifecycle. Start it from another terminal: - -```bash -nowhere tui -``` +| Unified edge | TLS/TCP and QUIC/UDP share one identity and lifecycle | +| Split routing | Uplink and downlink choose their carrier independently | +| Optional Morph | A keyed transform masks the TLS/QUIC wire image | +| TCP and UDP | SOCKS5 CONNECT and UDP ASSOCIATE are both supported | +| Native chaining | Portal forwards directly to Portal with no local proxy loop | +| Built-in telemetry | The same binary discovers and inspects live instances | ## How it works @@ -68,10 +54,8 @@ nowhere tui +------------+ +------------+ ``` -Every service URL has one shared host and either a compact or explicit -endpoint. The compact form enables both carriers on one port. The explicit -form enables only the listed carriers and gives each one its own port and -address-family policy. +Each service URL uses either a compact endpoint for both carriers on one port, +or an explicit endpoint that assigns carriers, ports, and address families. | Endpoint | Meaning | |---|---| @@ -80,17 +64,14 @@ address-family policy. | `@*/udp:2017` | QUIC/UDP only, IPv4 and IPv6 | | `@*/tcp4:2006/udp6:2017` | TLS/TCP on IPv4 and QUIC/UDP on IPv6 | -`*` is a Portal listen host. Vector and `next` use a concrete IP address or -hostname. The compact Portal form also accepts an empty host, so `@:2000` and -`@*:2000` have the same meaning. See -[Configuration](docs/configuration.md) for the complete grammar, validation -rules, DNS behavior, family availability, and canonical output. +`*` is reserved for Portal listeners; Vector and `next` require a concrete +address or hostname. On Portal, `@:2000` is shorthand for `@*:2000`. The full +grammar is documented in [Configuration](docs/configuration.md). -### One flow, two transport decisions +### Independent uplink and downlink -Vector's `up` and `down` parameters accept `tcp`, `udp`, or `mix`. When both -carriers are available, omitted directions select TCP. Mux remains disabled -unless `mux=1` is set: +`up` and `down` accept `tcp`, `udp`, or `mix`. With both carriers available, +the default is TCP; `mux=1` enables TLS multiplexing. | `up` ↓ / `down` → | `tcp` | `udp` | `mix` | |---|---|---|---| @@ -98,37 +79,62 @@ unless `mux=1` is set: | `udp` | QT | QQ | QT ↔ QQ | | `mix` | TT ↔ QT | TQ ↔ QQ | TT ↔ QQ | -T means TLS/TCP and Q means QUIC/UDP, with uplink first. Each mixed cell makes -one stateless 50/50 choice per flow; `mix/mix` produces only TT or QQ. The -primary route has a `NOW_MIX_FALLBACK_TIMEOUT` budget (default `1s`), then the -other route is attempted once with a new flow ID. FlowHeader carries only the -resolved concrete pair, and no fallback occurs after its write begins. Portal -`next=` applies the same policy independently per hop. +T is TLS/TCP and Q is QUIC/UDP, with uplink first. `mix` makes one 50/50 choice +per flow and may try the alternate route once before commitment. Portal +`next=` applies the same policy independently on each hop. -## Engineered for a small data path +## Data path + +Authentication belongs to each physical carrier; routing belongs to each +logical flow. Once Portal returns `READY`, application data travels as a plain +byte stream or QUIC DATAGRAM payload. + +```text +Carrier bootstrap Logical flow + ++----------------+ +----------------+----------+-------------+ +| AuthFrame | | FlowHeader | Target? | Payload ... | +| 32 bytes | | 5 bytes | variable | after READY | ++----------------+ +----------------+----------+-------------+ + | | + +-- TLS: dedicated lane or Mux +-- TCP: reliable byte stream + +-- QUIC: first stream only +-- UDP: UoT or QUIC DATAGRAM +``` -The data path uses compact binary frames, connection-bound authentication, -reusable buffers, bounded queues, and native QUIC streams and DATAGRAMs. TLS -flows use dedicated lanes or lazily opened Mux Shards. Detailed framing and -resource bounds live in [Protocol](docs/protocol.md) and -[Security](docs/security.md). +Frames are compact, queues are bounded, and hot-path buffers are reused. See +[Protocol](docs/protocol.md) for the wire contract and +[Security](docs/security.md) for trust boundaries. -### Native Portal chaining +### Morph -A relay Portal can terminate the incoming TLS/QUIC carrier and open the next -Nowhere flow directly with the same transport engine used by Vector: +`morph=1` masks the bare TLS/QUIC wire image with a transform derived from the +shared key: + +```text +TCP client -> server [ nonce 12B ][ ChaCha20-XOR(TLS stream) ] + server -> client [ ChaCha20-XOR(TLS stream) ] + +UDP each datagram [ nonce 12B ][ ChaCha20-XOR(QUIC datagram) ] +``` + +Both endpoints on a hop must enable it. Morph is wire masking, with no protocol +camouflage or added security semantics. See [Protocol](docs/protocol.md). + +### Native chaining + +A Portal can open the next Nowhere hop directly: ```bash nowhere \ 'portal://relay-key@:2000?next=origin-key@origin.example:2000&up=udp&down=udp' ``` -`next` is lazy and mutually exclusive with outbound `socks`. Portal forwarding -uses the native flow protocol and is bounded to seven hops. +`next` is lazy, mutually exclusive with outbound `socks`, and bounded to seven +hops. ## Quick start -Building from source requires a supported target and a stable Rust toolchain. +Use a stable Rust toolchain on a supported target. ### 1. Build @@ -138,7 +144,7 @@ cargo build --release --locked ### 2. Start Portal -The compact endpoint accepts TLS/TCP and QUIC/UDP on port `2000`: +Listen on TLS/TCP and QUIC/UDP at port `2000`: ```bash ./target/release/nowhere 'portal://change-me@127.0.0.1:2000' @@ -146,54 +152,60 @@ The compact endpoint accepts TLS/TCP and QUIC/UDP on port `2000`: ### 3. Start Vector -This Vector exposes SOCKS5 on `127.0.0.1:1080`: +Expose SOCKS5 on `127.0.0.1:1080`: ```bash ./target/release/nowhere \ 'vector://change-me@127.0.0.1:2000?up=tcp&down=tcp&socks=127.0.0.1:1080' ``` -Mux, split-carrier, certificate, and chaining examples are in the -[configuration guide](docs/configuration.md) and -[quick start](docs/quick-start.md). +More examples are available in [Configuration](docs/configuration.md) and the +[extended quick start](docs/quick-start.md). ### 4. Inspect -Open another terminal and run: +Open the local TUI from another terminal: ```bash ./target/release/nowhere tui ``` -## Before public deployment +## Live operations -The local examples omit `sni`, which disables certificate verification. A -public Portal should use a CA-trusted certificate with strict verification: +

    + Nowhere TUI showing live traffic histories, connection and carrier metrics, privacy-aware access logs, runtime events, filtering, pause, and help +

    + +The read-only TUI discovers local Portal and Vector instances and presents +traffic, carrier, process, and log data without controlling their lifecycle. + +## Public deployment + +The local examples disable certificate verification by omitting `sni`. Public +deployments should use a trusted certificate and verified server name: ```bash nowhere 'portal://change-me@:2000?tls=2&crt=/etc/nowhere/cert.pem&key=/etc/nowhere/key.pem' nowhere 'vector://change-me@relay.example:2000?sni=relay.example&socks=127.0.0.1:1080' ``` -Certificate pinning is also available. Review the -[security model](docs/security.md) and [configuration](docs/configuration.md) -before exposing a Portal publicly. +Certificate pinning is also available. Review [Security](docs/security.md) and +[Configuration](docs/configuration.md) before exposing a Portal. -## Operational boundaries +## Platform scope -Portal, Vector, relay, TUI, and local discovery run on every supported -platform; process telemetry varies by operating system. See -[Platforms](docs/platforms.md) and [Operations](docs/operations.md). +Portal, Vector, relay, TUI, and discovery share the supported platform matrix; +process telemetry varies by operating system. See [Platforms](docs/platforms.md) +and [Operations](docs/operations.md). -## Documentation map +## Documentation -Start with the [documentation index](docs/README.md). It links the focused -guides for configuration, protocol, security, operations, platforms, and -integrations. +The [documentation index](docs/README.md) covers configuration, protocol, +security, operations, platforms, and integrations. ## Development -Run the project checks on a supported host: +Run the standard checks on a supported host: ```bash cargo fmt --all -- --check @@ -202,25 +214,20 @@ cargo clippy --all-targets --locked -- -D warnings cargo build --release --locked ``` -On macOS with [Apple Container](https://github.com/apple/container), the -reusable Linux check environment remains available: +On macOS, [Apple Container](https://github.com/apple/container) provides the +reusable Linux check environment: ```bash ./scripts/check-linux.sh ``` -CI runs the project on Linux, macOS, and Windows. Release packaging covers -Linux GNU/musl on x86-64 and AArch64, macOS on Apple Silicon, and Windows -x86-64 MSVC. - -Protocol changes must update the normative wire document and protocol-vector -tests in the same change. +CI covers Linux, macOS, and Windows. Release packaging covers Linux GNU/musl on +x86-64 and AArch64, macOS on Apple Silicon, and Windows x86-64 MSVC. Protocol +changes must update the wire document and protocol vectors together. ## License Nowhere is licensed under the [GNU General Public License v3.0](LICENSE). -Distributions of original or modified binaries must comply with the GPLv3 -source and notice requirements. --- From 8fb0d7f66d8815db757ee95963cbf1b2e0216a0b Mon Sep 17 00:00:00 2001 From: yosebyte Date: Thu, 10 Sep 2026 15:53:47 +0800 Subject: [PATCH 40/62] Reseed Morph UDP nonces before exhaustion --- docs/operations.md | 2 +- docs/protocol.md | 4 +-- src/tests/transport/morph/udp.rs | 24 +++++++++++-- src/transport/morph/udp.rs | 59 +++++++++++++++++++++++++------- 4 files changed, 72 insertions(+), 17 deletions(-) diff --git a/docs/operations.md b/docs/operations.md index e33561a..53bf429 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -78,7 +78,7 @@ datagram. It preserves TCP payload length and keeps GSO/GRO batching when the platform provides it. UDP socket buffers reserve space for the outer nonce; Quinn measures decoded QUIC datagram sizes and performs path MTU discovery with 12 bytes reserved for the outer nonce. UDP nonce batches come from a user-space -CSPRNG seeded once per socket from the operating system. +CSPRNG seeded from the operating system and reseeded before stream exhaustion. ### TLS Shard placement diff --git a/docs/protocol.md b/docs/protocol.md index 658f21b..2797cd2 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -97,8 +97,8 @@ wire datagram = nonce[12] || ChaCha20-XOR(QUIC datagram, udp_key, nonce) ``` TCP nonces come directly from the operating system CSPRNG. Each UDP socket -seeds a user-space ChaCha20 CSPRNG from the operating system once and generates -nonce batches from that stream. Receivers drop wire datagrams of 12 bytes or +seeds a user-space ChaCha20 CSPRNG from the operating system and reseeds it +before its stream is exhausted. Receivers drop wire datagrams of 12 bytes or fewer. Morph adds 12 bytes to every QUIC datagram, including Retry, stateless reset, handshake, application, and MTU-probe packets. QUIC's 1200-byte minimum therefore requires a path capable of carrying a 1212-byte UDP payload. With diff --git a/src/tests/transport/morph/udp.rs b/src/tests/transport/morph/udp.rs index 93c2ab3..6b63aab 100644 --- a/src/tests/transport/morph/udp.rs +++ b/src/tests/transport/morph/udp.rs @@ -18,13 +18,33 @@ fn udp_nonce_generator_fills_batches_from_one_stream() { let mut buffers = UdpBuffers::from_seed([3; 32]); let mut first = [0; NONCE_LEN]; let mut second = [0; NONCE_LEN]; - generate_nonce(&mut buffers.nonce_cipher, &mut first).unwrap(); - generate_nonce(&mut buffers.nonce_cipher, &mut second).unwrap(); + buffers.nonce_generator.generate(&mut first).unwrap(); + buffers.nonce_generator.generate(&mut second).unwrap(); assert_ne!(first, [0; NONCE_LEN]); assert_ne!(second, first); } +#[test] +fn udp_nonce_generator_reseeds_before_exhaustion_and_retries_failures() { + let mut generator = UdpNonceGenerator::from_seed([3; 32]); + generator.generated = UDP_NONCE_STREAM_LIMIT - NONCE_LEN as u64 + 1; + let mut nonce = [9; NONCE_LEN]; + + let error = generator + .generate_with_reseed(&mut nonce, || Err(io::Error::other("no entropy"))) + .unwrap_err(); + assert_eq!(error.to_string(), "no entropy"); + assert_eq!(generator.generated, UDP_NONCE_STREAM_LIMIT - 11); + assert_eq!(nonce, [9; NONCE_LEN]); + + generator + .generate_with_reseed(&mut nonce, || Ok([4; 32])) + .unwrap(); + assert_eq!(generator.generated, NONCE_LEN as u64); + assert_ne!(nonce, [9; NONCE_LEN]); +} + #[test] fn morph_endpoint_reserves_the_udp_nonce_overhead() { let plain = morph_endpoint_config(false).unwrap(); diff --git a/src/transport/morph/udp.rs b/src/transport/morph/udp.rs index 05d498f..a55129a 100644 --- a/src/transport/morph/udp.rs +++ b/src/transport/morph/udp.rs @@ -16,6 +16,7 @@ use super::{MorphKey, NONCE_LEN, exhausted}; const QUINN_DEFAULT_MTU_UPPER_BOUND: u16 = 1452; const MAX_INVALID_RECEIVE_BATCHES: usize = 32; +pub(super) const UDP_NONCE_STREAM_LIMIT: u64 = (1u64 << 38) - 64; pub(crate) fn wrap_morph_udp_socket( inner: Arc, @@ -34,30 +35,62 @@ pub(crate) fn wrap_morph_udp_socket( pub(super) struct UdpBuffers { send: Vec, receive: [Vec; quinn::udp::BATCH_SIZE], - pub(super) nonce_cipher: ChaCha20, + pub(super) nonce_generator: UdpNonceGenerator, } impl UdpBuffers { fn new() -> io::Result { - let mut seed = [0u8; 32]; - getrandom::fill(&mut seed).map_err(io::Error::other)?; - Ok(Self::from_seed(seed)) + Ok(Self::from_seed(random_seed()?)) } pub(super) fn from_seed(seed: [u8; 32]) -> Self { Self { send: Vec::new(), receive: std::array::from_fn(|_| Vec::new()), - nonce_cipher: ChaCha20::new((&seed).into(), (&[0u8; NONCE_LEN]).into()), + nonce_generator: UdpNonceGenerator::from_seed(seed), } } } -pub(super) fn generate_nonce(cipher: &mut ChaCha20, nonce: &mut [u8; NONCE_LEN]) -> io::Result<()> { - nonce.fill(0); - cipher - .try_apply_keystream(nonce) - .map_err(|_| io::Error::other("Morph UDP nonce generator exhausted")) +pub(super) struct UdpNonceGenerator { + cipher: ChaCha20, + pub(super) generated: u64, +} + +impl UdpNonceGenerator { + pub(super) fn from_seed(seed: [u8; 32]) -> Self { + Self { + cipher: ChaCha20::new((&seed).into(), (&[0u8; NONCE_LEN]).into()), + generated: 0, + } + } + + pub(super) fn generate(&mut self, nonce: &mut [u8; NONCE_LEN]) -> io::Result<()> { + self.generate_with_reseed(nonce, random_seed) + } + + pub(super) fn generate_with_reseed( + &mut self, + nonce: &mut [u8; NONCE_LEN], + reseed: impl FnOnce() -> io::Result<[u8; 32]>, + ) -> io::Result<()> { + if UDP_NONCE_STREAM_LIMIT.saturating_sub(self.generated) < NONCE_LEN as u64 { + let seed = reseed()?; + *self = Self::from_seed(seed); + } + nonce.fill(0); + self.cipher + .try_apply_keystream(nonce) + .map_err(|_| io::Error::other("Morph UDP nonce generator exhausted"))?; + self.generated += NONCE_LEN as u64; + Ok(()) + } +} + +fn random_seed() -> io::Result<[u8; 32]> { + let mut seed = [0u8; 32]; + getrandom::fill(&mut seed).map_err(io::Error::other)?; + Ok(seed) } pub(crate) fn morph_endpoint_config(enabled: bool) -> anyhow::Result { @@ -111,7 +144,9 @@ impl AsyncUdpSocket for MorphUdpSocket { let segments = transmit.contents.len().div_ceil(plain_stride); let mut buffers = self.buffers.lock().unwrap_or_else(|lock| lock.into_inner()); let UdpBuffers { - send, nonce_cipher, .. + send, + nonce_generator, + .. } = &mut *buffers; send.clear(); send.reserve(transmit.contents.len() + segments * NONCE_LEN); @@ -120,7 +155,7 @@ impl AsyncUdpSocket for MorphUdpSocket { send.resize(start + NONCE_LEN + plain.len(), 0); let (nonce, payload) = send[start..].split_at_mut(NONCE_LEN); let nonce: &mut [u8; NONCE_LEN] = nonce.try_into().expect("fixed nonce prefix"); - generate_nonce(nonce_cipher, nonce)?; + nonce_generator.generate(nonce)?; payload.copy_from_slice(plain); let mut cipher = ChaCha20::new((&self.key).into(), (&*nonce).into()); cipher From 083f4810a6939da19c929dd199ac82bd0d9b6271 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Thu, 10 Sep 2026 15:55:29 +0800 Subject: [PATCH 41/62] Avoid redundant Morph TCP transforms under backpressure --- src/tests/transport/morph/tcp.rs | 89 +++++++++++++++++++++++++++++++- src/transport/morph/tcp.rs | 39 +++++++++++--- 2 files changed, 120 insertions(+), 8 deletions(-) diff --git a/src/tests/transport/morph/tcp.rs b/src/tests/transport/morph/tcp.rs index 942ee2f..20c1d59 100644 --- a/src/tests/transport/morph/tcp.rs +++ b/src/tests/transport/morph/tcp.rs @@ -1,9 +1,15 @@ // Copyright (C) 2026 NodePassProject // SPDX-License-Identifier: GPL-3.0-only +use std::io; +use std::pin::Pin; +use std::sync::Arc; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll, Waker}; + use chacha20::ChaCha20; use chacha20::cipher::KeyIvInit; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf}; use super::super::*; @@ -41,6 +47,87 @@ fn chacha20_starts_at_block_zero() { ); } +#[test] +fn tcp_empty_io_does_not_wait_for_the_nonce() { + let keys = MorphKeys::derive(b"shared"); + let (_client_io, server_io) = tokio::io::duplex(64); + let mut server = MorphTcpStream::server(server_io, Some(keys)); + let waker = Waker::noop(); + let mut context = Context::from_waker(waker); + let mut empty = []; + let mut read_buf = ReadBuf::new(&mut empty); + + assert!(matches!( + Pin::new(&mut server).poll_read(&mut context, &mut read_buf), + Poll::Ready(Ok(())) + )); + assert!(matches!( + Pin::new(&mut server).poll_write(&mut context, &[]), + Poll::Ready(Ok(0)) + )); +} + +#[derive(Debug)] +struct PendingWriteStream { + writes: Arc, +} + +impl AsyncRead for PendingWriteStream { + fn poll_read( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _buf: &mut ReadBuf<'_>, + ) -> Poll> { + Poll::Pending + } +} + +impl AsyncWrite for PendingWriteStream { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + input: &[u8], + ) -> Poll> { + self.writes.fetch_add(1, Ordering::Relaxed); + Poll::Ready(Ok(input.len())) + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +impl MorphWriteReady for PendingWriteStream { + fn poll_morph_write_ready(&self, _cx: &mut Context<'_>) -> Poll> { + Poll::Pending + } +} + +#[test] +fn tcp_waits_for_write_readiness_before_copying_or_xoring() { + let writes = Arc::new(AtomicUsize::new(0)); + let inner = PendingWriteStream { + writes: writes.clone(), + }; + let keys = MorphKeys::derive(b"shared"); + let mut client = MorphTcpStream::client(inner, Some(keys)).unwrap(); + set_tcp_nonce(&mut client, [11; NONCE_LEN]); + client.morph.as_mut().unwrap().write_prefix_pos = NONCE_LEN; + let waker = Waker::noop(); + let mut context = Context::from_waker(waker); + + assert!(matches!( + Pin::new(&mut client).poll_write(&mut context, b"payload"), + Poll::Pending + )); + assert_eq!(writes.load(Ordering::Relaxed), 0); + assert!(client.morph.as_ref().unwrap().write_buffer.is_empty()); +} + #[tokio::test] async fn tcp_uses_one_client_nonce_and_independent_directions() { let keys = MorphKeys::derive(b"shared"); diff --git a/src/transport/morph/tcp.rs b/src/transport/morph/tcp.rs index 3b6f2ac..d9a35fe 100644 --- a/src/transport/morph/tcp.rs +++ b/src/transport/morph/tcp.rs @@ -31,7 +31,7 @@ pub(super) struct TcpMorph { read_prefix_pos: usize, write_prefix_pos: usize, write_waiter: Option, - write_buffer: Vec, + pub(super) write_buffer: Vec, } pub(crate) struct MorphTcpStream { @@ -136,6 +136,9 @@ impl AsyncRead for MorphTcpStream { cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { + if buf.remaining() == 0 { + return Poll::Ready(Ok(())); + } if self.keys.is_none() { return Pin::new(&mut self.inner).poll_read(cx, buf); } @@ -202,12 +205,32 @@ impl AsyncRead for MorphTcpStream { } } -impl AsyncWrite for MorphTcpStream { +pub(crate) trait MorphWriteReady { + fn poll_morph_write_ready(&self, cx: &mut Context<'_>) -> Poll>; +} + +impl MorphWriteReady for tokio::net::TcpStream { + fn poll_morph_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + self.poll_write_ready(cx) + } +} + +#[cfg(test)] +impl MorphWriteReady for tokio::io::DuplexStream { + fn poll_morph_write_ready(&self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +impl AsyncWrite for MorphTcpStream { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, input: &[u8], ) -> Poll> { + if input.is_empty() { + return Poll::Ready(Ok(0)); + } if self.keys.is_none() { return Pin::new(&mut self.inner).poll_write(cx, input); } @@ -240,16 +263,18 @@ impl AsyncWrite for MorphTcpStream { return Poll::Pending; } } - if input.is_empty() { - return Poll::Ready(Ok(0)); - } - let state = this.morph.as_mut().unwrap(); - let remaining = TCP_STREAM_LIMIT.saturating_sub(state.write_offset); + let remaining = TCP_STREAM_LIMIT.saturating_sub(this.morph.as_ref().unwrap().write_offset); if remaining == 0 { return Poll::Ready(Err(exhausted())); } let count = usize::try_from(remaining.min(input.len() as u64)) .expect("count is bounded by usize input length"); + match this.inner.poll_morph_write_ready(cx) { + Poll::Ready(Ok(())) => {} + Poll::Ready(Err(error)) => return Poll::Ready(Err(error)), + Poll::Pending => return Poll::Pending, + } + let state = this.morph.as_mut().unwrap(); state.write_buffer.clear(); state.write_buffer.extend_from_slice(&input[..count]); state From 50ed83ed5aaa47b7ba4a6c06ac7816d94de7f749 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Thu, 10 Sep 2026 15:57:25 +0800 Subject: [PATCH 42/62] Drop malformed Morph UDP receive records --- src/tests/transport/morph/udp.rs | 23 ++++++++++++- src/transport/morph/udp.rs | 57 ++++++++++++++++++++++---------- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/src/tests/transport/morph/udp.rs b/src/tests/transport/morph/udp.rs index 6b63aab..3c3b9ca 100644 --- a/src/tests/transport/morph/udp.rs +++ b/src/tests/transport/morph/udp.rs @@ -164,7 +164,7 @@ async fn udp_preserves_gso_datagram_boundaries() { } #[tokio::test] -async fn udp_discards_short_wire_datagrams_before_returning_valid_data() { +async fn udp_discards_invalid_wire_datagrams_before_returning_valid_data() { let key = MorphKeys::derive(b"shared").udp_key(); let raw = Arc::new(FakeUdpSocket::default()); let socket = MorphUdpSocket { @@ -184,6 +184,27 @@ async fn udp_discards_short_wire_datagrams_before_returning_valid_data() { .unwrap() .push_back((vec![0; NONCE_LEN], packet_meta(NONCE_LEN))); + socket + .try_send(&Transmit { + destination: "127.0.0.1:2".parse().unwrap(), + ecn: None, + contents: b"oversized", + segment_size: None, + src_ip: None, + }) + .unwrap(); + let (oversized, _) = raw.sent.lock().unwrap().pop().unwrap(); + let oversized_len = oversized.len(); + raw.receive + .lock() + .unwrap() + .push_back((oversized, packet_meta(oversized_len))); + + raw.receive + .lock() + .unwrap() + .push_back((vec![0; NONCE_LEN], packet_meta(5 + NONCE_LEN * 2 + 1))); + socket .try_send(&Transmit { destination: "127.0.0.1:2".parse().unwrap(), diff --git a/src/transport/morph/udp.rs b/src/transport/morph/udp.rs index a55129a..00f9377 100644 --- a/src/transport/morph/udp.rs +++ b/src/transport/morph/udp.rs @@ -179,31 +179,62 @@ impl AsyncUdpSocket for MorphUdpSocket { bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta], ) -> Poll> { + let receive_count = bufs.len().min(meta.len()).min(quinn::udp::BATCH_SIZE); + if receive_count == 0 { + return Poll::Ready(Ok(0)); + } let max_segments = self.inner.max_receive_segments().max(1); let mut buffers = self.buffers.lock().unwrap_or_else(|lock| lock.into_inner()); - for (storage, target) in buffers.receive.iter_mut().zip(bufs.iter()) { + for (storage, target) in buffers + .receive + .iter_mut() + .zip(bufs.iter()) + .take(receive_count) + { storage.resize(target.len() + NONCE_LEN * max_segments, 0); } for _ in 0..MAX_INVALID_RECEIVE_BATCHES { let receive = buffers.receive.each_mut(); let mut wire_bufs = receive.map(|value| IoSliceMut::new(value)); - let received = match self.inner.poll_recv(cx, &mut wire_bufs[..bufs.len()], meta) { - Poll::Ready(Ok(received)) => received, + let received = match self.inner.poll_recv( + cx, + &mut wire_bufs[..receive_count], + &mut meta[..receive_count], + ) { + Poll::Ready(Ok(received)) => received.min(receive_count), Poll::Ready(Err(error)) => return Poll::Ready(Err(error)), Poll::Pending => return Poll::Pending, }; let mut output = 0; for index in 0..received { let wire_meta = meta[index]; - let wire_stride = wire_meta.stride.max(1); + let wire_stride = wire_meta.stride; let storage = &mut buffers.receive[index]; + if wire_meta.len == 0 + || wire_meta.len > storage.len() + || wire_stride <= NONCE_LEN + || wire_stride > wire_meta.len + { + continue; + } + let segment_count = wire_meta.len.div_ceil(wire_stride); + let Some(nonce_bytes) = segment_count.checked_mul(NONCE_LEN) else { + continue; + }; + let Some(expected_len) = wire_meta.len.checked_sub(nonce_bytes) else { + continue; + }; + let final_wire_len = wire_meta.len % wire_stride; + if expected_len == 0 + || expected_len > bufs[output].len() + || (final_wire_len != 0 && final_wire_len <= NONCE_LEN) + { + continue; + } let target = &mut bufs[output]; - let decoded_stride = wire_stride.saturating_sub(NONCE_LEN); + let decoded_stride = wire_stride - NONCE_LEN; let mut decoded_len = 0; for wire in storage[..wire_meta.len].chunks_mut(wire_stride) { - if wire.len() <= NONCE_LEN { - continue; - } let (nonce, encrypted) = wire.split_at_mut(NONCE_LEN); let nonce: &[u8; NONCE_LEN] = (&*nonce).try_into().expect("fixed nonce prefix"); let mut cipher = ChaCha20::new((&self.key).into(), nonce.into()); @@ -211,18 +242,10 @@ impl AsyncUdpSocket for MorphUdpSocket { .try_apply_keystream(encrypted) .map_err(|_| exhausted())?; let end = decoded_len + encrypted.len(); - if end > target.len() { - return Poll::Ready(Err(io::Error::new( - io::ErrorKind::InvalidData, - "Morph UDP receive buffer overflow", - ))); - } target[decoded_len..end].copy_from_slice(encrypted); decoded_len = end; } - if decoded_len == 0 { - continue; - } + debug_assert_eq!(decoded_len, expected_len); meta[output] = RecvMeta { len: decoded_len, stride: decoded_stride, From bc20074fd71d5ddfff735d53c945da183a4e6131 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Thu, 10 Sep 2026 15:58:24 +0800 Subject: [PATCH 43/62] Keep Morph regression helpers test-local --- src/tests/transport/morph/tcp.rs | 2 +- src/tests/transport/morph/udp.rs | 1 + src/transport/morph.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tests/transport/morph/tcp.rs b/src/tests/transport/morph/tcp.rs index 20c1d59..14b0c58 100644 --- a/src/tests/transport/morph/tcp.rs +++ b/src/tests/transport/morph/tcp.rs @@ -101,7 +101,7 @@ impl AsyncWrite for PendingWriteStream { } } -impl MorphWriteReady for PendingWriteStream { +impl super::super::tcp::MorphWriteReady for PendingWriteStream { fn poll_morph_write_ready(&self, _cx: &mut Context<'_>) -> Poll> { Poll::Pending } diff --git a/src/tests/transport/morph/udp.rs b/src/tests/transport/morph/udp.rs index 3c3b9ca..492af76 100644 --- a/src/tests/transport/morph/udp.rs +++ b/src/tests/transport/morph/udp.rs @@ -11,6 +11,7 @@ use std::task::{Context, Poll}; use quinn::udp::{RecvMeta, Transmit}; use quinn::{AsyncUdpSocket, UdpPoller}; +use super::super::udp::{UDP_NONCE_STREAM_LIMIT, UdpNonceGenerator}; use super::super::*; #[test] diff --git a/src/transport/morph.rs b/src/transport/morph.rs index eac09e8..e432867 100644 --- a/src/transport/morph.rs +++ b/src/transport/morph.rs @@ -79,7 +79,7 @@ fn exhausted() -> io::Error { #[cfg(test)] use tcp::apply_at; #[cfg(test)] -use udp::{MorphUdpSocket, UdpBuffers, generate_nonce}; +use udp::{MorphUdpSocket, UdpBuffers}; #[cfg(test)] #[path = "../tests/transport/morph.rs"] From 03ba09b6e56cb6e4fd7d1390d512a913723e5dbe Mon Sep 17 00:00:00 2001 From: yosebyte Date: Thu, 10 Sep 2026 16:04:41 +0800 Subject: [PATCH 44/62] Fix Morph regression test access and assertions --- src/tests/transport/morph/tcp.rs | 15 +++++++++++++-- src/tests/transport/morph/udp.rs | 10 +++++++--- src/transport/morph/tcp.rs | 7 ------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/tests/transport/morph/tcp.rs b/src/tests/transport/morph/tcp.rs index 14b0c58..642e2f2 100644 --- a/src/tests/transport/morph/tcp.rs +++ b/src/tests/transport/morph/tcp.rs @@ -13,6 +13,12 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf}; use super::super::*; +impl super::super::tcp::MorphWriteReady for tokio::io::DuplexStream { + fn poll_morph_write_ready(&self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + fn hex(value: &str) -> [u8; N] { assert_eq!(value.len(), N * 2); let mut bytes = [0; N]; @@ -116,7 +122,6 @@ fn tcp_waits_for_write_readiness_before_copying_or_xoring() { let keys = MorphKeys::derive(b"shared"); let mut client = MorphTcpStream::client(inner, Some(keys)).unwrap(); set_tcp_nonce(&mut client, [11; NONCE_LEN]); - client.morph.as_mut().unwrap().write_prefix_pos = NONCE_LEN; let waker = Waker::noop(); let mut context = Context::from_waker(waker); @@ -124,7 +129,13 @@ fn tcp_waits_for_write_readiness_before_copying_or_xoring() { Pin::new(&mut client).poll_write(&mut context, b"payload"), Poll::Pending )); - assert_eq!(writes.load(Ordering::Relaxed), 0); + // Only the nonce prefix reaches the underlying stream. + assert_eq!(writes.load(Ordering::Relaxed), 1); + assert!(matches!( + Pin::new(&mut client).poll_write(&mut context, b"payload"), + Poll::Pending + )); + assert_eq!(writes.load(Ordering::Relaxed), 1); assert!(client.morph.as_ref().unwrap().write_buffer.is_empty()); } diff --git a/src/tests/transport/morph/udp.rs b/src/tests/transport/morph/udp.rs index 492af76..e0eafc7 100644 --- a/src/tests/transport/morph/udp.rs +++ b/src/tests/transport/morph/udp.rs @@ -29,21 +29,25 @@ fn udp_nonce_generator_fills_batches_from_one_stream() { #[test] fn udp_nonce_generator_reseeds_before_exhaustion_and_retries_failures() { let mut generator = UdpNonceGenerator::from_seed([3; 32]); - generator.generated = UDP_NONCE_STREAM_LIMIT - NONCE_LEN as u64 + 1; + generator.generated = UDP_NONCE_STREAM_LIMIT; let mut nonce = [9; NONCE_LEN]; let error = generator .generate_with_reseed(&mut nonce, || Err(io::Error::other("no entropy"))) .unwrap_err(); assert_eq!(error.to_string(), "no entropy"); - assert_eq!(generator.generated, UDP_NONCE_STREAM_LIMIT - 11); + assert_eq!(generator.generated, UDP_NONCE_STREAM_LIMIT); assert_eq!(nonce, [9; NONCE_LEN]); generator .generate_with_reseed(&mut nonce, || Ok([4; 32])) .unwrap(); assert_eq!(generator.generated, NONCE_LEN as u64); - assert_ne!(nonce, [9; NONCE_LEN]); + let mut expected = [0; NONCE_LEN]; + UdpNonceGenerator::from_seed([4; 32]) + .generate_with_reseed(&mut expected, || panic!("fresh stream must not reseed")) + .unwrap(); + assert_eq!(nonce, expected); } #[test] diff --git a/src/transport/morph/tcp.rs b/src/transport/morph/tcp.rs index d9a35fe..1ccaf20 100644 --- a/src/transport/morph/tcp.rs +++ b/src/transport/morph/tcp.rs @@ -215,13 +215,6 @@ impl MorphWriteReady for tokio::net::TcpStream { } } -#[cfg(test)] -impl MorphWriteReady for tokio::io::DuplexStream { - fn poll_morph_write_ready(&self, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - impl AsyncWrite for MorphTcpStream { fn poll_write( mut self: Pin<&mut Self>, From 956d268842f932601e5db211d8920afdb68fe002 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Thu, 10 Sep 2026 16:32:43 +0800 Subject: [PATCH 45/62] Fuse Morph TCP copy and transform into reusable buffers --- src/tests/transport/morph/tcp.rs | 115 +++++++++++++++++++++++++++++-- src/transport/morph/tcp.rs | 9 +-- 2 files changed, 115 insertions(+), 9 deletions(-) diff --git a/src/tests/transport/morph/tcp.rs b/src/tests/transport/morph/tcp.rs index 642e2f2..6c8a30e 100644 --- a/src/tests/transport/morph/tcp.rs +++ b/src/tests/transport/morph/tcp.rs @@ -1,10 +1,11 @@ // Copyright (C) 2026 NodePassProject // SPDX-License-Identifier: GPL-3.0-only +use std::collections::VecDeque; use std::io; use std::pin::Pin; use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::task::{Context, Poll, Waker}; use chacha20::ChaCha20; @@ -75,7 +76,17 @@ fn tcp_empty_io_does_not_wait_for_the_nonce() { #[derive(Debug)] struct PendingWriteStream { + ready: Arc, writes: Arc, + written: Arc>>, + outcomes: std::sync::Mutex>, +} + +#[derive(Debug)] +enum WriteOutcome { + Accept(usize), + Pending, + Error(io::ErrorKind), } impl AsyncRead for PendingWriteStream { @@ -91,11 +102,26 @@ impl AsyncRead for PendingWriteStream { impl AsyncWrite for PendingWriteStream { fn poll_write( self: Pin<&mut Self>, - _cx: &mut Context<'_>, + cx: &mut Context<'_>, input: &[u8], ) -> Poll> { self.writes.fetch_add(1, Ordering::Relaxed); - Poll::Ready(Ok(input.len())) + let outcome = self.outcomes.lock().unwrap().pop_front(); + match outcome.unwrap_or(WriteOutcome::Accept(input.len())) { + WriteOutcome::Accept(limit) => { + let count = input.len().min(limit); + self.written + .lock() + .unwrap() + .extend_from_slice(&input[..count]); + Poll::Ready(Ok(count)) + } + WriteOutcome::Pending => { + cx.waker().wake_by_ref(); + Poll::Pending + } + WriteOutcome::Error(kind) => Poll::Ready(Err(kind.into())), + } } fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { @@ -109,15 +135,24 @@ impl AsyncWrite for PendingWriteStream { impl super::super::tcp::MorphWriteReady for PendingWriteStream { fn poll_morph_write_ready(&self, _cx: &mut Context<'_>) -> Poll> { - Poll::Pending + if self.ready.load(Ordering::Relaxed) { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } } } #[test] fn tcp_waits_for_write_readiness_before_copying_or_xoring() { + let ready = Arc::new(AtomicBool::new(false)); let writes = Arc::new(AtomicUsize::new(0)); + let written = Arc::new(std::sync::Mutex::new(Vec::new())); let inner = PendingWriteStream { + ready: ready.clone(), writes: writes.clone(), + written: written.clone(), + outcomes: Default::default(), }; let keys = MorphKeys::derive(b"shared"); let mut client = MorphTcpStream::client(inner, Some(keys)).unwrap(); @@ -126,7 +161,7 @@ fn tcp_waits_for_write_readiness_before_copying_or_xoring() { let mut context = Context::from_waker(waker); assert!(matches!( - Pin::new(&mut client).poll_write(&mut context, b"payload"), + Pin::new(&mut client).poll_write(&mut context, b"initial"), Poll::Pending )); // Only the nonce prefix reaches the underlying stream. @@ -137,6 +172,70 @@ fn tcp_waits_for_write_readiness_before_copying_or_xoring() { )); assert_eq!(writes.load(Ordering::Relaxed), 1); assert!(client.morph.as_ref().unwrap().write_buffer.is_empty()); + + ready.store(true, Ordering::Relaxed); + assert!(matches!( + Pin::new(&mut client).poll_write(&mut context, b"changed"), + Poll::Ready(Ok(7)) + )); + assert_eq!(writes.load(Ordering::Relaxed), 2); + let mut payload = written.lock().unwrap()[NONCE_LEN..].to_vec(); + apply_at( + &client.morph.as_ref().unwrap().write_key, + &[11; NONCE_LEN], + 0, + &mut payload, + ) + .unwrap(); + assert_eq!(&payload, b"changed"); +} + +#[test] +fn tcp_retries_changed_input_after_short_write_pending_and_error() { + let written = Arc::new(std::sync::Mutex::new(Vec::new())); + let inner = PendingWriteStream { + ready: Arc::new(AtomicBool::new(true)), + writes: Arc::new(AtomicUsize::new(0)), + written: written.clone(), + outcomes: std::sync::Mutex::new(VecDeque::from([ + WriteOutcome::Accept(NONCE_LEN), + WriteOutcome::Accept(3), + WriteOutcome::Pending, + WriteOutcome::Error(io::ErrorKind::Interrupted), + WriteOutcome::Accept(usize::MAX), + ])), + }; + let mut client = MorphTcpStream::client(inner, Some(MorphKeys::derive(b"shared"))).unwrap(); + set_tcp_nonce(&mut client, [12; NONCE_LEN]); + let mut context = Context::from_waker(Waker::noop()); + + assert!(matches!( + Pin::new(&mut client).poll_write(&mut context, b"abc-unaccepted"), + Poll::Ready(Ok(3)) + )); + assert!(matches!( + Pin::new(&mut client).poll_write(&mut context, b"pending input"), + Poll::Pending + )); + assert!(matches!( + Pin::new(&mut client).poll_write(&mut context, b"error input"), + Poll::Ready(Err(error)) if error.kind() == io::ErrorKind::Interrupted + )); + assert!(matches!( + Pin::new(&mut client).poll_write(&mut context, b"z"), + Poll::Ready(Ok(1)) + )); + let wire = written.lock().unwrap(); + assert_eq!(&wire[..NONCE_LEN], &[12; NONCE_LEN]); + let mut payload = wire[NONCE_LEN..].to_vec(); + apply_at( + &client.morph.as_ref().unwrap().write_key, + &[12; NONCE_LEN], + 0, + &mut payload, + ) + .unwrap(); + assert_eq!(&payload, b"abcz"); } #[tokio::test] @@ -208,4 +307,10 @@ async fn tcp_write_processes_the_full_available_buffer() { let payload = vec![0x5a; 128 * 1024]; assert_eq!(client.write(&payload).await.unwrap(), payload.len()); + let buffer_len = client.morph.as_ref().unwrap().write_buffer.len(); + assert_eq!(client.write(b"x").await.unwrap(), 1); + assert_eq!( + client.morph.as_ref().unwrap().write_buffer.len(), + buffer_len + ); } diff --git a/src/transport/morph/tcp.rs b/src/transport/morph/tcp.rs index 1ccaf20..fda2083 100644 --- a/src/transport/morph/tcp.rs +++ b/src/transport/morph/tcp.rs @@ -268,15 +268,16 @@ impl AsyncWrite for MorphTcpStream { Poll::Pending => return Poll::Pending, } let state = this.morph.as_mut().unwrap(); - state.write_buffer.clear(); - state.write_buffer.extend_from_slice(&input[..count]); + if state.write_buffer.len() < count { + state.write_buffer.resize(count, 0); + } state .write_cipher .as_mut() .expect("Morph write cipher initialized") - .try_apply_keystream(&mut state.write_buffer) + .try_apply_keystream_b2b(&input[..count], &mut state.write_buffer[..count]) .map_err(|_| exhausted())?; - match Pin::new(&mut this.inner).poll_write(cx, &state.write_buffer) { + match Pin::new(&mut this.inner).poll_write(cx, &state.write_buffer[..count]) { Poll::Ready(Ok(0)) => { state .write_cipher From 96d7f2acc2fb1d33d55e61d2f6cc147ad2b16659 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Thu, 10 Sep 2026 16:32:47 +0800 Subject: [PATCH 46/62] Fuse Morph UDP transforms and reuse initialized buffers --- src/tests/transport/morph/udp.rs | 185 +++++++++++++++++++++++++------ src/transport/morph/udp.rs | 80 ++++++++----- 2 files changed, 208 insertions(+), 57 deletions(-) diff --git a/src/tests/transport/morph/udp.rs b/src/tests/transport/morph/udp.rs index e0eafc7..6d8cc39 100644 --- a/src/tests/transport/morph/udp.rs +++ b/src/tests/transport/morph/udp.rs @@ -65,6 +65,7 @@ fn morph_endpoint_reserves_the_udp_nonce_overhead() { struct FakeUdpSocket { sent: StdMutex, Option)>>, receive: StdMutex, RecvMeta)>>, + receive_lengths: StdMutex>, } #[derive(Debug)] @@ -106,6 +107,7 @@ impl AsyncUdpSocket for FakeUdpSocket { let Some((packet, packet_meta)) = self.receive.lock().unwrap().pop_front() else { return Poll::Pending; }; + self.receive_lengths.lock().unwrap().push(bufs[0].len()); bufs[0][..packet.len()].copy_from_slice(&packet); meta[0] = packet_meta; Poll::Ready(Ok(1)) @@ -133,39 +135,48 @@ async fn udp_preserves_gso_datagram_boundaries() { key, buffers: Mutex::new(UdpBuffers::from_seed([1; 32])), }; - socket - .try_send(&Transmit { - destination: "127.0.0.1:2".parse().unwrap(), - ecn: None, - contents: b"abcdef", - segment_size: Some(3), - src_ip: None, - }) - .unwrap(); - let (wire, stride) = raw.sent.lock().unwrap().pop().unwrap(); - assert_eq!(stride, Some(15)); - assert_eq!(wire.len(), 30); + for plain in [ + b"abcdef".as_slice(), + b"abcde".as_slice(), + b"ghijkl".as_slice(), + ] { + socket + .try_send(&Transmit { + destination: "127.0.0.1:2".parse().unwrap(), + ecn: None, + contents: plain, + segment_size: Some(3), + src_ip: None, + }) + .unwrap(); + let (wire, stride) = raw.sent.lock().unwrap().pop().unwrap(); + assert_eq!(stride, Some(15)); + assert_eq!(wire.len(), plain.len() + 2 * NONCE_LEN); + assert_ne!(&wire[..NONCE_LEN], &wire[15..15 + NONCE_LEN]); - raw.receive.lock().unwrap().push_back(( - wire, - RecvMeta { - addr: "127.0.0.1:2".parse().unwrap(), - len: 30, - stride: 15, - ecn: None, - dst_ip: None, - }, - )); - let mut output = [0u8; 6]; - let mut bufs = [IoSliceMut::new(&mut output)]; - let mut meta = [RecvMeta::default()]; - let count = poll_fn(|cx| socket.poll_recv(cx, &mut bufs, &mut meta)) - .await - .unwrap(); - assert_eq!(count, 1); - assert_eq!(&output, b"abcdef"); - assert_eq!(meta[0].len, 6); - assert_eq!(meta[0].stride, 3); + let wire_len = wire.len(); + raw.receive.lock().unwrap().push_back(( + wire, + RecvMeta { + addr: "127.0.0.1:2".parse().unwrap(), + len: wire_len, + stride: 15, + ecn: None, + dst_ip: None, + }, + )); + let mut output = [0xa7; 6]; + let mut bufs = [IoSliceMut::new(&mut output)]; + let mut meta = [RecvMeta::default()]; + let count = poll_fn(|cx| socket.poll_recv(cx, &mut bufs, &mut meta)) + .await + .unwrap(); + assert_eq!(count, 1); + assert_eq!(&output[..plain.len()], plain); + assert!(output[plain.len()..].iter().all(|byte| *byte == 0xa7)); + assert_eq!(meta[0].len, plain.len()); + assert_eq!(meta[0].stride, 3); + } } #[tokio::test] @@ -236,3 +247,113 @@ async fn udp_discards_invalid_wire_datagrams_before_returning_valid_data() { assert_eq!(&output, b"valid"); assert_eq!(meta[0].len, 5); } + +#[tokio::test] +async fn udp_reuses_buffers_without_relaxing_current_receive_bounds() { + let key = MorphKeys::derive(b"shared").udp_key(); + let raw = Arc::new(FakeUdpSocket::default()); + let socket = MorphUdpSocket { + inner: raw.clone(), + key, + buffers: Mutex::new(UdpBuffers::from_seed([5; 32])), + }; + let destination = "127.0.0.1:2".parse().unwrap(); + let packet_meta = |len| RecvMeta { + addr: destination, + len, + stride: len, + ecn: None, + dst_ip: None, + }; + + socket + .try_send(&Transmit { + destination, + ecn: None, + contents: &[0x5a; 32], + segment_size: None, + src_ip: None, + }) + .unwrap(); + let (large_wire, _) = raw.sent.lock().unwrap().pop().unwrap(); + let large_wire_len = large_wire.len(); + raw.receive + .lock() + .unwrap() + .push_back((large_wire, packet_meta(large_wire_len))); + let mut large_output = [0; 32]; + let mut large_bufs = [IoSliceMut::new(&mut large_output)]; + let mut large_meta = [RecvMeta::default()]; + poll_fn(|cx| socket.poll_recv(cx, &mut large_bufs, &mut large_meta)) + .await + .unwrap(); + assert_eq!(large_output, [0x5a; 32]); + + socket + .try_send(&Transmit { + destination, + ecn: None, + contents: b"x", + segment_size: None, + src_ip: None, + }) + .unwrap(); + let (small_wire, _) = raw.sent.lock().unwrap().pop().unwrap(); + assert_eq!(small_wire.len(), NONCE_LEN + 1); + // Three one-byte GRO payloads would fit the decoded target, but the + // claimed wire length exceeds this call's receive slice (4 + 2 * 12). + raw.receive.lock().unwrap().push_back(( + vec![0; NONCE_LEN], + RecvMeta { + len: 3 * (NONCE_LEN + 1), + stride: NONCE_LEN + 1, + ..packet_meta(0) + }, + )); + let small_wire_len = small_wire.len(); + raw.receive + .lock() + .unwrap() + .push_back((small_wire, packet_meta(small_wire_len))); + let mut small_output = [0xa7; 4]; + let mut small_bufs = [IoSliceMut::new(&mut small_output)]; + let mut small_meta = [RecvMeta::default()]; + poll_fn(|cx| socket.poll_recv(cx, &mut small_bufs, &mut small_meta)) + .await + .unwrap(); + assert_eq!(small_meta[0].len, 1); + assert_eq!(small_output, [b'x', 0xa7, 0xa7, 0xa7]); + + socket + .try_send(&Transmit { + destination, + ecn: None, + contents: &[0xa5; 48], + segment_size: None, + src_ip: None, + }) + .unwrap(); + let (larger_wire, _) = raw.sent.lock().unwrap().pop().unwrap(); + assert_eq!(larger_wire.len(), NONCE_LEN + 48); + let larger_wire_len = larger_wire.len(); + raw.receive + .lock() + .unwrap() + .push_back((larger_wire, packet_meta(larger_wire_len))); + let mut larger_output = [0; 48]; + let mut larger_bufs = [IoSliceMut::new(&mut larger_output)]; + let mut larger_meta = [RecvMeta::default()]; + poll_fn(|cx| socket.poll_recv(cx, &mut larger_bufs, &mut larger_meta)) + .await + .unwrap(); + assert_eq!(larger_output, [0xa5; 48]); + assert_eq!( + *raw.receive_lengths.lock().unwrap(), + [ + 32 + 2 * NONCE_LEN, + 4 + 2 * NONCE_LEN, + 4 + 2 * NONCE_LEN, + 48 + 2 * NONCE_LEN + ] + ); +} diff --git a/src/transport/morph/udp.rs b/src/transport/morph/udp.rs index 00f9377..2c39fdb 100644 --- a/src/transport/morph/udp.rs +++ b/src/transport/morph/udp.rs @@ -142,31 +142,48 @@ impl AsyncUdpSocket for MorphUdpSocket { )); } let segments = transmit.contents.len().div_ceil(plain_stride); + let wire_len = transmit + .contents + .len() + .checked_add( + segments + .checked_mul(NONCE_LEN) + .ok_or_else(|| io::Error::other("Morph UDP datagram length overflow"))?, + ) + .ok_or_else(|| io::Error::other("Morph UDP datagram length overflow"))?; let mut buffers = self.buffers.lock().unwrap_or_else(|lock| lock.into_inner()); let UdpBuffers { send, nonce_generator, .. } = &mut *buffers; - send.clear(); - send.reserve(transmit.contents.len() + segments * NONCE_LEN); + if send.len() < wire_len { + send.resize(wire_len, 0); + } + let mut wire_offset = 0; for plain in transmit.contents.chunks(plain_stride) { - let start = send.len(); - send.resize(start + NONCE_LEN + plain.len(), 0); - let (nonce, payload) = send[start..].split_at_mut(NONCE_LEN); + let wire_end = wire_offset + NONCE_LEN + plain.len(); + let (nonce, payload) = send[wire_offset..wire_end].split_at_mut(NONCE_LEN); let nonce: &mut [u8; NONCE_LEN] = nonce.try_into().expect("fixed nonce prefix"); nonce_generator.generate(nonce)?; - payload.copy_from_slice(plain); let mut cipher = ChaCha20::new((&self.key).into(), (&*nonce).into()); cipher - .try_apply_keystream(payload) + .try_apply_keystream_b2b(plain, payload) .map_err(|_| exhausted())?; + wire_offset = wire_end; } - let wire_stride = transmit.segment_size.map(|size| size + NONCE_LEN); + debug_assert_eq!(wire_offset, wire_len); + let wire_stride = transmit + .segment_size + .map(|size| { + size.checked_add(NONCE_LEN) + .ok_or_else(|| io::Error::other("Morph UDP segment length overflow")) + }) + .transpose()?; let wire = Transmit { destination: transmit.destination, ecn: transmit.ecn, - contents: send, + contents: &send[..wire_len], segment_size: wire_stride, src_ip: transmit.src_ip, }; @@ -185,17 +202,31 @@ impl AsyncUdpSocket for MorphUdpSocket { } let max_segments = self.inner.max_receive_segments().max(1); let mut buffers = self.buffers.lock().unwrap_or_else(|lock| lock.into_inner()); - for (storage, target) in buffers - .receive - .iter_mut() - .zip(bufs.iter()) - .take(receive_count) - { - storage.resize(target.len() + NONCE_LEN * max_segments, 0); + let Some(nonce_overhead) = NONCE_LEN.checked_mul(max_segments) else { + return Poll::Ready(Err(io::Error::other( + "Morph UDP receive buffer length overflow", + ))); + }; + let mut wire_lengths = [0; quinn::udp::BATCH_SIZE]; + for index in 0..receive_count { + let Some(wire_len) = bufs[index].len().checked_add(nonce_overhead) else { + return Poll::Ready(Err(io::Error::other( + "Morph UDP receive buffer length overflow", + ))); + }; + wire_lengths[index] = wire_len; + if buffers.receive[index].len() < wire_len { + buffers.receive[index].resize(wire_len, 0); + } } for _ in 0..MAX_INVALID_RECEIVE_BATCHES { let receive = buffers.receive.each_mut(); - let mut wire_bufs = receive.map(|value| IoSliceMut::new(value)); + let mut index = 0; + let mut wire_bufs = receive.map(|value| { + let wire_len = wire_lengths[index]; + index += 1; + IoSliceMut::new(&mut value[..wire_len]) + }); let received = match self.inner.poll_recv( cx, &mut wire_bufs[..receive_count], @@ -209,9 +240,9 @@ impl AsyncUdpSocket for MorphUdpSocket { for index in 0..received { let wire_meta = meta[index]; let wire_stride = wire_meta.stride; - let storage = &mut buffers.receive[index]; + let storage = &buffers.receive[index]; if wire_meta.len == 0 - || wire_meta.len > storage.len() + || wire_meta.len > wire_lengths[index] || wire_stride <= NONCE_LEN || wire_stride > wire_meta.len { @@ -234,15 +265,14 @@ impl AsyncUdpSocket for MorphUdpSocket { let target = &mut bufs[output]; let decoded_stride = wire_stride - NONCE_LEN; let mut decoded_len = 0; - for wire in storage[..wire_meta.len].chunks_mut(wire_stride) { - let (nonce, encrypted) = wire.split_at_mut(NONCE_LEN); - let nonce: &[u8; NONCE_LEN] = (&*nonce).try_into().expect("fixed nonce prefix"); + for wire in storage[..wire_meta.len].chunks(wire_stride) { + let (nonce, encrypted) = wire.split_at(NONCE_LEN); + let nonce: &[u8; NONCE_LEN] = nonce.try_into().expect("fixed nonce prefix"); let mut cipher = ChaCha20::new((&self.key).into(), nonce.into()); + let end = decoded_len + encrypted.len(); cipher - .try_apply_keystream(encrypted) + .try_apply_keystream_b2b(encrypted, &mut target[decoded_len..end]) .map_err(|_| exhausted())?; - let end = decoded_len + encrypted.len(); - target[decoded_len..end].copy_from_slice(encrypted); decoded_len = end; } debug_assert_eq!(decoded_len, expected_len); From 74810202aaffac145a098aa1c0c4bb4896b31f55 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Thu, 10 Sep 2026 16:32:52 +0800 Subject: [PATCH 47/62] Document Morph buffer reuse and fused transforms --- docs/operations.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/operations.md b/docs/operations.md index 53bf429..01e01ae 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -77,8 +77,10 @@ Morph adds 12 bytes once per TCP connection and 12 bytes to every UDP datagram. It preserves TCP payload length and keeps GSO/GRO batching when the platform provides it. UDP socket buffers reserve space for the outer nonce; Quinn measures decoded QUIC datagram sizes and performs path MTU discovery with -12 bytes reserved for the outer nonce. UDP nonce batches come from a user-space -CSPRNG seeded from the operating system and reseeded before stream exhaustion. +12 bytes reserved for the outer nonce. Morph reuses initialized transport +buffers and applies ChaCha20 while copying between caller and wire buffers. UDP +nonce batches come from a user-space CSPRNG seeded from the operating system and +reseeded before stream exhaustion. ### TLS Shard placement From 54ddf4412eb7a70c98c9e081b796b15ff36ab72f Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 09:04:51 +0800 Subject: [PATCH 48/62] Constrain V2 flow IDs to 30 bits --- src/protocol/flow.rs | 18 ++++----- src/protocol/mod.rs | 2 +- src/tests/portal/conn/asymmetric.rs | 2 +- src/tests/portal/conn/quic.rs | 57 +++++++++++++++++------------ src/tests/portal/conn/tcp.rs | 33 ++++++++++------- src/tests/protocol/flow.rs | 33 ++++++++++++++++- src/tests/vector/flow.rs | 4 +- src/tests/vector/flow_id.rs | 30 +++++++++++++-- src/vector/flow.rs | 6 +-- src/vector/flow_id.rs | 22 ++++++----- 10 files changed, 139 insertions(+), 68 deletions(-) diff --git a/src/protocol/flow.rs b/src/protocol/flow.rs index 27efd68..4f93b7a 100644 --- a/src/protocol/flow.rs +++ b/src/protocol/flow.rs @@ -22,6 +22,8 @@ pub const MAX_PORTAL_HOPS: u8 = 7; pub type SessionId = [u8; SESSION_ID_LEN]; /// Flow identifier scoped to one logical session. pub type FlowId = u32; +/// Largest logical-flow identifier representable by every V2 carrier. +pub const MAX_FLOW_ID: FlowId = 0x3fff_ffff; /// Relationship of the current physical lane to a logical flow. #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -70,8 +72,8 @@ impl FlowHeader { if self.hops > MAX_PORTAL_HOPS { bail!("protocol::flow::FlowHeader::validate: hop budget exceeds {MAX_PORTAL_HOPS}") } - if self.flow_id == 0 { - bail!("protocol::flow::FlowHeader::validate: zero flow id"); + if self.flow_id == 0 || self.flow_id > MAX_FLOW_ID { + bail!("protocol::flow::FlowHeader::validate: flow id out of range"); } match self.role { FlowRole::Duplex if self.uplink != self.downlink => { @@ -102,16 +104,14 @@ impl FlowHeader { /// Encodes a header after validating its semantic invariants. pub fn encode_flow_header(header: FlowHeader) -> Result<[u8; FLOW_HEADER_LEN]> { - header.validate()?; - Ok(write_flow_header(header)) + write_flow_header(header) } /// Encodes a flow header into a fixed stack array. /// -/// Callers accepting untrusted or dynamically assembled metadata should use -/// [`encode_flow_header`] first. This low-level spelling remains allocation-free -/// and matches the existing request-building call sites. -pub fn write_flow_header(header: FlowHeader) -> [u8; FLOW_HEADER_LEN] { +/// Validates the same semantic invariants as [`encode_flow_header`]. +pub fn write_flow_header(header: FlowHeader) -> Result<[u8; FLOW_HEADER_LEN]> { + header.validate()?; let flags = header.role as u8 | (header.kind as u8) << 2 | (header.uplink as u8) << 3 @@ -120,7 +120,7 @@ pub fn write_flow_header(header: FlowHeader) -> [u8; FLOW_HEADER_LEN] { let mut output = [0; FLOW_HEADER_LEN]; output[0] = flags; output[1..].copy_from_slice(&header.flow_id.to_be_bytes()); - output + Ok(output) } /// Decodes exactly one fixed flow header. diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 5311ec7..4a59b43 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -24,7 +24,7 @@ pub use datagram::{ encode_udp_fragments, }; pub use flow::{ - Carrier, FLOW_HEADER_LEN, FlowHeader, FlowId, FlowKind, FlowRole, MAX_PORTAL_HOPS, + Carrier, FLOW_HEADER_LEN, FlowHeader, FlowId, FlowKind, FlowRole, MAX_FLOW_ID, MAX_PORTAL_HOPS, SESSION_ID_LEN, SessionId, decode_flow_header, encode_flow_header, read_flow_header, write_flow_header, }; diff --git a/src/tests/portal/conn/asymmetric.rs b/src/tests/portal/conn/asymmetric.rs index 48fa1b4..656c76d 100644 --- a/src/tests/portal/conn/asymmetric.rs +++ b/src/tests/portal/conn/asymmetric.rs @@ -112,7 +112,7 @@ async fn authenticate_quic(portal: &Portal, conn: &quinn::Connection, session: [ } fn request(header: FlowHeader, target: SocketAddr, payload: &[u8]) -> Vec { - let mut out = write_flow_header(header).to_vec(); + let mut out = write_flow_header(header).unwrap().to_vec(); if matches!(header.role, FlowRole::Open | FlowRole::Duplex) { out.extend_from_slice(&write_request_frame(&Target::ip(target).unwrap()).unwrap()); } diff --git a/src/tests/portal/conn/quic.rs b/src/tests/portal/conn/quic.rs index 2ff9781..face744 100644 --- a/src/tests/portal/conn/quic.rs +++ b/src/tests/portal/conn/quic.rs @@ -42,14 +42,17 @@ async fn setup_quic_udp( target: &str, ) -> (FlowResult, quinn::RecvStream) { let (mut send, mut recv) = connection.open_bi().await.unwrap(); - send.write_all(&write_flow_header(FlowHeader { - role: FlowRole::Duplex, - flow_id, - kind: FlowKind::Udp, - uplink: Carrier::Quic, - downlink: Carrier::Quic, - hops: 0, - })) + send.write_all( + &write_flow_header(FlowHeader { + role: FlowRole::Duplex, + flow_id, + kind: FlowKind::Udp, + uplink: Carrier::Quic, + downlink: Carrier::Quic, + hops: 0, + }) + .unwrap(), + ) .await .unwrap(); send.write_all(&write_request_frame(&test_target(target)).unwrap()) @@ -141,14 +144,17 @@ async fn quic_carrier_mismatch_returns_invalid_request() { authenticate_test_connection(&portal, &connection).await; let (mut send, mut recv) = connection.open_bi().await.unwrap(); - send.write_all(&write_flow_header(FlowHeader { - role: FlowRole::Duplex, - flow_id: 76, - kind: FlowKind::Tcp, - uplink: Carrier::TlsTcp, - downlink: Carrier::TlsTcp, - hops: 0, - })) + send.write_all( + &write_flow_header(FlowHeader { + role: FlowRole::Duplex, + flow_id: 76, + kind: FlowKind::Tcp, + uplink: Carrier::TlsTcp, + downlink: Carrier::TlsTcp, + hops: 0, + }) + .unwrap(), + ) .await .unwrap(); send.finish().unwrap(); @@ -181,14 +187,17 @@ async fn first_stream_carries_auth_and_flow_while_pre_auth_datagrams_are_dropped send.write_all(&quic_auth_frame(&portal, &connection, [78; 16])) .await .unwrap(); - send.write_all(&write_flow_header(FlowHeader { - role: FlowRole::Duplex, - flow_id: 78, - kind: FlowKind::Udp, - uplink: Carrier::Quic, - downlink: Carrier::Quic, - hops: 0, - })) + send.write_all( + &write_flow_header(FlowHeader { + role: FlowRole::Duplex, + flow_id: 78, + kind: FlowKind::Udp, + uplink: Carrier::Quic, + downlink: Carrier::Quic, + hops: 0, + }) + .unwrap(), + ) .await .unwrap(); send.write_all(&write_request_frame(&test_target(&target_addr)).unwrap()) diff --git a/src/tests/portal/conn/tcp.rs b/src/tests/portal/conn/tcp.rs index 18af6b6..cb0423e 100644 --- a/src/tests/portal/conn/tcp.rs +++ b/src/tests/portal/conn/tcp.rs @@ -38,6 +38,7 @@ fn duplex_setup(flow_id: u32, kind: FlowKind, target: &str) -> Vec { downlink: Carrier::TlsTcp, hops: 0, }) + .unwrap() .to_vec(); setup.extend_from_slice(&write_request_frame(&test_target(target)).unwrap()); setup @@ -509,14 +510,17 @@ async fn tls_tcp_carrier_mismatch_returns_invalid_request() { let mut tls = connect_test_tls(listen_addr).await; let mut bootstrap = tls_auth_frame(&portal, &tls, [11; 16]).to_vec(); - bootstrap.extend_from_slice(&write_flow_header(FlowHeader { - role: FlowRole::Duplex, - flow_id: 11, - kind: FlowKind::Tcp, - uplink: Carrier::Quic, - downlink: Carrier::Quic, - hops: 0, - })); + bootstrap.extend_from_slice( + &write_flow_header(FlowHeader { + role: FlowRole::Duplex, + flow_id: 11, + kind: FlowKind::Tcp, + uplink: Carrier::Quic, + downlink: Carrier::Quic, + hops: 0, + }) + .unwrap(), + ); tls.write_all(&bootstrap).await.unwrap(); assert_eq!( @@ -569,7 +573,7 @@ async fn mismatched_open_leaves_invalid_request_for_later_attach() { }; let mut first = connect_test_tls(listen_addr).await; let mut bootstrap = tls_auth_frame(&portal, &first, session_id).to_vec(); - bootstrap.extend_from_slice(&write_flow_header(open)); + bootstrap.extend_from_slice(&write_flow_header(open).unwrap()); first.write_all(&bootstrap).await.unwrap(); let mut eof = [0u8; 1]; match first.read(&mut eof).await { @@ -580,10 +584,13 @@ async fn mismatched_open_leaves_invalid_request_for_later_attach() { let mut second = connect_test_tls(listen_addr).await; let mut attach = tls_auth_frame(&portal, &second, session_id).to_vec(); - attach.extend_from_slice(&write_flow_header(FlowHeader { - role: FlowRole::Attach, - ..open - })); + attach.extend_from_slice( + &write_flow_header(FlowHeader { + role: FlowRole::Attach, + ..open + }) + .unwrap(), + ); second.write_all(&attach).await.unwrap(); assert_eq!( read_flow_result(&mut second).await.unwrap(), diff --git a/src/tests/protocol/flow.rs b/src/tests/protocol/flow.rs index 4d57e1f..c2bd669 100644 --- a/src/tests/protocol/flow.rs +++ b/src/tests/protocol/flow.rs @@ -61,7 +61,7 @@ fn every_valid_role_kind_and_carrier_combination_round_trips() { for role in [FlowRole::Open, FlowRole::Attach] { round_trip(FlowHeader { role, - flow_id: u32::MAX, + flow_id: MAX_FLOW_ID, kind, uplink: Carrier::TlsTcp, downlink: Carrier::Quic, @@ -80,7 +80,7 @@ fn every_valid_role_kind_and_carrier_combination_round_trips() { } #[test] -fn semantic_validation_rejects_zero_ids_and_duplex_carrier_conflicts() { +fn semantic_validation_rejects_out_of_range_ids_and_duplex_carrier_conflicts() { let duplex = FlowHeader { role: FlowRole::Duplex, flow_id: 1, @@ -110,6 +110,14 @@ fn semantic_validation_rejects_zero_ids_and_duplex_carrier_conflicts() { hops: 0, }; assert!(encode_flow_header(zero).is_err()); + + assert!( + encode_flow_header(FlowHeader { + flow_id: MAX_FLOW_ID + 1, + ..zero + }) + .is_err() + ); } #[test] @@ -141,6 +149,11 @@ fn decoder_rejects_invalid_ids_lengths_and_semantics() { assert!(decode_flow_header(input).is_err()); } assert!(decode_flow_header(&[0, 0, 0, 0, 0]).is_err()); + for id in [MAX_FLOW_ID + 1, u32::MAX] { + let mut bytes = [0; FLOW_HEADER_LEN]; + bytes[1..].copy_from_slice(&id.to_be_bytes()); + assert!(decode_flow_header(&bytes).is_err()); + } let valid = [0, 0, 0, 0, 1]; let mut invalid_role = valid; @@ -193,3 +206,19 @@ fn round_trip(header: FlowHeader) { assert_eq!(encoded.len(), FLOW_HEADER_LEN); assert_eq!(decode_flow_header(&encoded).unwrap(), header); } + +#[test] +fn both_public_encoders_reject_invalid_ids() { + for flow_id in [0, MAX_FLOW_ID + 1, u32::MAX] { + let header = FlowHeader { + role: FlowRole::Duplex, + flow_id, + kind: FlowKind::Tcp, + uplink: Carrier::TlsTcp, + downlink: Carrier::TlsTcp, + hops: 0, + }; + assert!(write_flow_header(header).is_err()); + assert!(encode_flow_header(header).is_err()); + } +} diff --git a/src/tests/vector/flow.rs b/src/tests/vector/flow.rs index 5eb9feb..9429818 100644 --- a/src/tests/vector/flow.rs +++ b/src/tests/vector/flow.rs @@ -64,7 +64,7 @@ async fn cold_lane_coalesces_auth_flow_and_target() { assert_eq!(&wire[..AUTH_FRAME_LEN], &auth); assert_eq!( &wire[AUTH_FRAME_LEN..AUTH_FRAME_LEN + FLOW_HEADER_LEN], - &write_flow_header(header) + &write_flow_header(header).unwrap() ); assert_eq!(&wire[AUTH_FRAME_LEN + FLOW_HEADER_LEN..], encoded_target); } @@ -89,7 +89,7 @@ async fn cold_attach_lane_coalesces_auth_and_flow_header() { let mut wire = Vec::new(); reader.read_to_end(&mut wire).await.unwrap(); assert_eq!(&wire[..AUTH_FRAME_LEN], &auth); - assert_eq!(&wire[AUTH_FRAME_LEN..], &write_flow_header(header)); + assert_eq!(&wire[AUTH_FRAME_LEN..], &write_flow_header(header).unwrap()); } #[tokio::test] diff --git a/src/tests/vector/flow_id.rs b/src/tests/vector/flow_id.rs index 77a2a96..0cc2d4d 100644 --- a/src/tests/vector/flow_id.rs +++ b/src/tests/vector/flow_id.rs @@ -1,5 +1,27 @@ use super::*; +#[test] +fn exhausted_allocator_recovers_only_released_ids() { + let allocator = FlowIdAllocator::new(); + let first = allocator.allocate_with_limit(3).unwrap(); + let second = allocator.allocate_with_limit(3).unwrap(); + let third = allocator.allocate_with_limit(3).unwrap(); + assert!( + allocator + .allocate_with_limit(3) + .unwrap_err() + .to_string() + .contains("space exhausted") + ); + let released = second.id(); + drop(second); + let reused = allocator.allocate_with_limit(3).unwrap(); + assert_eq!(reused.id(), released); + assert_ne!(reused.id(), first.id()); + assert_ne!(reused.id(), third.id()); + assert!(allocator.allocate_with_limit(3).is_err()); +} + #[test] fn allocator_never_reuses_an_active_id() { let allocator = FlowIdAllocator::new(); @@ -18,9 +40,11 @@ fn allocator_never_reuses_an_active_id() { #[test] fn allocator_skips_zero_at_wrap() { let allocator = FlowIdAllocator::new(); - allocator.next.store(u32::MAX, Ordering::Relaxed); + let first = allocator.allocate().unwrap(); + assert_eq!(first.id(), 1); + allocator.next.store(MAX_FLOW_ID, Ordering::Relaxed); let max = allocator.allocate().unwrap(); let wrapped = allocator.allocate().unwrap(); - assert_eq!(max.id(), u32::MAX); - assert_ne!(wrapped.id(), 0); + assert_eq!(max.id(), MAX_FLOW_ID); + assert_eq!(wrapped.id(), 2); } diff --git a/src/vector/flow.rs b/src/vector/flow.rs index 5153547..47cc9cc 100644 --- a/src/vector/flow.rs +++ b/src/vector/flow.rs @@ -271,8 +271,7 @@ pub(super) async fn write_open_request( header: FlowHeader, target: &Target, ) -> Result<()> { - header.validate()?; - let flow = write_flow_header(header); + let flow = write_flow_header(header)?; let mut request = [0u8; AUTH_FRAME_LEN + FLOW_HEADER_LEN + TARGET_MAX_ENCODED_LEN]; let auth_len = if let Some(auth) = pending_auth { request[..AUTH_FRAME_LEN].copy_from_slice(&auth); @@ -300,8 +299,7 @@ pub(super) async fn write_header( pending_auth: Option, header: FlowHeader, ) -> Result<()> { - header.validate()?; - let flow = write_flow_header(header); + let flow = write_flow_header(header)?; let mut request = [0u8; AUTH_FRAME_LEN + FLOW_HEADER_LEN]; let auth_len = if let Some(auth) = pending_auth { request[..AUTH_FRAME_LEN].copy_from_slice(&auth); diff --git a/src/vector/flow_id.rs b/src/vector/flow_id.rs index 8c76247..cdbfb74 100644 --- a/src/vector/flow_id.rs +++ b/src/vector/flow_id.rs @@ -1,7 +1,7 @@ // Copyright (C) 2026 NodePassProject // SPDX-License-Identifier: GPL-3.0-only -//! Collision-free `u32` flow identifier allocation. +//! Collision-free allocation within the shared 30-bit flow identifier space. use std::collections::HashSet; use std::sync::atomic::{AtomicU32, Ordering}; @@ -9,7 +9,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{Result, bail}; -use crate::protocol::FlowId; +use crate::protocol::{FlowId, MAX_FLOW_ID}; #[derive(Debug)] pub(super) struct FlowIdAllocator { @@ -26,17 +26,21 @@ impl FlowIdAllocator { } pub(super) fn allocate(self: &Arc) -> Result { + self.allocate_with_limit(MAX_FLOW_ID) + } + + fn allocate_with_limit(self: &Arc, max_id: FlowId) -> Result { let mut active = self.active.lock().unwrap_or_else(|lock| lock.into_inner()); - if active.len() == u32::MAX as usize { + if active.len() == max_id as usize { bail!("vector::flow_id: flow identifier space exhausted"); } for _ in 0..=active.len() { - let id = self.next.fetch_add(1, Ordering::Relaxed); - let id = if id == 0 { - self.next.fetch_add(1, Ordering::Relaxed) - } else { - id - }; + let id = self + .next + .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| { + Some(if id >= max_id { 1 } else { id + 1 }) + }) + .unwrap_or_else(|id| id); if id != 0 && active.insert(id) { return Ok(FlowLease { id, From e899f6eae5b319ca5fb046b7148f91ab1a457ab7 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 09:05:00 +0800 Subject: [PATCH 49/62] Shrink Mux frame headers to seven bytes --- src/mux/driver.rs | 15 +++++---- src/mux/mod.rs | 8 ++++- src/mux/wire.rs | 59 +++++++++++++++++------------------- src/tests/mux/runtime.rs | 16 +++++++++- src/tests/mux/wire.rs | 53 +++++++++++++++++++++++++++++--- src/tests/portal/conn/tcp.rs | 4 +-- 6 files changed, 107 insertions(+), 48 deletions(-) diff --git a/src/mux/driver.rs b/src/mux/driver.rs index ff312bb..ec63158 100644 --- a/src/mux/driver.rs +++ b/src/mux/driver.rs @@ -9,8 +9,7 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio::sync::mpsc; use super::wire::{ - CLOSE_FIN, CLOSE_RESET, FlowId, FrameHeader, FrameKind, HEADER_LEN, decode_header, - encode_header, + CLOSE_FIN, FlowId, FrameHeader, FrameKind, HEADER_LEN, decode_header, encode_header, }; use super::{Inbound, MuxChunk, Outbound, Shared}; @@ -65,7 +64,7 @@ pub(super) async fn run_reader(mut reader: R, shared: Arc< let header = decode_header(&encoded).map_err(invalid)?; let payload_len = match header.kind { FrameKind::Data => header.value as usize, - FrameKind::Open | FrameKind::Window | FrameKind::Close => 0, + FrameKind::Open | FrameKind::Window | FrameKind::Fin | FrameKind::Reset => 0, }; let mut payload = vec![0; payload_len]; if payload_len != 0 { @@ -78,7 +77,7 @@ pub(super) async fn run_reader(mut reader: R, shared: Arc< FrameKind::Open => receive_open(&shared, header).await?, FrameKind::Data => receive_data(&shared, header, Bytes::from(payload)).await?, FrameKind::Window => receive_window(&shared, header)?, - FrameKind::Close => receive_close(&shared, header).await, + FrameKind::Fin | FrameKind::Reset => receive_close(&shared, header).await, } if payload_len != 0 { data_frames = data_frames.wrapping_add(1); @@ -126,7 +125,7 @@ async fn receive_data(shared: &Arc, header: FrameHeader, payload: Bytes) } async fn receive_close(shared: &Shared, header: FrameHeader) { - if header.code == CLOSE_RESET { + if header.kind == FrameKind::Reset { if let Some(flow) = shared.remove_flow(header.flow_id) { let _ = flow.inbound.send(Inbound::Reset); } @@ -210,8 +209,8 @@ pub(super) async fn run_writer( shared: Arc, mut data_rx: mpsc::Receiver, ) { - let mut control = Vec::with_capacity(8 * 64); - let mut headers = Vec::with_capacity(8 * 256); + let mut control = Vec::with_capacity(HEADER_LEN * 64); + let mut headers = Vec::with_capacity(HEADER_LEN * 256); let mut pending_item = None; let operation = async { loop { @@ -244,7 +243,7 @@ pub(super) async fn run_writer( Outbound::Control(header) => { headers.clear(); headers.extend_from_slice(&encode_header(header).map_err(invalid)?); - while headers.len() < 8 * 256 { + while headers.len() < HEADER_LEN * 256 { let Ok(next) = data_rx.try_recv() else { break }; match next { Outbound::Control(header) => { diff --git a/src/mux/mod.rs b/src/mux/mod.rs index a873763..2122aa3 100644 --- a/src/mux/mod.rs +++ b/src/mux/mod.rs @@ -215,7 +215,13 @@ impl Shared { flow_id: FlowId, advertise_window: bool, ) -> io::Result { - if flow_id == 0 || self.closed.load(Ordering::Acquire) { + if flow_id == 0 || flow_id > crate::protocol::MAX_FLOW_ID { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "flow ID is outside the 30-bit range", + )); + } + if self.closed.load(Ordering::Acquire) { return Err(closed()); } // Every DATA frame consumes at least one KiB of connection credit, diff --git a/src/mux/wire.rs b/src/mux/wire.rs index b85b149..2e9c310 100644 --- a/src/mux/wire.rs +++ b/src/mux/wire.rs @@ -4,8 +4,10 @@ use std::error::Error; use std::fmt; +use crate::protocol::MAX_FLOW_ID; + pub(super) type FlowId = u32; -pub(super) const HEADER_LEN: usize = 8; +pub(super) const HEADER_LEN: usize = 7; pub(super) const CLOSE_FIN: u8 = 0; pub(super) const CLOSE_RESET: u8 = 1; @@ -16,7 +18,8 @@ pub(super) enum FrameKind { Open = 0x01, Data = 0x02, Window = 0x03, - Close = 0x04, + Fin = 0x04, + Reset = 0x05, } impl TryFrom for FrameKind { @@ -27,7 +30,8 @@ impl TryFrom for FrameKind { 0x01 => Ok(Self::Open), 0x02 => Ok(Self::Data), 0x03 => Ok(Self::Window), - 0x04 => Ok(Self::Close), + 0x04 => Ok(Self::Fin), + 0x05 => Ok(Self::Reset), _ => Err(WireError::UnknownKind(value)), } } @@ -36,33 +40,36 @@ impl TryFrom for FrameKind { #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(super) struct FrameHeader { pub kind: FrameKind, - pub code: u8, pub value: u16, pub flow_id: FlowId, } impl FrameHeader { pub fn open(flow_id: FlowId, window_extension: usize) -> Result { - Self::new(FrameKind::Open, 0, window_extension, flow_id) + Self::new(FrameKind::Open, window_extension, flow_id) } pub fn data(flow_id: FlowId, payload_len: usize) -> Result { - Self::new(FrameKind::Data, 0, payload_len, flow_id) + Self::new(FrameKind::Data, payload_len, flow_id) } pub fn window(flow_id: FlowId, credit: usize) -> Result { - Self::new(FrameKind::Window, 0, credit, flow_id) + Self::new(FrameKind::Window, credit, flow_id) } pub fn close(flow_id: FlowId, code: u8) -> Result { - Self::new(FrameKind::Close, code, 0, flow_id) + let kind = match code { + CLOSE_FIN => FrameKind::Fin, + CLOSE_RESET => FrameKind::Reset, + _ => return Err(WireError::InvalidClose), + }; + Self::new(kind, 0, flow_id) } - fn new(kind: FrameKind, code: u8, value: usize, flow_id: FlowId) -> Result { + fn new(kind: FrameKind, value: usize, flow_id: FlowId) -> Result { let value = u16::try_from(value).map_err(|_| WireError::ValueTooLarge)?; let header = Self { kind, - code, value, flow_id, }; @@ -74,30 +81,24 @@ impl FrameHeader { match self.kind { FrameKind::Open => { require_flow(self.flow_id)?; - if self.code != 0 { - return Err(WireError::ReservedCode); - } } FrameKind::Data => { require_flow(self.flow_id)?; - if self.code != 0 { - return Err(WireError::ReservedCode); - } if self.value == 0 { return Err(WireError::InvalidData); } } FrameKind::Window => { - if self.code != 0 { - return Err(WireError::ReservedCode); - } if self.value == 0 { return Err(WireError::InvalidWindow); } + if self.flow_id != 0 { + require_flow(self.flow_id)?; + } } - FrameKind::Close => { + FrameKind::Fin | FrameKind::Reset => { require_flow(self.flow_id)?; - if self.code > CLOSE_RESET || self.value != 0 { + if self.value != 0 { return Err(WireError::InvalidClose); } } @@ -107,7 +108,7 @@ impl FrameHeader { } fn require_flow(flow_id: FlowId) -> Result<(), WireError> { - if flow_id == 0 { + if flow_id == 0 || flow_id > MAX_FLOW_ID { Err(WireError::InvalidFlowId) } else { Ok(()) @@ -118,9 +119,8 @@ pub(super) fn encode_header(header: FrameHeader) -> Result<[u8; HEADER_LEN], Wir header.validate()?; let mut output = [0; HEADER_LEN]; output[0] = header.kind as u8; - output[1] = header.code; - output[2..4].copy_from_slice(&header.value.to_be_bytes()); - output[4..8].copy_from_slice(&header.flow_id.to_be_bytes()); + output[1..3].copy_from_slice(&header.value.to_be_bytes()); + output[3..7].copy_from_slice(&header.flow_id.to_be_bytes()); Ok(output) } @@ -130,9 +130,8 @@ pub(super) fn decode_header(input: &[u8]) -> Result { } let header = FrameHeader { kind: FrameKind::try_from(input[0])?, - code: input[1], - value: u16::from_be_bytes([input[2], input[3]]), - flow_id: u32::from_be_bytes(input[4..8].try_into().expect("fixed flow ID")), + value: u16::from_be_bytes([input[1], input[2]]), + flow_id: u32::from_be_bytes(input[3..7].try_into().expect("fixed flow ID")), }; header.validate()?; Ok(header) @@ -143,7 +142,6 @@ pub(super) enum WireError { InvalidHeaderLength(usize), UnknownKind(u8), ValueTooLarge, - ReservedCode, InvalidFlowId, InvalidData, InvalidWindow, @@ -158,8 +156,7 @@ impl fmt::Display for WireError { } Self::UnknownKind(kind) => write!(formatter, "unknown frame kind: {kind}"), Self::ValueTooLarge => formatter.write_str("frame value exceeds u16"), - Self::ReservedCode => formatter.write_str("reserved frame code is non-zero"), - Self::InvalidFlowId => formatter.write_str("invalid zero flow ID"), + Self::InvalidFlowId => formatter.write_str("flow ID is outside the 30-bit range"), Self::InvalidData => formatter.write_str("DATA payload must be non-empty"), Self::InvalidWindow => formatter.write_str("WINDOW credit must be non-zero"), Self::InvalidClose => formatter.write_str("invalid CLOSE code or value"), diff --git a/src/tests/mux/runtime.rs b/src/tests/mux/runtime.rs index bdef3b0..6410886 100644 --- a/src/tests/mux/runtime.rs +++ b/src/tests/mux/runtime.rs @@ -122,7 +122,7 @@ async fn assert_raw_frame_closes_carrier(frame: &[u8]) { #[tokio::test] async fn invalid_kind_and_unknown_flow_data_close_carrier() { - assert_raw_frame_closes_carrier(&[0xff, 0, 0, 0, 0, 0, 0, 1]).await; + assert_raw_frame_closes_carrier(&[0xff, 0, 0, 0, 0, 0, 1]).await; let mut frame = encode_header(FrameHeader::data(99, 1).unwrap()) .unwrap() @@ -131,6 +131,20 @@ async fn invalid_kind_and_unknown_flow_data_close_carrier() { assert_raw_frame_closes_carrier(&frame).await; } +#[tokio::test] +async fn invalid_prepared_id_does_not_reserve_a_stream_or_close_the_carrier() { + let (left, _peer) = tokio::io::duplex(1024); + let (handle, _incoming) = MuxHandle::start(left, MuxConfig::default()).unwrap(); + for id in [0, crate::protocol::MAX_FLOW_ID + 1, u32::MAX] { + assert!(handle.prepare_stream(id).is_err()); + assert_eq!(handle.active_streams(), 0); + assert!(!handle.is_closed()); + } + let _stream = handle.prepare_stream(crate::protocol::MAX_FLOW_ID).unwrap(); + assert_eq!(handle.active_streams(), 1); + handle.close(); +} + #[tokio::test] async fn duplicate_open_and_credit_overflow_close_carrier() { let open = encode_header(FrameHeader::open(7, 0).unwrap()).unwrap(); diff --git a/src/tests/mux/wire.rs b/src/tests/mux/wire.rs index efef5f0..16548b5 100644 --- a/src/tests/mux/wire.rs +++ b/src/tests/mux/wire.rs @@ -1,23 +1,27 @@ use super::*; #[test] -fn headers_have_stable_eight_byte_vectors() { +fn headers_have_stable_seven_byte_vectors() { let cases = [ ( FrameHeader::open(0x0102_0304, 0x0506).unwrap(), - [1, 0, 5, 6, 1, 2, 3, 4], + [1, 5, 6, 1, 2, 3, 4], ), ( FrameHeader::data(0x0102_0304, 0x0506).unwrap(), - [2, 0, 5, 6, 1, 2, 3, 4], + [2, 5, 6, 1, 2, 3, 4], ), ( FrameHeader::window(0, 0x0506).unwrap(), - [3, 0, 5, 6, 0, 0, 0, 0], + [3, 5, 6, 0, 0, 0, 0], + ), + ( + FrameHeader::close(0x0102_0304, CLOSE_FIN).unwrap(), + [4, 0, 0, 1, 2, 3, 4], ), ( FrameHeader::close(0x0102_0304, CLOSE_RESET).unwrap(), - [4, 1, 0, 0, 1, 2, 3, 4], + [5, 0, 0, 1, 2, 3, 4], ), ]; for (header, encoded) in cases { @@ -42,3 +46,42 @@ fn invalid_codes_and_values_are_rejected() { assert!(FrameHeader::close(1, CLOSE_RESET).is_ok()); assert!(FrameHeader::close(1, 2).is_err()); } + +#[test] +fn flow_ids_must_fit_the_shared_thirty_bit_space() { + for kind in [ + FrameKind::Open, + FrameKind::Data, + FrameKind::Window, + FrameKind::Fin, + FrameKind::Reset, + ] { + let value = u16::from(matches!(kind, FrameKind::Data | FrameKind::Window)); + let header = FrameHeader { + kind, + value, + flow_id: MAX_FLOW_ID, + }; + let bytes = encode_header(header).unwrap(); + assert_eq!(decode_header(&bytes).unwrap(), header); + for flow_id in [MAX_FLOW_ID + 1, u32::MAX] { + assert!(encode_header(FrameHeader { flow_id, ..header }).is_err()); + let mut invalid = bytes; + invalid[3..].copy_from_slice(&flow_id.to_be_bytes()); + assert!(decode_header(&invalid).is_err()); + } + for len in 0..HEADER_LEN { + assert!(decode_header(&bytes[..len]).is_err()); + } + } +} + +#[test] +fn decoder_rejects_unknown_types_and_nonzero_terminal_values() { + for kind in [0, 6, 0xff] { + assert!(decode_header(&[kind, 0, 0, 0, 0, 0, 1]).is_err()); + } + for kind in [4, 5] { + assert!(decode_header(&[kind, 0, 1, 0, 0, 0, 1]).is_err()); + } +} diff --git a/src/tests/portal/conn/tcp.rs b/src/tests/portal/conn/tcp.rs index cb0423e..71c8d22 100644 --- a/src/tests/portal/conn/tcp.rs +++ b/src/tests/portal/conn/tcp.rs @@ -423,10 +423,10 @@ async fn tls_mux_carrier_closes_after_becoming_fully_idle() { .await .unwrap() .unwrap(); - let mut window = [0_u8; 8]; + let mut window = [0_u8; 7]; tls.read_exact(&mut window).await.unwrap(); assert_eq!(window[0], 0x03); - assert_eq!(&window[4..], &[0, 0, 0, 0]); + assert_eq!(&window[3..], &[0, 0, 0, 0]); let mut byte = [0_u8; 1]; let read = tls.read(&mut byte).await; assert!( From fc65b9d0a873e3027a7ac8781aaa78a962cc8dca Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 09:05:05 +0800 Subject: [PATCH 50/62] Pack QUIC UDP frames into 30-bit IDs --- src/protocol/datagram.rs | 65 ++++++------ src/protocol/datagram/reassembly.rs | 1 + src/tests/protocol/datagram.rs | 151 ++++++++++++++++++++++------ 3 files changed, 157 insertions(+), 60 deletions(-) diff --git a/src/protocol/datagram.rs b/src/protocol/datagram.rs index 7f49ca5..bc409a9 100644 --- a/src/protocol/datagram.rs +++ b/src/protocol/datagram.rs @@ -8,21 +8,21 @@ use bytes::Bytes; use super::FlowId; -/// Unfragmented DATA frame type in bits 0..1. +/// Unfragmented DATA frame type in the high two bits. pub const UDP_FRAME_DATA: u8 = 0; -/// Fragmented DATA frame type in bits 0..1. +/// Fragmented DATA frame type in the high two bits. pub const UDP_FRAME_FRAGMENT: u8 = 1; -/// Flow CLOSE frame type in bits 0..1. +/// Flow CLOSE frame type in the high two bits. pub const UDP_FRAME_CLOSE: u8 = 2; /// Common unfragmented/CLOSE header length. -pub const UDP_HEADER_LEN: usize = 5; +pub const UDP_HEADER_LEN: usize = 4; /// Fragment header length. -pub const UDP_FRAGMENT_HEADER_LEN: usize = 13; +pub const UDP_FRAGMENT_HEADER_LEN: usize = 12; /// Largest UDP payload representable by the protocol. pub const UDP_PACKET_MAX: usize = u16::MAX as usize; -const FRAME_TYPE_MASK: u8 = 0b0000_0011; -const RESERVED_MASK: u8 = 0b1111_1100; +const FRAME_TYPE_SHIFT: u32 = 30; +const FRAME_TYPE_MASK: u32 = 0b11 << FRAME_TYPE_SHIFT; /// Fragment metadata parameterized by borrowed or owned payload storage. #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -101,12 +101,11 @@ pub fn encode_udp_fragment_header( "encode_udp_fragment_header", )?; let mut output = [0; UDP_FRAGMENT_HEADER_LEN]; - output[0] = UDP_FRAME_FRAGMENT; - output[1..5].copy_from_slice(&flow_id.to_be_bytes()); - output[5..9].copy_from_slice(&packet_id.to_be_bytes()); - output[9] = fragment_index; - output[10] = fragment_count; - output[11..13].copy_from_slice(&total_len.to_be_bytes()); + output[..4].copy_from_slice(&encode_base_word(UDP_FRAME_FRAGMENT, flow_id)?.to_be_bytes()); + output[4..8].copy_from_slice(&packet_id.to_be_bytes()); + output[8] = fragment_index; + output[9] = fragment_count; + output[10..12].copy_from_slice(&total_len.to_be_bytes()); Ok(output) } @@ -226,21 +225,19 @@ pub fn decode_udp_frame(input: &[u8]) -> Result> { if input.len() < UDP_HEADER_LEN { bail!("protocol::datagram::decode_udp_frame: short header"); } - let flags = input[0]; - if flags & RESERVED_MASK != 0 { - bail!("protocol::datagram::decode_udp_frame: reserved flags are non-zero"); - } - let flow_id = u32::from_be_bytes(input[1..5].try_into().expect("fixed flow id")); + let base = u32::from_be_bytes(input[..4].try_into().expect("fixed base header")); + let frame_type = (base & FRAME_TYPE_MASK) >> FRAME_TYPE_SHIFT; + let flow_id = base & super::MAX_FLOW_ID; validate_flow_id(flow_id, "decode_udp_frame")?; - match flags & FRAME_TYPE_MASK { - UDP_FRAME_DATA => { + match frame_type { + value if value == UDP_FRAME_DATA as u32 => { let payload = &input[UDP_HEADER_LEN..]; validate_udp_payload(payload, "decode_udp_frame")?; Ok(UdpFrame::Data { flow_id, payload }) } - UDP_FRAME_FRAGMENT => decode_fragment(input, flow_id), - UDP_FRAME_CLOSE => { + value if value == UDP_FRAME_FRAGMENT as u32 => decode_fragment(input, flow_id), + value if value == UDP_FRAME_CLOSE as u32 => { if input.len() != UDP_HEADER_LEN { bail!("protocol::datagram::decode_udp_frame: CLOSE payload"); } @@ -275,11 +272,11 @@ fn decode_fragment(input: &[u8], flow_id: FlowId) -> Result> { if input.len() < UDP_FRAGMENT_HEADER_LEN { bail!("protocol::datagram::decode_udp_frame: short fragment header"); } - let packet_id = u32::from_be_bytes(input[5..9].try_into().expect("fixed packet id")); + let packet_id = u32::from_be_bytes(input[4..8].try_into().expect("fixed packet id")); validate_packet_id(packet_id, "decode_udp_frame")?; - let fragment_index = input[9]; - let fragment_count = input[10]; - let total_len = u16::from_be_bytes([input[11], input[12]]); + let fragment_index = input[8]; + let fragment_count = input[9]; + let total_len = u16::from_be_bytes([input[10], input[11]]); validate_fragment_metadata( fragment_index, fragment_count, @@ -305,16 +302,20 @@ fn decode_fragment(input: &[u8], flow_id: FlowId) -> Result> { } fn encode_base_header(frame_type: u8, flow_id: FlowId) -> Result<[u8; UDP_HEADER_LEN]> { + Ok(encode_base_word(frame_type, flow_id)?.to_be_bytes()) +} + +fn encode_base_word(frame_type: u8, flow_id: FlowId) -> Result { validate_flow_id(flow_id, "encode_base_header")?; - let mut output = [0; UDP_HEADER_LEN]; - output[0] = frame_type; - output[1..].copy_from_slice(&flow_id.to_be_bytes()); - Ok(output) + if frame_type > UDP_FRAME_CLOSE { + bail!("protocol::datagram::encode_base_header: invalid frame type"); + } + Ok((u32::from(frame_type) << FRAME_TYPE_SHIFT) | flow_id) } fn validate_flow_id(flow_id: FlowId, operation: &str) -> Result<()> { - if flow_id == 0 { - bail!("protocol::datagram::{operation}: zero flow id"); + if flow_id == 0 || flow_id > super::MAX_FLOW_ID { + bail!("protocol::datagram::{operation}: flow id out of range"); } Ok(()) } diff --git a/src/protocol/datagram/reassembly.rs b/src/protocol/datagram/reassembly.rs index f7d20d1..2088d85 100644 --- a/src/protocol/datagram/reassembly.rs +++ b/src/protocol/datagram/reassembly.rs @@ -162,6 +162,7 @@ impl DatagramReassembler { F: FnOnce(u16) -> Option, { if flow_id == 0 + || flow_id > crate::protocol::MAX_FLOW_ID || fragment.packet_id == 0 || validate_fragment_metadata( fragment.fragment_index, diff --git a/src/tests/protocol/datagram.rs b/src/tests/protocol/datagram.rs index 62f392a..d6a69ab 100644 --- a/src/tests/protocol/datagram.rs +++ b/src/tests/protocol/datagram.rs @@ -3,20 +3,117 @@ use std::time::{Duration, Instant}; +use crate::protocol::MAX_FLOW_ID; + use super::*; #[test] -fn normal_data_and_close_have_exact_five_byte_headers() { +fn packed_header_preserves_maximum_ids_and_all_type_bits() { + let data = encode_udp_data(MAX_FLOW_ID, &[]).unwrap(); + assert_eq!(data, [0x3f, 0xff, 0xff, 0xff]); assert_eq!( - encode_udp_data(0x0102_0304, &[0xaa, 0xbb]).unwrap(), - [0x00, 1, 2, 3, 4, 0xaa, 0xbb] + decode_udp_frame(&data).unwrap(), + UdpFrame::Data { + flow_id: MAX_FLOW_ID, + payload: &[] + } ); + let close = encode_udp_close(MAX_FLOW_ID).unwrap(); + assert_eq!(close, [0xbf, 0xff, 0xff, 0xff]); + assert_eq!( + decode_udp_frame(&close).unwrap(), + UdpFrame::Close { + flow_id: MAX_FLOW_ID + } + ); + let mut fragment = encode_udp_fragment_header(MAX_FLOW_ID, u32::MAX, 0, 2, 2) + .unwrap() + .to_vec(); assert_eq!( - encode_udp_data(0x0102_0304, &[]).unwrap(), - [0x00, 1, 2, 3, 4] + fragment, + [0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 2, 0, 2] + ); + fragment.push(42); + let UdpFrame::Fragment { + flow_id, + fragment: decoded, + } = decode_udp_frame(&fragment).unwrap() + else { + panic!("expected fragment"); + }; + assert_eq!(flow_id, MAX_FLOW_ID); + assert_eq!(decoded.packet_id, u32::MAX); + for flow_id in [0, MAX_FLOW_ID + 1, u32::MAX] { + assert!(encode_udp_data(flow_id, &[]).is_err()); + assert!(encode_udp_close(flow_id).is_err()); + assert!(encode_udp_fragment_header(flow_id, 1, 0, 2, 2).is_err()); + } + for len in 0..UDP_HEADER_LEN { + assert!(decode_udp_frame(&data[..len]).is_err()); + } + for len in 0..=UDP_FRAGMENT_HEADER_LEN { + assert!(decode_udp_frame(&fragment[..len]).is_err()); + } +} + +#[test] +fn fragmentation_starts_exactly_above_the_new_data_capacity() { + for max_size in [13, 64, 1200] { + let exact = vec![0x5a; max_size - UDP_HEADER_LEN]; + let frames = encode_udp_data_fragments(1, 1, &exact, max_size).unwrap(); + assert_eq!(frames.len(), 1); + assert_eq!(frames[0].len(), max_size); + assert!(matches!( + decode_udp_frame(&frames[0]).unwrap(), + UdpFrame::Data { .. } + )); + + let larger = vec![0x5a; exact.len() + 1]; + let frames = encode_udp_data_fragments(1, 1, &larger, max_size).unwrap(); + assert!(frames.len() >= 2); + let mut recovered = Vec::new(); + for frame in frames { + assert!(frame.len() <= max_size); + let UdpFrame::Fragment { fragment, .. } = decode_udp_frame(&frame).unwrap() else { + panic!("expected fragment"); + }; + recovered.extend_from_slice(fragment.payload); + } + assert_eq!(recovered, larger); + } +} + +#[test] +fn invalid_reassembly_id_cannot_reserve_resources() { + let mut reassembler = DatagramReassembler::<()>::new(ReassemblyConfig::default()); + let fragment = OwnedUdpFragment { + packet_id: 1, + fragment_index: 0, + fragment_count: 2, + total_len: 2, + payload: Bytes::from_static(b"a"), + }; + for id in [0, MAX_FLOW_ID + 1, u32::MAX] { + assert!(matches!( + reassembler.push_with(id, fragment.clone(), Instant::now(), |_| panic!( + "invalid ID reserved resources" + )), + ReassemblyOutcome::Dropped(_) + )); + assert_eq!(reassembler.slot_count(), 0); + assert_eq!(reassembler.reserved_bytes(), 0); + } +} + +#[test] +fn normal_data_and_close_have_exact_four_byte_headers() { + assert_eq!( + encode_udp_data(0x0102_0304, &[0xaa, 0xbb]).unwrap(), + [1, 2, 3, 4, 0xaa, 0xbb] ); - assert_eq!(encode_udp_close(0x0102_0304).unwrap(), [0x02, 1, 2, 3, 4]); - assert_eq!(UDP_HEADER_LEN, 5); + assert_eq!(encode_udp_data(0x0102_0304, &[]).unwrap(), [1, 2, 3, 4]); + assert_eq!(encode_udp_close(0x0102_0304).unwrap(), [0x81, 2, 3, 4]); + assert_eq!(UDP_HEADER_LEN, 4); } #[test] @@ -37,14 +134,14 @@ fn unfragmented_packets_never_carry_fragment_metadata() { } #[test] -fn fragmented_packets_use_exact_thirteen_byte_headers() { +fn fragmented_packets_use_exact_twelve_byte_headers() { let payload = vec![0x5a; 2500]; let frames = encode_udp_data_fragments(0x0102_0304, 0x1122_3344, &payload, 1200).unwrap(); assert_eq!(frames.len(), 3); - assert_eq!(UDP_FRAGMENT_HEADER_LEN, 13); + assert_eq!(UDP_FRAGMENT_HEADER_LEN, 12); assert_eq!( &frames[0][..UDP_FRAGMENT_HEADER_LEN], - &[0x01, 1, 2, 3, 4, 0x11, 0x22, 0x33, 0x44, 0, 3, 0x09, 0xc4] + &[0x41, 2, 3, 4, 0x11, 0x22, 0x33, 0x44, 0, 3, 0x09, 0xc4] ); let mut assembled = Vec::new(); @@ -92,11 +189,11 @@ fn lazy_fragment_plan_materializes_only_requested_frames() { #[test] fn lazy_fragment_plan_enforces_two_to_255_fragments() { - assert!(encode_udp_fragments(1, 1, &[0; 9], 14).is_err()); - let fragments = encode_udp_fragments(1, 1, &[0; 255], 14).unwrap(); + assert!(encode_udp_fragments(1, 1, &[0; 9], 13).is_err()); + let fragments = encode_udp_fragments(1, 1, &[0; 255], 13).unwrap(); assert_eq!(fragments.len(), 255); - assert!(encode_udp_fragments(1, 1, &[0; 256], 14).is_err()); - assert!(encode_udp_fragments(1, 0, &[0; 20], 14).is_err()); + assert!(encode_udp_fragments(1, 1, &[0; 256], 13).is_err()); + assert!(encode_udp_fragments(1, 0, &[0; 20], 13).is_err()); } #[test] @@ -139,35 +236,33 @@ fn owned_decoder_slices_data_and_fragment_payloads_without_copying() { }; assert_eq!(flow_id, 7); assert_eq!(fragment.payload.as_ptr(), fragment_payload_ptr); - assert_eq!(fragment.payload, [0x5a; 51][..]); + assert_eq!(fragment.payload, [0x5a; 52][..]); } #[test] -fn flow_and_packet_ids_must_be_nonzero() { +fn flow_ids_must_fit_thirty_bits_and_packet_ids_must_be_nonzero() { assert!(encode_udp_data(0, b"x").is_err()); assert!(encode_udp_close(0).is_err()); assert!(encode_udp_data_fragments(0, 1, b"x", 64).is_err()); + assert!(encode_udp_data(MAX_FLOW_ID, b"x").is_ok()); + assert!(encode_udp_data(MAX_FLOW_ID + 1, b"x").is_err()); assert!(encode_udp_fragment_header(1, 0, 0, 2, 2).is_err()); let mut fragment = encode_udp_data_fragments(1, 9, &[1; 100], 64) .unwrap() .remove(0); - fragment[5..9].fill(0); + fragment[4..8].fill(0); assert!(decode_udp_frame(&fragment).is_err()); } #[test] -fn decoder_rejects_short_reserved_unknown_and_close_payload_frames() { +fn decoder_rejects_short_unknown_and_close_payload_frames() { for input in [ vec![], vec![0], vec![0, 0, 0, 0], - vec![0, 0, 0, 0, 0], - vec![3, 0, 0, 0, 1], - vec![0x04, 0, 0, 0, 1], - vec![0x40, 0, 0, 0, 1], - vec![0x82, 0, 0, 0, 1], - vec![2, 0, 0, 0, 1, 0], + vec![0xc0, 0, 0, 1], + vec![0x80, 0, 0, 1, 0], ] { assert!(decode_udp_frame(&input).is_err(), "accepted {input:?}"); } @@ -184,11 +279,11 @@ fn fragment_validation_rejects_every_invalid_metadata_shape() { .unwrap() .remove(0); for mutate in [ - |frame: &mut Vec| frame[10] = 1, - |frame: &mut Vec| frame[9] = frame[10], - |frame: &mut Vec| frame[11..13].fill(0), + |frame: &mut Vec| frame[9] = 1, + |frame: &mut Vec| frame[8] = frame[9], + |frame: &mut Vec| frame[10..12].fill(0), + |frame: &mut Vec| frame.truncate(11), |frame: &mut Vec| frame.truncate(12), - |frame: &mut Vec| frame.truncate(13), ] { let mut frame = valid.clone(); mutate(&mut frame); From 7eb01fd14f7c132a96a7ad6aaa46cde74a070587 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 09:05:14 +0800 Subject: [PATCH 51/62] Document compact V2 frame layouts --- docs/interoperability.md | 4 ++ docs/protocol.md | 81 ++++++++++++++++++++-------------------- docs/security.md | 2 +- 3 files changed, 45 insertions(+), 42 deletions(-) diff --git a/docs/interoperability.md b/docs/interoperability.md index 370624c..ddb8786 100644 --- a/docs/interoperability.md +++ b/docs/interoperability.md @@ -11,6 +11,10 @@ Portal and Vector use the same wire contract. Alternate clients and native Portal chains follow it as peers rather than relying on a separately versioned SDK. +V2 uses 7-byte Mux headers and 4-byte QUIC UDP base headers (12 bytes for +fragments), with a shared 30-bit flow ID range. The `nw2` protocol fixes these +frame layouts; peers and every native Portal hop use the same contract. + ## Endpoint contract Portal listeners, Vector remotes, and Portal `next` endpoints share two forms: diff --git a/docs/protocol.md b/docs/protocol.md index 2797cd2..9926145 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -161,7 +161,7 @@ Client -> Portal +------------+------------+-------------+-------------+-----+ | AuthFrame | Mux marker | MuxFrame | MuxFrame | ... | -| 32 bytes | 0xff | 8 + N bytes | 8 + N bytes | | +| 32 bytes | 0xff | 7 + N bytes | 7 + N bytes | | +------------+------------+-------------+-------------+-----+ Reconstructed logical stream @@ -267,17 +267,17 @@ Mux frame. ### MuxHeader -Every Mux frame starts with an 8-byte header. A DATA frame carries +Every Mux frame starts with a 7-byte header. A DATA frame carries exactly `value` payload bytes; control frames carry no payload. ```text -MuxHeader - 8 bytes +MuxHeader - 7 bytes - offset 0 1 2 4 8 - +--------+--------+---------------+-----------------------+ - | kind | code | value | flow_id | - | u8 | u8 | u16 | u32 | - +--------+--------+---------------+-----------------------+ + offset 0 1 3 7 + +--------+---------------+-----------------------+ + | kind | value | flow_id | + | u8 | u16 | u32 | + +--------+---------------+-----------------------+ ``` | `kind` | Name | `value` | `flow_id` | @@ -285,14 +285,16 @@ MuxHeader - 8 bytes | `0x01` | OPEN | opener receive-window extension in 1 KiB units | nonzero | | `0x02` | DATA | payload length, 1..65535 | nonzero | | `0x03` | WINDOW | returned credit in 1 KiB units | `0` for connection, nonzero for stream | -| `0x04` | CLOSE | always `0` | nonzero | +| `0x04` | FIN | always `0` | nonzero | +| `0x05` | RESET | always `0` | nonzero | -OPEN, DATA, and WINDOW require `code=0`. OPEN carries no payload and extends +OPEN carries no payload and extends the opener's 4 MiB initial stream receive window. The runtime emits DATA payloads of at most 32 KiB. -CLOSE carries no payload. `code=0` is FIN half-close and `code=1` is RESET. -Other codes are invalid. +FIN and RESET carry no payload. FIN half-closes a logical stream; RESET +immediately removes it. Other frame kinds are invalid. Every nonzero `flow_id` +must be at most `0x3fffffff`; the upper two bits of its u32 field must be zero. WINDOW carries no payload and requires nonzero credit in 1 KiB units. A @@ -300,7 +302,7 @@ WINDOW with `flow_id=0` replenishes connection credit; a nonzero ID replenishes that logical stream. Credit that would exceed the configured window closes the carrier. A late stream-local WINDOW for an already closed stream is ignored. -DATA for an unknown flow is a carrier error. Late or duplicate CLOSE processing +DATA for an unknown flow is a carrier error. Late or duplicate FIN/RESET processing is idempotent. Closing the physical Mux carrier fails every logical stream on that carrier. @@ -382,8 +384,9 @@ Field values: | `down` | 4 | `0=TLS/TCP`, `1=QUIC` | | `hops` | 7..5 | remaining Portal forwarding budget, `0..7` | -`flow_id` is nonzero and is scoped to `session_id`. The same logical flow uses -the same ID on OPEN and ATTACH, in MuxHeader, and in QUIC UDP DATAGRAM frames. +`flow_id` is in `1..=0x3fffffff` and is scoped to `session_id`. Its u32 field's +upper two bits must be zero. The same logical flow uses the same ID on OPEN +and ATTACH, in MuxHeader, and in QUIC UDP DATAGRAM frames. Role semantics: @@ -527,34 +530,28 @@ Every DATAGRAM contains exactly one DATA, FRAGMENT, or CLOSE frame. ### Common DATA/CLOSE header ```text -QUIC UDP DATA or CLOSE - 5 + N bytes +QUIC UDP DATA or CLOSE - 4 + N bytes - offset 0 1 5 - +------------------------+-----------------------+ - | flags | flow_id | - | u8 | u32 | - +------------------------+-----------------------+ + offset 0 4 + +------------------------------------------------+ + | type:2 | flow_id:30 | + | u32, network byte order | + +------------------------------------------------+ | payload ... | DATA only +------------------------------------------------+ - -flags byte - - bit 7 2 1 0 - +-----------------------------+-----------+ - | reserved, MUST be zero | type | - | 6 bits | 2 bits | - +-----------------------------+-----------+ ``` | `type` | Name | Payload | |---:|---|---| | `0b00` | DATA | remaining DATAGRAM bytes; zero length is valid | -| `0b01` | FRAGMENT | uses the 13-byte header below | -| `0b10` | CLOSE | none; total DATAGRAM length MUST be 5 | +| `0b01` | FRAGMENT | uses the 12-byte header below | +| `0b10` | CLOSE | none; total DATAGRAM length MUST be 4 | | `0b11` | invalid | — | -`flow_id` is nonzero. DATA has no payload-length field because the QUIC -DATAGRAM boundary supplies the length. CLOSE immediately removes the UDP route. +The common word is `(type << 30) | flow_id`, with type in bits 31..30 and +`flow_id` in bits 29..0. `flow_id` is in `1..=0x3fffffff`. DATA has no +payload-length field because the QUIC DATAGRAM boundary supplies the length. +CLOSE immediately removes the UDP route. ### Fragment header @@ -562,13 +559,13 @@ Packets that exceed the current QUIC maximum DATAGRAM size are divided into 2–255 fragments. ```text -QUIC UDP FRAGMENT - 13 + N bytes +QUIC UDP FRAGMENT - 12 + N bytes - offset 0 1 5 9 10 11 13 - +------+------------+------------+----------+---------+------------+ - | 0x01 | flow_id | packet_id | frag_ix | count | total_len | - | u8 | u32 | u32 | u8 | u8 | u16 | - +------+------------+------------+----------+---------+------------+ + offset 0 4 8 9 10 12 + +--------------------+------------+----------+---------+------------+ + | type:2|flow_id:30 | packet_id | frag_ix | count | total_len | + | u32 | u32 | u8 | u8 | u16 | + +--------------------+------------+----------+---------+------------+ | fragment payload, N > 0 | +------------------------------------------------------------------+ ``` @@ -600,8 +597,10 @@ ATTACH. ## 11. Runtime limits and failure scope Application sessions impose no fixed TCP, UDP, or pending-pair count limit. -Flow IDs are unique within their wire identifier space. Byte flow control, -queue budgets, and pairing/setup timeouts apply. +Active flow IDs are unique within `1..=0x3fffffff`. Allocation wraps to 1, +skips IDs held by live leases, and fails when the space is exhausted. Released +IDs may be reused; this does not provide generation isolation for delayed +messages. Byte flow control, queue budgets, and pairing/setup timeouts apply. QUIC bidirectional-stream credit grows with live and pending QUIC flows, with setup headroom of max(64, live / 4). A QUIC TCP flow owns one reliable stream; diff --git a/docs/security.md b/docs/security.md index e046cea..39ba38a 100644 --- a/docs/security.md +++ b/docs/security.md @@ -104,7 +104,7 @@ bound their total memory use. Per-stream and connection credit plus bounded channel admission limit how much one stream can occupy. The finite frame queue has 512 slots, but payload admission is capped by the selected connection window; empty -OPEN/CLOSE/WINDOW frames cannot turn those slots into +OPEN/FIN/RESET/WINDOW frames cannot turn those slots into retained application payload. These are credit ceilings rather than eagerly allocated payload buffers. From 3fc6b96018fdab96097c5e3f46728423083b574b Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 14:26:58 +0800 Subject: [PATCH 52/62] Stabilize Mux full-duplex credit test --- src/tests/portal/conn/vector.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tests/portal/conn/vector.rs b/src/tests/portal/conn/vector.rs index d01d7e4..a66a0ec 100644 --- a/src/tests/portal/conn/vector.rs +++ b/src/tests/portal/conn/vector.rs @@ -25,6 +25,7 @@ use crate::transport::Stats; use crate::vector::{PortalClient, PortalClientConfig, Vector}; const TEST_TIMEOUT: Duration = Duration::from_secs(10); +const FULL_DUPLEX_TIMEOUT: Duration = Duration::from_secs(60); const ROUTE_POLICY_MATRIX: [(&str, &str); 9] = [ ("tcp", "tcp"), ("tcp", "udp"), @@ -273,7 +274,9 @@ async fn mux_symmetric_carriers_relay_tcp_and_fragmented_udp() { #[tokio::test] async fn mux_full_duplex_tcp_exceeds_each_direction_credit_window() { - const DIRECTION_BYTES: usize = 3 * 1024 * 1024; + // The throughput profile grants 16 MiB per stream. Cross that boundary in + // both directions so progress depends on returning Mux credit. + const DIRECTION_BYTES: usize = 20 * 1024 * 1024; for carrier in ["tcp", "udp"] { let target = TcpListener::bind("127.0.0.1:0").await.unwrap(); @@ -295,7 +298,7 @@ async fn mux_full_duplex_tcp_exceeds_each_direction_credit_window() { tokio::join!(upload, download); }); let runtime = start_runtime(carrier, carrier, 1).await; - timeout(TEST_TIMEOUT, async { + timeout(FULL_DUPLEX_TIMEOUT, async { let mut stream = TcpStream::connect(runtime.socks).await.unwrap(); negotiate_socks(&mut stream).await; stream From df168c1cc2bee59e896fec5f23588fcce93dc104 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 15:01:06 +0800 Subject: [PATCH 53/62] Bound Mux OPEN resource admission --- src/mux/mod.rs | 14 +++++++++++-- src/tests/mux/runtime.rs | 37 +++++++++++++++++++++++++++++++++ src/tests/vector/session/tls.rs | 1 + 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/mux/mod.rs b/src/mux/mod.rs index 2122aa3..a09b9d2 100644 --- a/src/mux/mod.rs +++ b/src/mux/mod.rs @@ -28,12 +28,14 @@ const MAX_STREAM_WINDOW_BYTES: usize = 16 * MIB; const MAX_CONNECTION_WINDOW_BYTES: usize = 32 * MIB; const CREDIT_UNIT_BYTES: usize = 1024; const WINDOW_UPDATE_DIVISOR: usize = 8; +const ACTIVE_STREAM_RESOURCE_LIMIT: usize = 4096; pub(crate) const MUX_IDLE_TIMEOUT: Duration = Duration::from_secs(30); #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) struct MuxConfig { pub stream_window_bytes: usize, pub connection_window_bytes: usize, + pub active_stream_limit: usize, pub outbound_frames: usize, } @@ -54,6 +56,7 @@ impl MuxConfig { Self { stream_window_bytes: profile.stream_receive_window as usize, connection_window_bytes: profile.connection_receive_window as usize, + active_stream_limit: ACTIVE_STREAM_RESOURCE_LIMIT, outbound_frames: 512, } } @@ -67,6 +70,7 @@ impl MuxConfig { .connection_window_bytes .is_multiple_of(CREDIT_UNIT_BYTES) || self.connection_window_bytes < self.stream_window_bytes + || self.active_stream_limit == 0 || self.outbound_frames == 0 || self.connection_window_bytes > Semaphore::MAX_PERMITS { @@ -224,8 +228,8 @@ impl Shared { if self.closed.load(Ordering::Acquire) { return Err(closed()); } - // Every DATA frame consumes at least one KiB of connection credit, - // bounding both payload and queue nodes without blocking the reader. + // DATA is bounded separately by byte credit. OPEN must have its own + // admission ceiling because it allocates flow metadata without DATA. let (sender, receiver) = mpsc::unbounded_channel(); let mut flows = self.flows.lock().expect("mux flow lock"); if self.closed.load(Ordering::Acquire) { @@ -237,6 +241,12 @@ impl Shared { "mux flow already exists", )); } + if flows.len() >= self.config.active_stream_limit { + return Err(io::Error::new( + io::ErrorKind::WouldBlock, + "mux active-stream resource limit reached", + )); + } let send_credit = Arc::new(Semaphore::new(credit_units(BASE_STREAM_WINDOW_BYTES))); let initial_credit = if advertise_window { credit_units( diff --git a/src/tests/mux/runtime.rs b/src/tests/mux/runtime.rs index 6410886..10015b9 100644 --- a/src/tests/mux/runtime.rs +++ b/src/tests/mux/runtime.rs @@ -30,6 +30,35 @@ async fn more_than_256_live_streams_transfer_and_half_close() { .expect("stream admission must not depend on terminal queue capacity"); } +#[tokio::test] +async fn remote_open_admission_closes_carrier_at_the_metadata_limit() { + let config = MuxConfig { + active_stream_limit: 2, + ..MuxConfig::default() + }; + let (mut peer, carrier) = tokio::io::duplex(4096); + let (server, mut incoming) = MuxHandle::start(carrier, config).unwrap(); + let mut streams = Vec::new(); + + for flow_id in 1..=2 { + peer.write_all(&encode_header(FrameHeader::open(flow_id, 0).unwrap()).unwrap()) + .await + .unwrap(); + let stream = incoming.accept().await.unwrap().unwrap(); + assert_eq!(stream.flow_id(), flow_id); + streams.push(stream); + } + assert_eq!(server.active_streams(), 2); + + peer.write_all(&encode_header(FrameHeader::open(3, 0).unwrap()).unwrap()) + .await + .unwrap(); + tokio::time::timeout(Duration::from_secs(1), server.closed()) + .await + .expect("OPEN beyond the metadata budget must close the carrier"); + assert_eq!(server.active_streams(), 0); +} + #[tokio::test] async fn slow_small_packet_reader_does_not_block_other_flows() { tokio::time::timeout(Duration::from_secs(5), async { @@ -517,4 +546,12 @@ fn profiles_outside_wire_window_limits_are_rejected() { .is_err() ); } + assert!( + MuxConfig { + active_stream_limit: 0, + ..MuxConfig::default() + } + .validate() + .is_err() + ); } diff --git a/src/tests/vector/session/tls.rs b/src/tests/vector/session/tls.rs index 8de7319..29fcccb 100644 --- a/src/tests/vector/session/tls.rs +++ b/src/tests/vector/session/tls.rs @@ -16,6 +16,7 @@ async fn carrier(pressured: bool) -> (MuxHandle, MuxHandle, crate::mux::Incoming stream_window_bytes: 4 << 20, connection_window_bytes: 8 << 20, outbound_frames: 512, + ..MuxConfig::default() }; let (handle, _) = MuxHandle::start(left, config).unwrap(); let (peer, mut incoming) = MuxHandle::start(right, config).unwrap(); From bda06e2c37a89bb3b8a13bd7e732395a780add1c Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 15:01:11 +0800 Subject: [PATCH 54/62] Bound Vector SOCKS ingress resources --- src/tests/vector/socks_server.rs | 35 ++++++++++++++++++++++++++++++++ src/vector/mod.rs | 9 ++++++++ src/vector/socks/server.rs | 23 ++++++++++++++++++++- src/vector/socks/server/udp.rs | 13 ++++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/tests/vector/socks_server.rs b/src/tests/vector/socks_server.rs index 52ba01c..bfd73bf 100644 --- a/src/tests/vector/socks_server.rs +++ b/src/tests/vector/socks_server.rs @@ -38,6 +38,41 @@ fn source_endpoint_locks_first_port() { )); } +#[test] +fn socks_client_resource_admission_is_process_wide_and_reusable() { + let vector = Vector::new( + Url::parse("vector://secret@127.0.0.1:2000?socks=127.0.0.1:1080").unwrap(), + Logger::new(LogLevel::None, false), + ) + .unwrap(); + let limit = vector.inner.socks_client_admission.available_permits(); + let mut held = (0..limit) + .map(|_| try_admit_client(&vector.inner).unwrap()) + .collect::>(); + assert!(try_admit_client(&vector.inner).is_none()); + + held.pop(); + assert!(try_admit_client(&vector.inner).is_some()); +} + +#[test] +fn udp_target_resource_admission_is_held_for_the_target_lifetime() { + let vector = Vector::new( + Url::parse("vector://secret@127.0.0.1:2000?socks=127.0.0.1:1080").unwrap(), + Logger::new(LogLevel::None, false), + ) + .unwrap(); + let admission = &vector.inner.socks_udp_target_admission; + let limit = admission.available_permits(); + let mut held = (0..limit) + .map(|_| try_admit_udp_target(admission).unwrap()) + .collect::>(); + assert!(try_admit_udp_target(admission).is_none()); + + held.pop(); + assert!(try_admit_udp_target(admission).is_some()); +} + #[tokio::test] async fn pending_target_setup_does_not_block_control_shutdown() { let portal = TcpListener::bind("127.0.0.1:0").await.unwrap(); diff --git a/src/vector/mod.rs b/src/vector/mod.rs index 0a58589..1d72d00 100644 --- a/src/vector/mod.rs +++ b/src/vector/mod.rs @@ -37,6 +37,9 @@ use crate::telemetry::TelemetryServer; use crate::telemetry::{InstanceRole, TelemetryHub}; use crate::transport::{Buffers, RateLimiter, Stats}; +const SOCKS_CLIENT_RESOURCE_LIMIT: usize = 1024; +const SOCKS_UDP_TARGET_RESOURCE_LIMIT: usize = 1024; + /// Runnable native client serving a local SOCKS5 endpoint. pub struct Vector { inner: Arc, @@ -53,6 +56,8 @@ pub(super) struct VectorInner { rate_limiter: Option>, client: Arc, local_udp_budget: Arc, + socks_client_admission: Arc, + socks_udp_target_admission: Arc, shutdown: CancellationToken, } @@ -242,6 +247,10 @@ impl Vector { rate_limiter, client, local_udp_budget: Arc::new(Semaphore::new(udp_queue_bytes)), + socks_client_admission: Arc::new(Semaphore::new(SOCKS_CLIENT_RESOURCE_LIMIT)), + socks_udp_target_admission: Arc::new(Semaphore::new( + SOCKS_UDP_TARGET_RESOURCE_LIMIT, + )), shutdown, }), }) diff --git a/src/vector/socks/server.rs b/src/vector/socks/server.rs index cb7e5de..8d5032b 100644 --- a/src/vector/socks/server.rs +++ b/src/vector/socks/server.rs @@ -39,7 +39,7 @@ use super::super::udp_flow::{UdpTunnel, open_udp}; mod udp; #[cfg(test)] -use self::udp::{accept_udp_source, validate_udp_source_request}; +use self::udp::{accept_udp_source, try_admit_udp_target, validate_udp_source_request}; use self::udp::{run_udp_association, start_access}; const TCP_LISTEN_BACKLOG: i32 = 1024; const SOCKS_UDP_PACKET_MAX: usize = u16::MAX as usize + 3 + 1 + 1 + 255 + 2; @@ -78,9 +78,22 @@ pub(in crate::vector) async fn serve_listener( _ = shutdown.cancelled() => break, accepted = listener.accept() => match accepted { Ok((stream, peer)) => { + let Some(admission) = try_admit_client(&vector) else { + vector.telemetry.emit_runtime( + RuntimeEvent::new( + RuntimeLevel::Warn, + RuntimeKind::Listener, + "SOCKS client resource limit reached", + ) + .with_client(peer.to_string()), + ); + drop(stream); + continue; + }; let vector = vector.clone(); let shutdown = shutdown.clone(); clients.spawn(async move { + let _admission = admission; if let Err(error) = handle_client(vector.clone(), stream, peer, shutdown).await { vector.logger.debug(format_args!( "vector::socks::handle_client: {peer}: {error}" @@ -106,6 +119,14 @@ pub(in crate::vector) async fn serve_listener( while clients.join_next().await.is_some() {} } +fn try_admit_client(vector: &Arc) -> Option { + vector + .socks_client_admission + .clone() + .try_acquire_owned() + .ok() +} + async fn handle_client( vector: Arc, mut stream: TcpStream, diff --git a/src/vector/socks/server/udp.rs b/src/vector/socks/server/udp.rs index 78a705c..0259919 100644 --- a/src/vector/socks/server/udp.rs +++ b/src/vector/socks/server/udp.rs @@ -56,6 +56,7 @@ pub(super) async fn run_udp_association( )); let mut flows: HashMap> = HashMap::new(); let mut tasks = JoinSet::new(); + let active_targets = vector.socks_udp_target_admission.clone(); let mut packet = vec![0u8; SOCKS_UDP_PACKET_MAX]; let mut control_byte = [0u8; 1]; @@ -104,6 +105,10 @@ pub(super) async fn run_udp_association( } flows.remove(&target); } + flows.retain(|_, sender| !sender.is_closed()); + let Some(target_admission) = try_admit_udp_target(&active_targets) else { + continue; + }; let (sender, receiver) = mpsc::channel(64); if sender.try_send(payload).is_err() { continue; @@ -116,6 +121,7 @@ pub(super) async fn run_udp_association( target, receiver, association_shutdown.clone(), + target_admission, )); } Some(_) = tasks.join_next(), if !tasks.is_empty() => { @@ -136,6 +142,7 @@ async fn open_and_relay_udp_target( target: SocksAddress, outbound: mpsc::Receiver, shutdown: CancellationToken, + _target_admission: OwnedSemaphorePermit, ) { let source = client_endpoint .lock() @@ -177,6 +184,12 @@ async fn open_and_relay_udp_target( .await; } +pub(super) fn try_admit_udp_target( + admission: &Arc, +) -> Option { + admission.clone().try_acquire_owned().ok() +} + pub(super) fn validate_udp_source_request( requested: &SocksAddress, peer_ip: IpAddr, From 4b09656b1eb55af2222dd38fd69027489f00724b Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 15:01:15 +0800 Subject: [PATCH 55/62] Preserve reassembly state on admission failure --- src/protocol/datagram/reassembly.rs | 22 +++++++---- src/tests/portal/conn/session_flow.rs | 53 +++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/src/protocol/datagram/reassembly.rs b/src/protocol/datagram/reassembly.rs index 2088d85..b537b1a 100644 --- a/src/protocol/datagram/reassembly.rs +++ b/src/protocol/datagram/reassembly.rs @@ -236,26 +236,34 @@ impl DatagramReassembler { if self.config.max_slots == 0 || fragment.total_len as usize > self.config.max_bytes { return ReassemblyOutcome::Dropped(ReassemblyDropReason::ByteLimit); } - if self.slots.len() >= self.config.max_slots - && let Some(oldest) = self - .slots + let oldest = if self.slots.len() >= self.config.max_slots { + self.slots .iter() .min_by_key(|(_, slot)| slot.created_at) .map(|(key, _)| *key) - { - self.remove_slot(&oldest); - evicted_partial = true; - } + } else { + None + }; + let replaced_bytes = oldest + .and_then(|key| self.slots.get(&key)) + .map_or(0, |slot| slot.total_len as usize); if self .reserved_bytes + .saturating_sub(replaced_bytes) .saturating_add(fragment.total_len as usize) > self.config.max_bytes { return ReassemblyOutcome::Dropped(ReassemblyDropReason::ByteLimit); } + // External admission is fallible. Reserve before evicting so a failed + // replacement cannot discard an otherwise valid partial packet. let Some(reservation) = reserve(fragment.total_len) else { return ReassemblyOutcome::Dropped(ReassemblyDropReason::ByteLimit); }; + if let Some(oldest) = oldest { + self.remove_slot(&oldest); + evicted_partial = true; + } self.reserved_bytes += fragment.total_len as usize; let expiry = now.checked_add(self.config.ttl).unwrap_or(now); self.next_expiry = Some( diff --git a/src/tests/portal/conn/session_flow.rs b/src/tests/portal/conn/session_flow.rs index 39288c7..741e1b2 100644 --- a/src/tests/portal/conn/session_flow.rs +++ b/src/tests/portal/conn/session_flow.rs @@ -119,6 +119,59 @@ fn packet_budget_rejects_new_slot_without_leaking_permits() { assert_eq!(budget.available_permits(), 5); } +#[test] +fn failed_replacement_admission_preserves_the_existing_partial_packet() { + let budget = Arc::new(Semaphore::new(6)); + let now = Instant::now(); + let mut reassembler = DatagramReassembler::new(ReassemblyConfig { + max_slots: 1, + max_bytes: 12, + ..ReassemblyConfig::default() + }); + assert!(matches!( + push( + &mut reassembler, + 9, + fragment(3, 0, 2, 6, b"abc"), + now, + &budget, + ), + ReassemblyOutcome::Pending { .. } + )); + assert_eq!(budget.available_permits(), 0); + + assert!(matches!( + push( + &mut reassembler, + 10, + fragment(4, 0, 2, 6, b"xyz"), + now, + &budget, + ), + ReassemblyOutcome::Dropped(ReassemblyDropReason::ByteLimit) + )); + assert_eq!(reassembler.slot_count(), 1); + assert_eq!(reassembler.reserved_bytes(), 6); + + let (payload, reservation) = match push( + &mut reassembler, + 9, + fragment(3, 1, 2, 6, b"def"), + now, + &budget, + ) { + ReassemblyOutcome::Complete { + payload, + reservation, + .. + } => (payload, reservation), + _ => panic!("existing partial packet must survive failed replacement admission"), + }; + assert_eq!(payload, b"abcdef"[..]); + drop(reservation); + assert_eq!(budget.available_permits(), 6); +} + #[test] fn remove_flow_releases_only_that_flows_partial_reservations() { let budget = Arc::new(Semaphore::new(20)); From a9c273cf40b83f3bf60d580bcdb5eb9b8800bddb Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 15:01:19 +0800 Subject: [PATCH 56/62] Document V2 resource admission ceilings --- README.md | 3 ++- docs/configuration.md | 17 ++++++++++------- docs/interoperability.md | 4 ++-- docs/operations.md | 11 +++++++---- docs/protocol.md | 18 +++++++++++------- docs/security.md | 18 +++++++++--------- 6 files changed, 41 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 8e99762..df2fa36 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,8 @@ Carrier bootstrap Logical flow +-- QUIC: first stream only +-- UDP: UoT or QUIC DATAGRAM ``` -Frames are compact, queues are bounded, and hot-path buffers are reused. See +Frames are compact, DATA payload queues are bounded by byte credit, and hot-path +buffers are reused. See [Protocol](docs/protocol.md) for the wire contract and [Security](docs/security.md) for trust boundaries. diff --git a/docs/configuration.md b/docs/configuration.md index 87bdba4..598dee4 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -235,7 +235,8 @@ they establish another carrier. Establishments may run in parallel and count against the same eight slots. At capacity, flows choose the lowest credit/queue occupancy, breaking ties by live streams plus pending reservations. Connecting carriers also accept reservations to balance cold bursts. Existing streams do not migrate, and a full pool -continues accepting new streams. There is no fixed Mux stream density or count limit. +continues accepting new streams until a carrier's 4,096-stream resource ceiling. +There is no stream-density target. A carrier closes after 30 seconds fully idle. With `mux=0`, every TLS-carried Flow owns one on-demand lane that closes with the Flow. Mux applies when at @@ -323,19 +324,21 @@ Durations use humantime syntax such as `250ms`, `15s`, `2m`, or `1h`. | `NOW_RELOAD_INTERVAL` | `1h` | Supplied-certificate reload interval | TLS Mux shares the transport profile's 4/8, 8/16, or 16/32 MiB stream/connection -receive windows with QUIC. A Mux carrier has no fixed stream count limit and 512 -queued frame slots; queued payload remains charged against the connection +receive windows with QUIC. A Mux carrier admits at most 4,096 active streams and +has 512 queued frame slots; queued payload remains charged against the connection window. Each flow has at most one DATA frame queued or being written, so a bulk writer cannot fill the shared queue. Receive queues are bounded by byte credit without blocking unrelated flows on per-flow frame counts. The application shares at most eight carriers across both directions and retires -fully idle shards after 30 seconds. TCP, UDP, SOCKS associations, and pending -split pairs have no fixed application flow-count setting. QUIC stream credit +fully idle shards after 30 seconds. The former TCP, UDP, SOCKS association, and +pending split-pair application quotas are absent. Independent resource admission +allows up to 1,024 accepted SOCKS clients and 1,024 active SOCKS UDP targets per +Vector. QUIC stream credit grows with live and pending QUIC flows, reserving setup headroom of at least 64 streams or 25% of that count. -This avoids a fixed active-flow ceiling and excessive eager stream allocation. +This avoids the former application flow quotas and excessive eager stream allocation. Byte budgets and setup, pairing, and idle deadlines apply; per-flow -metadata and target sockets grow with concurrency. +metadata and target sockets stay within the independent resource ceilings. Portal and Vector use the same QUIC profile regardless of the client Mux setting. The stream/connection/send windows are respectively 4/8/8 MiB for `memory`, diff --git a/docs/interoperability.md b/docs/interoperability.md index ddb8786..ab8a24a 100644 --- a/docs/interoperability.md +++ b/docs/interoperability.md @@ -60,8 +60,8 @@ TLS listener. The Mux pool is full duplex and shared by both logical directions. It opens carriers lazily, reuses idle carriers, selects the least occupied carrier at capacity, and contains at most eight connecting or established carriers per -session. A fully idle carrier closes after 30 seconds. Logical stream counts -have no fixed application limit. +session. A fully idle carrier closes after 30 seconds. There is no legacy +application stream quota; each carrier has a 4,096-stream resource ceiling. ## Route contract diff --git a/docs/operations.md b/docs/operations.md index 01e01ae..e4e9bc1 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -57,8 +57,10 @@ the other family. Payload memory is controlled by the selected 4/8, 8/16, or 16/32 MiB per-stream/per-Mux receive windows, bounded reusable relay-buffer caches, and -QUIC UDP queue/reassembly limits. Logical TCP, UDP, SOCKS, and pending-pair -counts have no fixed application cap; metadata and sockets grow with concurrency. +QUIC UDP queue/reassembly limits. The former logical TCP, UDP, SOCKS, and +pending-pair application quotas are absent. Implementation safeguards admit at +most 4,096 active streams per Mux carrier, 1,024 accepted SOCKS clients per +Vector, and 1,024 active SOCKS UDP targets per Vector. TLS shards originated with `mux=1` by Vector or a Portal `next` client adapt their pool to concurrent flow demand, stop at eight carriers per session @@ -85,7 +87,8 @@ reseeded before stream exhaustion. ### TLS Shard placement An originating client shares one full-duplex TLS carrier pool across directions. -Mux and application sessions impose no fixed logical-flow count limit. +There is no legacy logical-flow quota; each Mux carrier has an independent +4,096-stream resource ceiling. ```text new flow --> idle carrier? --> reuse @@ -166,7 +169,7 @@ Functional validation belongs on every deployment platform: up a failed pre-commit attempt; - negotiated protocol version, credentials, certificate verification, and native chains match at both ends; -- flow limits fail promptly instead of waiting for capacity; +- resource admission fails promptly instead of waiting for capacity; - idle Mux Shards and UDP flows retire at their documented deadlines; - graceful shutdown reaches `STOPPED` within the configured deadline; - the local TUI discovers the process without exposing credentials or payload. diff --git a/docs/protocol.md b/docs/protocol.md index 9926145..fbb8334 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -310,7 +310,8 @@ Mux uses an initial 4 MiB stream window and 8 MiB connection window. Each side sends one WINDOW to extend its connection window. OPEN advertises the opener's stream extension; the receiver returns its stream extension with WINDOW. The selected transport profile sets final windows to 4/8, 8/16, or 16/32 MiB. -There is no fixed Mux logical-stream count limit. Each carrier has 512 queued +Each carrier admits at most 4,096 active streams as an implementation resource +ceiling, independent of application flow policy. Each carrier has 512 queued outbound frame slots; each stream may have one DATA frame queued or being written. Payload must obtain both stream and connection credit before it enters the outbound queue. @@ -339,16 +340,16 @@ credit, receive credit, and outbound frame slots; stream count plus pending reservations breaks ties. Connecting slots also accept reservations, so a cold burst does not pile onto the first completed handshake. Each slot shares one initializer; cancellation allows a waiter to retry it. Failed expansion can -fall back to an established carrier. There is -no fixed stream density, latency threshold, background polling, or migration +fall back to an established carrier. There is no stream-density target, latency +threshold, background polling, or migration of established streams. This favors parallel throughput over minimizing the number of carriers for many idle logical streams. Receive queues use byte-credit admission rather than blocking the carrier reader on a per-flow frame count. Every DATA frame consumes at least one KiB of credit, -bounding queued payload and DATA metadata across the carrier. Stream and lifecycle -metadata grow with live/pending streams; no fixed stream limit does not mean -constant process memory. Authentication remains separate from Mux placement. A fully idle carrier closes after 30 +bounding queued payload and DATA metadata across the carrier. Separate OPEN +admission bounds stream and lifecycle metadata at 4,096 active streams per +carrier. Authentication remains separate from Mux placement. A fully idle carrier closes after 30 seconds. Portal applies the same timeout to an authenticated Mux carrier with no active streams. Sharding is runtime placement and does not add wire fields. @@ -596,7 +597,10 @@ ATTACH. ## 11. Runtime limits and failure scope -Application sessions impose no fixed TCP, UDP, or pending-pair count limit. +The former application-level TCP, UDP, and pending-pair quotas are absent. +Independent implementation safeguards admit at most 4,096 active streams per +Mux carrier, 1,024 accepted SOCKS clients per Vector, and 1,024 active SOCKS UDP +targets per Vector. Active flow IDs are unique within `1..=0x3fffffff`. Allocation wraps to 1, skips IDs held by live leases, and fails when the space is exhausted. Released IDs may be reused; this does not provide generation isolation for delayed diff --git a/docs/security.md b/docs/security.md index 39ba38a..d89df51 100644 --- a/docs/security.md +++ b/docs/security.md @@ -91,25 +91,25 @@ and DATA for unknown streams close the carrier. Late terminal and credit frames for a terminal stream are idempotent. The transport memory profile bounds Mux stream/connection windows at 4/8, -8/16, or 16/32 MiB. Mux has no fixed logical-stream count limit. With client `mux=1`, +8/16, or 16/32 MiB. Each Mux carrier admits at most 4,096 active streams. With client `mux=1`, Vector or Portal `next` shares at most eight TLS carriers across both directions, reuses idle carriers before creating more, distributes flows by occupancy at capacity, and closes a fully idle carrier after 30 seconds. Stream and pending lifecycle -metadata remain proportional to admitted streams; byte credit bounds DATA, not -arbitrary OPEN traffic. One +metadata remain proportional to admitted streams; separate OPEN admission bounds +metadata that carries no DATA credit. One authenticated inbound Mux carrier is subject to the same fully idle timeout. -Authenticated sessions have no fixed logical-flow count limit. Stream metadata, -pending pairs, and target sockets grow with concurrency; byte windows do not -bound their total memory use. -Per-stream and connection credit plus bounded channel admission limit how much +The former authenticated-session logical-flow quotas are absent. Independent +resource admission caps Mux streams, accepted SOCKS clients, and active SOCKS UDP +targets; byte windows do not bound those resources. +Per-stream and connection credit plus OPEN admission limit how much one stream can occupy. The finite frame queue has 512 slots, but payload admission is capped by the selected connection window; empty OPEN/FIN/RESET/WINDOW frames cannot turn those slots into retained application payload. These are credit ceilings rather than eagerly allocated payload buffers. -TCP, UoT, and QUIC flows all follow the same policy: byte budgets and lifecycle -timeouts apply without fixed application flow-count admission. QUIC expands +TCP, UoT, and QUIC flows all follow the same policy: byte budgets, lifecycle +timeouts, and resource admission apply without restoring legacy application quotas. QUIC expands stream credit with actual demand instead of preallocating a huge stream ceiling. Operators control aggregate exposure through key distribution, host resource limits, and network-level admission policy. From fead3ee21f80b0eea64cd5630b575927a1dcfada Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 15:04:37 +0800 Subject: [PATCH 57/62] Bound pending Mux delivery across OPEN RESET churn --- docs/configuration.md | 3 ++- docs/protocol.md | 6 ++++-- docs/security.md | 6 ++++-- src/mux/driver.rs | 4 +++- src/mux/handle.rs | 2 +- src/mux/mod.rs | 4 ++-- src/tests/mux/runtime.rs | 23 +++++++++++++++++++++++ 7 files changed, 39 insertions(+), 9 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 598dee4..2b795c0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -338,7 +338,8 @@ grows with live and pending QUIC flows, reserving setup headroom of at least 64 streams or 25% of that count. This avoids the former application flow quotas and excessive eager stream allocation. Byte budgets and setup, pairing, and idle deadlines apply; per-flow -metadata and target sockets stay within the independent resource ceilings. +Mux metadata and Vector SOCKS target tasks have the resource ceilings described +above. These do not impose an aggregate limit on Portal sessions or target sockets. Portal and Vector use the same QUIC profile regardless of the client Mux setting. The stream/connection/send windows are respectively 4/8/8 MiB for `memory`, diff --git a/docs/protocol.md b/docs/protocol.md index fbb8334..6f16a44 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -348,8 +348,10 @@ number of carriers for many idle logical streams. Receive queues use byte-credit admission rather than blocking the carrier reader on a per-flow frame count. Every DATA frame consumes at least one KiB of credit, bounding queued payload and DATA metadata across the carrier. Separate OPEN -admission bounds stream and lifecycle metadata at 4,096 active streams per -carrier. Authentication remains separate from Mux placement. A fully idle carrier closes after 30 +admission caps active streams and pending incoming deliveries separately at +4,096 per carrier. A full incoming queue closes the carrier immediately without +blocking its reader; RESET cannot bypass this queue limit. Authentication remains +separate from Mux placement. A fully idle carrier closes after 30 seconds. Portal applies the same timeout to an authenticated Mux carrier with no active streams. Sharding is runtime placement and does not add wire fields. diff --git a/docs/security.md b/docs/security.md index d89df51..e9c57ad 100644 --- a/docs/security.md +++ b/docs/security.md @@ -95,8 +95,10 @@ The transport memory profile bounds Mux stream/connection windows at 4/8, Vector or Portal `next` shares at most eight TLS carriers across both directions, reuses idle carriers before creating more, distributes flows by occupancy at capacity, and closes a fully idle carrier after 30 seconds. Stream and pending lifecycle -metadata remain proportional to admitted streams; separate OPEN admission bounds -metadata that carries no DATA credit. One +metadata remain proportional to admitted streams. Active streams and pending +incoming deliveries each have a separate 4,096-entry ceiling, so OPEN/RESET churn +cannot grow the delivery queue without bound. Queue overflow closes the carrier +without blocking its reader. One authenticated inbound Mux carrier is subject to the same fully idle timeout. The former authenticated-session logical-flow quotas are absent. Independent resource admission caps Mux streams, accepted SOCKS clients, and active SOCKS UDP diff --git a/src/mux/driver.rs b/src/mux/driver.rs index ec63158..133e485 100644 --- a/src/mux/driver.rs +++ b/src/mux/driver.rs @@ -110,7 +110,9 @@ async fn receive_open(shared: &Arc, header: FrameHeader) -> io::Result<( } credit.add_permits(extra_credit); } - shared.incoming_tx.send(stream).map_err(|_| closed()) + // RESET removes active flow state, but cannot remove an already queued + // stream. Bound pending delivery separately and never block the reader. + shared.incoming_tx.try_send(stream).map_err(|_| closed()) } async fn receive_data(shared: &Arc, header: FrameHeader, payload: Bytes) -> io::Result<()> { diff --git a/src/mux/handle.rs b/src/mux/handle.rs index e6fc60a..290bb21 100644 --- a/src/mux/handle.rs +++ b/src/mux/handle.rs @@ -23,7 +23,7 @@ impl MuxHandle { let config = config.validate()?; let (data_tx, data_rx) = mpsc::channel(config.outbound_frames); let (terminal_tx, terminal_rx) = mpsc::unbounded_channel(); - let (incoming_tx, incoming_rx) = mpsc::unbounded_channel(); + let (incoming_tx, incoming_rx) = mpsc::channel(config.active_stream_limit); let (active_streams_tx, _) = watch::channel(0); let shared = Arc::new(Shared { config, diff --git a/src/mux/mod.rs b/src/mux/mod.rs index a09b9d2..4d0cacd 100644 --- a/src/mux/mod.rs +++ b/src/mux/mod.rs @@ -121,7 +121,7 @@ pub(crate) struct MuxHandle { } pub(crate) struct Incoming { - receiver: mpsc::UnboundedReceiver, + receiver: mpsc::Receiver, } type WriteFuture = Pin> + Send>>; @@ -138,7 +138,7 @@ struct Shared { data_tx: mpsc::Sender, terminal_tx: mpsc::UnboundedSender, control_notify: Notify, - incoming_tx: mpsc::UnboundedSender, + incoming_tx: mpsc::Sender, active_streams_tx: watch::Sender, closed: AtomicBool, closed_notify: tokio_util::sync::CancellationToken, diff --git a/src/tests/mux/runtime.rs b/src/tests/mux/runtime.rs index 10015b9..725bf28 100644 --- a/src/tests/mux/runtime.rs +++ b/src/tests/mux/runtime.rs @@ -30,6 +30,29 @@ async fn more_than_256_live_streams_transfer_and_half_close() { .expect("stream admission must not depend on terminal queue capacity"); } +#[tokio::test] +async fn open_reset_churn_cannot_overflow_pending_incoming_admission() { + let config = MuxConfig { + active_stream_limit: 2, + ..MuxConfig::default() + }; + let (mut peer, carrier) = tokio::io::duplex(4096); + let (server, incoming) = MuxHandle::start(carrier, config).unwrap(); + for flow_id in 1..=3 { + peer.write_all(&encode_header(FrameHeader::open(flow_id, 0).unwrap()).unwrap()) + .await + .unwrap(); + peer.write_all(&encode_header(FrameHeader::close(flow_id, CLOSE_RESET).unwrap()).unwrap()) + .await + .unwrap(); + } + tokio::time::timeout(Duration::from_secs(1), server.closed()) + .await + .expect("RESET must not bypass pending OPEN admission"); + assert_eq!(incoming.receiver.len(), 2); + assert_eq!(server.active_streams(), 0); +} + #[tokio::test] async fn remote_open_admission_closes_carrier_at_the_metadata_limit() { let config = MuxConfig { From 6d7fa56929a4da412b6a059645fa52997fa52793 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 15:16:20 +0800 Subject: [PATCH 58/62] Chunk full-duplex test IO and isolate carrier failures --- src/tests/portal/conn/vector.rs | 100 +++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 26 deletions(-) diff --git a/src/tests/portal/conn/vector.rs b/src/tests/portal/conn/vector.rs index a66a0ec..fe37156 100644 --- a/src/tests/portal/conn/vector.rs +++ b/src/tests/portal/conn/vector.rs @@ -274,32 +274,40 @@ async fn mux_symmetric_carriers_relay_tcp_and_fragmented_udp() { #[tokio::test] async fn mux_full_duplex_tcp_exceeds_each_direction_credit_window() { + full_duplex_exceeds_each_direction_credit_window("tcp").await; +} + +#[tokio::test] +async fn quic_full_duplex_tcp_exceeds_each_direction_credit_window() { + full_duplex_exceeds_each_direction_credit_window("udp").await; +} + +async fn full_duplex_exceeds_each_direction_credit_window(carrier: &str) { // The throughput profile grants 16 MiB per stream. Cross that boundary in // both directions so progress depends on returning Mux credit. const DIRECTION_BYTES: usize = 20 * 1024 * 1024; - for carrier in ["tcp", "udp"] { + { + let progress = Arc::new(std::array::from_fn::<_, 4, _>(|_| { + std::sync::atomic::AtomicUsize::new(0) + })); let target = TcpListener::bind("127.0.0.1:0").await.unwrap(); let target_address = target.local_addr().unwrap(); + let target_progress = progress.clone(); let target_task = tokio::spawn(async move { let (stream, _) = target.accept().await.unwrap(); + stream.set_nodelay(true).unwrap(); let (mut reader, mut writer) = stream.into_split(); - let upload = async { - let mut received = vec![0_u8; DIRECTION_BYTES]; - reader.read_exact(&mut received).await.unwrap(); - assert!(received.iter().all(|byte| *byte == 0xa5)); - }; - let download = async { - writer - .write_all(&vec![0x5a; DIRECTION_BYTES]) - .await - .unwrap(); - }; + let upload = + read_full_duplex_payload(&mut reader, DIRECTION_BYTES, 0xa5, &target_progress[1]); + let download = + write_full_duplex_payload(&mut writer, DIRECTION_BYTES, 0x5a, &target_progress[2]); tokio::join!(upload, download); }); let runtime = start_runtime(carrier, carrier, 1).await; - timeout(FULL_DUPLEX_TIMEOUT, async { + let result = timeout(FULL_DUPLEX_TIMEOUT, async { let mut stream = TcpStream::connect(runtime.socks).await.unwrap(); + stream.set_nodelay(true).unwrap(); negotiate_socks(&mut stream).await; stream .write_all(&ip_request(1, target_address)) @@ -307,26 +315,66 @@ async fn mux_full_duplex_tcp_exceeds_each_direction_credit_window() { .unwrap(); read_ipv4_reply(&mut stream).await; let (mut reader, mut writer) = stream.into_split(); - let upload = async { - writer - .write_all(&vec![0xa5; DIRECTION_BYTES]) - .await - .unwrap(); - }; - let download = async { - let mut received = vec![0_u8; DIRECTION_BYTES]; - reader.read_exact(&mut received).await.unwrap(); - assert!(received.iter().all(|byte| *byte == 0x5a)); - }; + let upload = + write_full_duplex_payload(&mut writer, DIRECTION_BYTES, 0xa5, &progress[0]); + let download = + read_full_duplex_payload(&mut reader, DIRECTION_BYTES, 0x5a, &progress[3]); tokio::join!(upload, download); }) - .await - .unwrap(); + .await; + if result.is_err() { + target_task.abort(); + runtime.stop().await; + panic!( + "{carrier} full-duplex timeout: client sent={}, target received={}, target sent={}, client received={}, expected={DIRECTION_BYTES}", + progress[0].load(Ordering::Relaxed), + progress[1].load(Ordering::Relaxed), + progress[2].load(Ordering::Relaxed), + progress[3].load(Ordering::Relaxed) + ); + } target_task.await.unwrap(); runtime.stop().await; } } +async fn write_full_duplex_payload( + writer: &mut (impl tokio::io::AsyncWrite + Unpin), + total: usize, + byte: u8, + progress: &std::sync::atomic::AtomicUsize, +) { + let chunk = [byte; 32 * 1024]; + let mut sent = 0; + while sent < total { + let count = writer + .write(&chunk[..chunk.len().min(total - sent)]) + .await + .unwrap(); + assert_ne!(count, 0, "full-duplex write made no progress"); + sent += count; + progress.store(sent, Ordering::Relaxed); + } +} + +async fn read_full_duplex_payload( + reader: &mut (impl tokio::io::AsyncRead + Unpin), + total: usize, + byte: u8, + progress: &std::sync::atomic::AtomicUsize, +) { + let mut chunk = [0; 32 * 1024]; + let mut received = 0; + while received < total { + let capacity = chunk.len().min(total - received); + let count = reader.read(&mut chunk[..capacity]).await.unwrap(); + assert_ne!(count, 0, "full-duplex stream ended early"); + assert!(chunk[..count].iter().all(|value| *value == byte)); + received += count; + progress.store(received, Ordering::Relaxed); + } +} + #[tokio::test] async fn mux_hundreds_of_tcp_flows_share_at_most_eight_carriers() { const FLOW_COUNT: usize = 300; From 6d08c9c39e07149151e1d09ba8ea7645ed92d07b Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 16:16:38 +0800 Subject: [PATCH 59/62] Bound Mux terminal delivery queue --- src/mux/driver.rs | 5 +---- src/mux/handle.rs | 2 +- src/mux/mod.rs | 2 +- src/mux/stream.rs | 7 ++++++- src/tests/mux/runtime.rs | 20 ++++++++++++++++++++ 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/mux/driver.rs b/src/mux/driver.rs index 133e485..89bf46d 100644 --- a/src/mux/driver.rs +++ b/src/mux/driver.rs @@ -180,10 +180,7 @@ fn receive_window(shared: &Shared, header: FrameHeader) -> io::Result<()> { Ok(()) } -pub(super) async fn run_terminals( - shared: Arc, - mut terminal_rx: mpsc::UnboundedReceiver, -) { +pub(super) async fn run_terminals(shared: Arc, mut terminal_rx: mpsc::Receiver) { loop { if shared.closed.load(std::sync::atomic::Ordering::Acquire) { return; diff --git a/src/mux/handle.rs b/src/mux/handle.rs index 290bb21..9f7ebab 100644 --- a/src/mux/handle.rs +++ b/src/mux/handle.rs @@ -22,7 +22,7 @@ impl MuxHandle { { let config = config.validate()?; let (data_tx, data_rx) = mpsc::channel(config.outbound_frames); - let (terminal_tx, terminal_rx) = mpsc::unbounded_channel(); + let (terminal_tx, terminal_rx) = mpsc::channel(config.active_stream_limit); let (incoming_tx, incoming_rx) = mpsc::channel(config.active_stream_limit); let (active_streams_tx, _) = watch::channel(0); let shared = Arc::new(Shared { diff --git a/src/mux/mod.rs b/src/mux/mod.rs index 4d0cacd..19602c8 100644 --- a/src/mux/mod.rs +++ b/src/mux/mod.rs @@ -136,7 +136,7 @@ struct Shared { pending_connection_credit: AtomicUsize, ready_flows: Mutex>, data_tx: mpsc::Sender, - terminal_tx: mpsc::UnboundedSender, + terminal_tx: mpsc::Sender, control_notify: Notify, incoming_tx: mpsc::Sender, active_streams_tx: watch::Sender, diff --git a/src/mux/stream.rs b/src/mux/stream.rs index 2083ef7..bd92780 100644 --- a/src/mux/stream.rs +++ b/src/mux/stream.rs @@ -331,7 +331,12 @@ impl Drop for FlowWriter { // already queued DATA without spawning a task for every dropped // stream. Dropping a writer is a half-close: split-direction users // intentionally discard the unused half while retaining the other. - let _ = self.shared.terminal_tx.send(self.flow_id); + if self.shared.terminal_tx.try_send(self.flow_id).is_err() { + // Drop cannot wait for terminal delivery. A full queue means + // the peer is not draining control traffic, so fail the + // carrier before terminal metadata can grow without bound. + self.shared.close(); + } } self.shared.release_part(self.flow_id); } diff --git a/src/tests/mux/runtime.rs b/src/tests/mux/runtime.rs index 725bf28..961d7b5 100644 --- a/src/tests/mux/runtime.rs +++ b/src/tests/mux/runtime.rs @@ -53,6 +53,26 @@ async fn open_reset_churn_cannot_overflow_pending_incoming_admission() { assert_eq!(server.active_streams(), 0); } +#[tokio::test] +async fn dropped_writers_close_carrier_when_terminal_delivery_is_saturated() { + let config = MuxConfig { + active_stream_limit: 2, + ..MuxConfig::default() + }; + let (_peer, carrier) = tokio::io::duplex(1); + let (handle, _incoming) = MuxHandle::start(carrier, config).unwrap(); + + // No await gives the terminal dispatcher no opportunity to drain between + // drops. Flow state is released each time, so only the terminal queue can + // bound this churn. + for flow_id in 1..=3 { + drop(handle.prepare_stream(flow_id).unwrap()); + } + + assert!(handle.is_closed()); + assert_eq!(handle.active_streams(), 0); +} + #[tokio::test] async fn remote_open_admission_closes_carrier_at_the_metadata_limit() { let config = MuxConfig { From 1449d24ac510ad1167e9cb784d349ea78bf3ec70 Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 16:16:38 +0800 Subject: [PATCH 60/62] Bound Portal flow claim admission --- src/portal/pairing.rs | 50 +++++++++++-- src/portal/pairing/state.rs | 17 ++++- src/portal/pairing/tcp.rs | 3 + src/portal/pairing/udp.rs | 3 + src/tests/portal/pairing/lifecycle.rs | 101 ++++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 10 deletions(-) diff --git a/src/portal/pairing.rs b/src/portal/pairing.rs index 6340722..f650ccc 100644 --- a/src/portal/pairing.rs +++ b/src/portal/pairing.rs @@ -7,11 +7,11 @@ use std::collections::{HashMap, HashSet}; use std::fmt; use std::sync::Arc; use std::sync::Mutex as StdMutex; -use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; use std::time::{Duration, Instant}; use tokio::io::AsyncWriteExt; -use tokio::sync::Mutex; +use tokio::sync::{Mutex, Semaphore}; use crate::protocol::{ Carrier, FlowErrorCode, FlowHeader, FlowKind, FlowResult, FlowRole, Target, write_flow_result, @@ -34,6 +34,8 @@ use self::tcp::reject_tcp_writer; use self::udp::reject_udp_downlink_ref; const FLOW_RESULT_WRITE_TIMEOUT: Duration = Duration::from_secs(1); +const SESSION_FLOW_RESOURCE_LIMIT: usize = 4096; +const PORTAL_FLOW_RESOURCE_LIMIT: usize = 65_536; #[derive(Clone, Copy)] struct TerminalRejection { @@ -70,6 +72,7 @@ pub(super) struct PairingRegistry { pub(super) udp: Mutex>, pub(super) links: StdMutex>, claims: StdMutex>, + claim_admission: Arc, rejections: StdMutex>, accepting: AtomicBool, pub(super) next_quic_generation: AtomicU64, @@ -87,6 +90,7 @@ impl PairingRegistry { udp: Mutex::new(HashMap::new()), links: StdMutex::new(HashMap::new()), claims: StdMutex::new(HashMap::new()), + claim_admission: Arc::new(Semaphore::new(PORTAL_FLOW_RESOURCE_LIMIT)), rejections: StdMutex::new(HashMap::new()), accepting: AtomicBool::new(true), next_quic_generation: AtomicU64::new(1), @@ -180,7 +184,8 @@ impl PairingRegistry { metadata: Metadata, target: Option, quic_generation: Option, - quic_count: Option>, + quic_count: Option>, + session_admission: Option>, ) -> Result<(u64, bool), PairingError> { let mut claims = self.claims.lock().expect("flow claim registry poisoned"); // The claims lock is the drain/admission linearization point. Once @@ -218,6 +223,28 @@ impl PairingRegistry { return Ok((claim.epoch, false)); } let epoch = self.next_epoch.fetch_add(1, Ordering::Relaxed); + let session_admission = session_admission.ok_or_else(|| { + PairingError::new( + FlowErrorCode::SessionReplaced, + "portal::pairing: session disappeared before flow admission", + ) + })?; + let session_admission = session_admission.try_acquire_owned().map_err(|_| { + PairingError::new( + FlowErrorCode::FlowLimit, + "portal::pairing: session flow resource limit reached", + ) + })?; + let portal_admission = self + .claim_admission + .clone() + .try_acquire_owned() + .map_err(|_| { + PairingError::new( + FlowErrorCode::FlowLimit, + "portal::pairing: Portal flow resource limit reached", + ) + })?; let quic_count = quic_count .filter(|_| metadata.uplink == Carrier::Quic || metadata.downlink == Carrier::Quic); if let Some(count) = &quic_count { @@ -226,6 +253,8 @@ impl PairingRegistry { claims.insert( key, FlowClaim { + _portal_admission: portal_admission, + _session_admission: session_admission, quic_count, epoch, metadata, @@ -238,10 +267,7 @@ impl PairingRegistry { Ok((epoch, true)) } - fn quic_flow_counter( - &self, - session_id: SessionKey, - ) -> Option> { + fn quic_flow_counter(&self, session_id: SessionKey) -> Option> { self.links .lock() .expect("link registry poisoned") @@ -249,6 +275,14 @@ impl PairingRegistry { .map(|counts| counts.quic_flows.clone()) } + fn session_flow_admission(&self, session_id: SessionKey) -> Option> { + self.links + .lock() + .expect("link registry poisoned") + .get(&session_id) + .map(|counts| counts.flow_admission.clone()) + } + pub(super) fn quic_stream_credit(&self, session_id: SessionKey) -> quinn::VarInt { // Quinn preallocates stream state. Keep a sliding headroom for setup, // instead of advertising a huge fixed count or capping active flows. @@ -259,7 +293,7 @@ impl PairingRegistry { // must grow too, otherwise a fixed reserve eventually stalls updates. quinn::VarInt::from_u32( live.saturating_add((live / 4).max(64)) - .min(u32::MAX as usize) as u32, + .min(SESSION_FLOW_RESOURCE_LIMIT) as u32, ) } diff --git a/src/portal/pairing/state.rs b/src/portal/pairing/state.rs index 086c3da..eef6c4c 100644 --- a/src/portal/pairing/state.rs +++ b/src/portal/pairing/state.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use quinn::Connection; -use tokio::sync::{mpsc, oneshot}; +use tokio::sync::{OwnedSemaphorePermit, Semaphore, mpsc, oneshot}; use tokio_util::sync::CancellationToken; use crate::protocol::{FlowKind, SessionId, Target}; @@ -198,11 +198,22 @@ pub(in crate::portal) struct PairedTcp { pub(in crate::portal) _flow_lease: FlowLease, } -#[derive(Default)] pub(in crate::portal) struct LinkCounts { pub(in crate::portal) tcp: usize, pub(in crate::portal) udp: Option, pub(in crate::portal) quic_flows: Arc, + pub(in crate::portal) flow_admission: Arc, +} + +impl Default for LinkCounts { + fn default() -> Self { + Self { + tcp: 0, + udp: None, + quic_flows: Arc::new(AtomicUsize::new(0)), + flow_admission: Arc::new(Semaphore::new(super::SESSION_FLOW_RESOURCE_LIMIT)), + } + } } pub(in crate::portal) struct ActiveQuic { @@ -211,6 +222,8 @@ pub(in crate::portal) struct ActiveQuic { } pub(in crate::portal) struct FlowClaim { + pub(in crate::portal) _portal_admission: OwnedSemaphorePermit, + pub(in crate::portal) _session_admission: OwnedSemaphorePermit, pub(in crate::portal) quic_count: Option>, pub(in crate::portal) epoch: u64, pub(in crate::portal) metadata: Metadata, diff --git a/src/portal/pairing/tcp.rs b/src/portal/pairing/tcp.rs index c808df5..003f062 100644 --- a/src/portal/pairing/tcp.rs +++ b/src/portal/pairing/tcp.rs @@ -32,6 +32,7 @@ impl PairingRegistry { ) -> Result, PairingError> { let session_id = session_id.into(); let quic_count = self.quic_flow_counter(session_id); + let session_admission = self.session_flow_admission(session_id); if let Err(err) = self.validate_header_and_link(session_id, header, FlowKind::Tcp, target.as_ref(), &link) { @@ -81,6 +82,7 @@ impl PairingRegistry { target.clone(), link.quic_generation, quic_count.clone(), + session_admission.clone(), ) { Ok(claim) => claim, Err(err) => { @@ -196,6 +198,7 @@ impl PairingRegistry { target.clone(), link.quic_generation, quic_count.clone(), + session_admission.clone(), ) { Ok(claim) => claim, Err(error) => { diff --git a/src/portal/pairing/udp.rs b/src/portal/pairing/udp.rs index 7993a69..103e47e 100644 --- a/src/portal/pairing/udp.rs +++ b/src/portal/pairing/udp.rs @@ -26,6 +26,7 @@ impl PairingRegistry { ) -> Result, PairingError> { let session_id = session_id.into(); let quic_count = self.quic_flow_counter(session_id); + let session_admission = self.session_flow_admission(session_id); if let Err(err) = self.validate_header_and_link(session_id, header, FlowKind::Udp, target.as_ref(), &link) { @@ -78,6 +79,7 @@ impl PairingRegistry { target.clone(), link.quic_generation, quic_count.clone(), + session_admission.clone(), ) { Ok(claim) => claim, Err(err) => { @@ -196,6 +198,7 @@ impl PairingRegistry { target.clone(), link.quic_generation, quic_count.clone(), + session_admission.clone(), ) { Ok(claim) => claim, Err(error) => { diff --git a/src/tests/portal/pairing/lifecycle.rs b/src/tests/portal/pairing/lifecycle.rs index 60726db..dafeac3 100644 --- a/src/tests/portal/pairing/lifecycle.rs +++ b/src/tests/portal/pairing/lifecycle.rs @@ -275,3 +275,104 @@ async fn pending_pairs_exceed_former_limit_and_release_quic_credit_on_drain() { assert!(registry.claims.lock().unwrap().is_empty()); assert_eq!(registry.quic_stream_credit(session).into_inner(), 64); } + +#[tokio::test] +async fn session_claim_admission_is_bounded_and_reusable() { + let registry = registry(Duration::from_secs(30)); + let session = [0x33; SESSION_ID_LEN]; + let _guard = registry.register_tcp_link(session, Arc::new(Stats::default())); + let admission = registry.session_flow_admission(session).unwrap(); + let _held = admission + .clone() + .try_acquire_many_owned((SESSION_FLOW_RESOURCE_LIMIT - 1) as u32) + .unwrap(); + + let first = submit_resource_test_flow(®istry, session, 1) + .await + .unwrap() + .unwrap(); + let error = submit_resource_test_flow(®istry, session, 2) + .await + .unwrap_pairing_error(); + assert_eq!(error.code(), FlowErrorCode::FlowLimit); + + drop(first); + assert!( + submit_resource_test_flow(®istry, session, 3) + .await + .unwrap() + .is_some() + ); +} + +#[tokio::test] +async fn portal_claim_admission_is_bounded_and_reusable() { + let registry = registry(Duration::from_secs(30)); + let first_session = [0x34; SESSION_ID_LEN]; + let second_session = [0x35; SESSION_ID_LEN]; + let stats = Arc::new(Stats::default()); + let _first_guard = registry.register_tcp_link(first_session, stats.clone()); + let _second_guard = registry.register_tcp_link(second_session, stats); + let _held = registry + .claim_admission + .clone() + .try_acquire_many_owned((PORTAL_FLOW_RESOURCE_LIMIT - 1) as u32) + .unwrap(); + + let first = submit_resource_test_flow(®istry, first_session, 1) + .await + .unwrap() + .unwrap(); + let error = submit_resource_test_flow(®istry, second_session, 1) + .await + .unwrap_pairing_error(); + assert_eq!(error.code(), FlowErrorCode::FlowLimit); + + drop(first); + assert!( + submit_resource_test_flow(®istry, second_session, 2) + .await + .unwrap() + .is_some() + ); +} + +#[test] +fn quic_stream_credit_is_clamped_to_the_session_claim_budget() { + let registry = registry(Duration::from_secs(30)); + let session = [0x36; SESSION_ID_LEN]; + let _guard = registry.register_tcp_link(session, Arc::new(Stats::default())); + let counter = registry.quic_flow_counter(session).unwrap(); + counter.store(SESSION_FLOW_RESOURCE_LIMIT * 2, Ordering::Relaxed); + + assert_eq!( + registry.quic_stream_credit(session).into_inner(), + SESSION_FLOW_RESOURCE_LIMIT as u64 + ); +} + +async fn submit_resource_test_flow( + registry: &Arc, + session: [u8; SESSION_ID_LEN], + flow_id: u32, +) -> Result, PairingError> { + let (uplink, _uplink_peer) = tokio::io::duplex(64); + let (downlink, _downlink_peer) = tokio::io::duplex(64); + registry + .submit_tcp( + session, + header( + FlowRole::Duplex, + flow_id, + FlowKind::Tcp, + Carrier::TlsTcp, + Carrier::TlsTcp, + ), + Some(target("target.test:443")), + tcp_half("resource"), + Some(Box::pin(uplink)), + Some(Box::pin(downlink)), + None, + ) + .await +} From 4c777daef365824016cb64dba2c4cb5cfc92da7f Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 16:16:38 +0800 Subject: [PATCH 61/62] Document terminal and claim resource ceilings --- docs/configuration.md | 6 ++++-- docs/operations.md | 8 +++++--- docs/protocol.md | 16 ++++++++++------ docs/security.md | 16 +++++++++------- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 2b795c0..41c24e1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -333,9 +333,11 @@ shares at most eight carriers across both directions and retires fully idle shards after 30 seconds. The former TCP, UDP, SOCKS association, and pending split-pair application quotas are absent. Independent resource admission allows up to 1,024 accepted SOCKS clients and 1,024 active SOCKS UDP targets per -Vector. QUIC stream credit +Vector. Portal pairing admits up to 4,096 active or pending claims per +authenticated session and 65,536 total. QUIC stream credit grows with live and pending QUIC -flows, reserving setup headroom of at least 64 streams or 25% of that count. +flows, reserving setup headroom of at least 64 streams or 25% of that count and +stopping at the per-session claim budget. This avoids the former application flow quotas and excessive eager stream allocation. Byte budgets and setup, pairing, and idle deadlines apply; per-flow Mux metadata and Vector SOCKS target tasks have the resource ceilings described diff --git a/docs/operations.md b/docs/operations.md index e4e9bc1..86f127c 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -60,7 +60,8 @@ per-stream/per-Mux receive windows, bounded reusable relay-buffer caches, and QUIC UDP queue/reassembly limits. The former logical TCP, UDP, SOCKS, and pending-pair application quotas are absent. Implementation safeguards admit at most 4,096 active streams per Mux carrier, 1,024 accepted SOCKS clients per -Vector, and 1,024 active SOCKS UDP targets per Vector. +Vector, and 1,024 active SOCKS UDP targets per Vector. Portal pairing admits at +most 4,096 active or pending claims per authenticated session and 65,536 total. TLS shards originated with `mux=1` by Vector or a Portal `next` client adapt their pool to concurrent flow demand, stop at eight carriers per session @@ -68,8 +69,9 @@ across both directions, use lowest-occupancy placement, and close after 30 seconds fully idle. Frame queue slots do not bypass byte credit. Windows are granted as permits and payload is admitted incrementally. -QUIC stream credit grows with live and pending QUIC flows plus setup headroom. -Pairing and setup deadlines reclaim incomplete requests. +QUIC stream credit grows with live and pending QUIC flows plus setup headroom, +then stops at the 4,096-claim session budget. Pairing and setup deadlines reclaim +incomplete requests. QUIC uses the shared `throughput` memory profile by default. Select `balanced` or `memory` when connection density matters more than a single flow's diff --git a/docs/protocol.md b/docs/protocol.md index 6f16a44..8de8b75 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -312,7 +312,8 @@ stream extension; the receiver returns its stream extension with WINDOW. The selected transport profile sets final windows to 4/8, 8/16, or 16/32 MiB. Each carrier admits at most 4,096 active streams as an implementation resource ceiling, independent of application flow policy. Each carrier has 512 queued -outbound frame slots; each stream may have one DATA frame queued or being written. +outbound frame slots and 4,096 queued terminal-delivery slots; each stream may +have one DATA frame queued or being written. Payload must obtain both stream and connection credit before it enters the outbound queue. @@ -348,9 +349,10 @@ number of carriers for many idle logical streams. Receive queues use byte-credit admission rather than blocking the carrier reader on a per-flow frame count. Every DATA frame consumes at least one KiB of credit, bounding queued payload and DATA metadata across the carrier. Separate OPEN -admission caps active streams and pending incoming deliveries separately at -4,096 per carrier. A full incoming queue closes the carrier immediately without -blocking its reader; RESET cannot bypass this queue limit. Authentication remains +admission caps active streams, pending incoming deliveries, and pending terminal +deliveries separately at 4,096 per carrier. A full incoming or terminal queue +closes the carrier immediately without blocking its reader; OPEN/RESET churn +cannot bypass these queue limits. Authentication remains separate from Mux placement. A fully idle carrier closes after 30 seconds. Portal applies the same timeout to an authenticated Mux carrier with no active streams. Sharding is runtime placement and does not add wire fields. @@ -602,14 +604,16 @@ ATTACH. The former application-level TCP, UDP, and pending-pair quotas are absent. Independent implementation safeguards admit at most 4,096 active streams per Mux carrier, 1,024 accepted SOCKS clients per Vector, and 1,024 active SOCKS UDP -targets per Vector. +targets per Vector. Portal admits at most 4,096 active or pending claims per +authenticated session and 65,536 claims across its pairing registry. Active flow IDs are unique within `1..=0x3fffffff`. Allocation wraps to 1, skips IDs held by live leases, and fails when the space is exhausted. Released IDs may be reused; this does not provide generation isolation for delayed messages. Byte flow control, queue budgets, and pairing/setup timeouts apply. QUIC bidirectional-stream credit grows with live and pending QUIC flows, with -setup headroom of max(64, live / 4). A QUIC TCP flow owns one reliable stream; +setup headroom of max(64, live / 4), and is clamped to the 4,096-claim session +budget. A QUIC TCP flow owns one reliable stream; a QUIC UDP flow owns one reliable control stream plus its DATAGRAM route. This is sliding transport credit, not a fixed application concurrency ceiling. diff --git a/docs/security.md b/docs/security.md index e9c57ad..e90174e 100644 --- a/docs/security.md +++ b/docs/security.md @@ -95,14 +95,16 @@ The transport memory profile bounds Mux stream/connection windows at 4/8, Vector or Portal `next` shares at most eight TLS carriers across both directions, reuses idle carriers before creating more, distributes flows by occupancy at capacity, and closes a fully idle carrier after 30 seconds. Stream and pending lifecycle -metadata remain proportional to admitted streams. Active streams and pending -incoming deliveries each have a separate 4,096-entry ceiling, so OPEN/RESET churn -cannot grow the delivery queue without bound. Queue overflow closes the carrier -without blocking its reader. One +metadata remain proportional to admitted streams. Active streams, pending +incoming deliveries, and terminal deliveries each have a separate 4,096-entry +ceiling, so OPEN/RESET churn cannot grow either delivery queue without bound. +Queue overflow closes the carrier without blocking its reader. One authenticated inbound Mux carrier is subject to the same fully idle timeout. The former authenticated-session logical-flow quotas are absent. Independent -resource admission caps Mux streams, accepted SOCKS clients, and active SOCKS UDP -targets; byte windows do not bound those resources. +resource admission caps Mux streams, accepted SOCKS clients, active SOCKS UDP +targets, and Portal flow claims. Each authenticated Portal session admits 4,096 +active or pending claims, with 65,536 across the pairing registry; byte windows +do not bound those resources. Per-stream and connection credit plus OPEN admission limit how much one stream can occupy. The finite frame queue has 512 slots, but payload admission is capped by the selected connection window; empty @@ -112,7 +114,7 @@ allocated payload buffers. TCP, UoT, and QUIC flows all follow the same policy: byte budgets, lifecycle timeouts, and resource admission apply without restoring legacy application quotas. QUIC expands -stream credit with actual demand instead of preallocating a huge stream ceiling. +stream credit with actual demand and clamps it to the session claim budget. Operators control aggregate exposure through key distribution, host resource limits, and network-level admission policy. From cfabb09eb62abadcae1d9e61a9b1a1c9c55a7e8c Mon Sep 17 00:00:00 2001 From: yosebyte Date: Fri, 11 Sep 2026 17:07:21 +0800 Subject: [PATCH 62/62] Normalize legacy Portal wildcard syntax --- src/main.rs | 40 +++++++++++++--------------------------- src/tests/main.rs | 33 +++++++++++++++------------------ 2 files changed, 28 insertions(+), 45 deletions(-) diff --git a/src/main.rs b/src/main.rs index c99a2d2..0ef88d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -196,28 +196,24 @@ async fn start(args: Vec) -> Result<()> { } let command_url = parse_command_url(&args[1]).with_context(|| "invalid configuration URL")?; - let scheme = command_url.url.scheme().to_string(); + let scheme = command_url.scheme().to_string(); if !matches!(scheme.as_str(), "portal" | "vector") { bail!("invalid configuration URL: scheme must be portal or vector, found {scheme:?}"); } // Startup only needs `log` here. Each role parses its own configuration, // including Portal's intentionally ignored upstream options when `next` // is disabled. - let query = query_first(&command_url.url, &["log"]) - .with_context(|| "invalid configuration URL query")?; + let query = + query_first(&command_url, &["log"]).with_context(|| "invalid configuration URL query")?; let logger = init_logger(query.get("log").map(String::as_str))?; match scheme.as_str() { "portal" => { - let portal = Portal::new_with_listen_host( - command_url.url, - command_url.listen_host.as_deref(), - logger, - )?; + let portal = Portal::new(command_url, logger)?; portal.run().await } "vector" => { - let vector = Vector::new(command_url.url, logger)?; + let vector = Vector::new(command_url, logger)?; vector.run().await } _ => unreachable!("scheme was validated above"), @@ -239,32 +235,22 @@ fn print_help() { ); } -struct CommandUrl { - url: Url, - listen_host: Option, -} - -fn parse_command_url(raw: &str) -> Result { +fn parse_command_url(raw: &str) -> Result { validate_endpoint_url_input(raw, "endpoint")?; match Url::parse(raw) { - Ok(url) => Ok(CommandUrl { - url, - listen_host: None, - }), + Ok(url) => Ok(url), Err(ParseError::EmptyHost) => { - let normalized = normalize_empty_portal_host(raw) + let normalized = normalize_legacy_empty_portal_host(raw) .ok_or(ParseError::EmptyHost) .and_then(|url| Url::parse(&url))?; - Ok(CommandUrl { - url: normalized, - listen_host: Some(String::new()), - }) + Ok(normalized) } Err(err) => Err(err.into()), } } -fn normalize_empty_portal_host(raw: &str) -> Option { +/// Converts the V1 compact wildcard alias into the canonical V2 host form. +fn normalize_legacy_empty_portal_host(raw: &str) -> Option { let prefix = "portal://"; let rest = raw.strip_prefix(prefix)?; let authority_len = rest.find(['/', '?', '#']).unwrap_or(rest.len()); @@ -276,10 +262,10 @@ fn normalize_empty_portal_host(raw: &str) -> Option { return None; } - let mut normalized = String::with_capacity(raw.len() + "localhost".len()); + let mut normalized = String::with_capacity(raw.len() + 1); normalized.push_str(prefix); normalized.push_str(&authority[..host_port_start]); - normalized.push_str("localhost"); + normalized.push('*'); normalized.push_str(host_port); normalized.push_str(suffix); Some(normalized) diff --git a/src/tests/main.rs b/src/tests/main.rs index aec9baf..f758531 100644 --- a/src/tests/main.rs +++ b/src/tests/main.rs @@ -71,10 +71,9 @@ fn parse_command_url_keeps_vector_remote_host() { "vector://secret@relay.example:2000?up=udp&down=tcp&socks=127.0.0.1:1080", ) .unwrap(); - assert_eq!(parsed.url.scheme(), "vector"); - assert_eq!(parsed.url.host_str(), Some("relay.example")); - assert_eq!(parsed.url.port(), Some(2000)); - assert_eq!(parsed.listen_host, None); + assert_eq!(parsed.scheme(), "vector"); + assert_eq!(parsed.host_str(), Some("relay.example")); + assert_eq!(parsed.port(), Some(2000)); } #[test] @@ -86,16 +85,15 @@ fn logger_rejects_unknown_or_empty_levels() { } #[test] -fn parse_command_url_accepts_empty_listen_host() { +fn parse_command_url_normalizes_legacy_empty_listen_host() { let parsed = parse_command_url("portal://secret@:2000?log=none&dial=::1").unwrap(); - assert_eq!(parsed.url.scheme(), "portal"); - assert_eq!(parsed.url.username(), "secret"); - assert_eq!(parsed.url.port(), Some(2000)); - assert_eq!(parsed.listen_host.as_deref(), Some("")); + assert_eq!(parsed.scheme(), "portal"); + assert_eq!(parsed.username(), "secret"); + assert_eq!(parsed.host_str(), Some("*")); + assert_eq!(parsed.port(), Some(2000)); assert_eq!( parsed - .url .query_pairs() .find(|(key, _)| key == "dial") .map(|(_, value)| value.into_owned()) @@ -105,13 +103,13 @@ fn parse_command_url_accepts_empty_listen_host() { } #[test] -fn parse_command_url_accepts_empty_listen_host_without_userinfo() { +fn parse_command_url_normalizes_legacy_empty_host_without_userinfo() { let parsed = parse_command_url("portal://:2000").unwrap(); - assert_eq!(parsed.url.scheme(), "portal"); - assert_eq!(parsed.url.username(), ""); - assert_eq!(parsed.url.port(), Some(2000)); - assert_eq!(parsed.listen_host.as_deref(), Some("")); + assert_eq!(parsed.scheme(), "portal"); + assert_eq!(parsed.username(), ""); + assert_eq!(parsed.host_str(), Some("*")); + assert_eq!(parsed.port(), Some(2000)); } #[test] @@ -123,9 +121,8 @@ fn parse_command_url_rejects_empty_host_for_explicit_carriers() { fn parse_command_url_keeps_normal_hosts() { let parsed = parse_command_url("portal://secret@[::]:2000?dial=auto").unwrap(); - assert_eq!(parsed.url.host_str(), Some("[::]")); - assert_eq!(parsed.url.port(), Some(2000)); - assert_eq!(parsed.listen_host, None); + assert_eq!(parsed.host_str(), Some("[::]")); + assert_eq!(parsed.port(), Some(2000)); } #[tokio::test]

    - Live operations · Architecture · Quick start · + Live operations · Documentation · Wire protocol