feat(transport,cli,tunnel): v3.4 port auto-detect + bug fixes from live test
Live macOS test against the production server uncovered six bugs (one of which turned out to be a port collision with sing-box, not a real bug); this commit addresses all of them and adds v3.4 port discovery so the same collision is handled transparently next time. ## v3.4 server port-discovery - Defaults moved off 443/444 to 8443/8443/8444 (TransportSection::default, ServerInitOpts, ProvisionClientOpts, CLI flags). 443 is heavily contested in practice (sing-box, Hysteria2, reverse proxies) and the previous default silently lost the bind when a co-tenant was already there. - MultiServer::bind_with_outer_or_scan: scans forward up to DEFAULT_PORT_SCAN_MAX (20) candidates per transport when the requested port is occupied; QUIC keeps walking if it lands on the custom-UDP port. - MultiServer::bound_addrs(): the actual addresses each transport bound to. - Server logs the bound addresses and writes a runtime snapshot (server.toml.runtime.json) when they differ from the requested ones, so `aura sign-bridges` can re-sign the bridges manifest later. - BridgeManifest gains an optional `endpoints: Vec<BridgeEndpoint>` field with per-transport ports. Backward-compatible: old v3.3 clients ignore the field and continue to use the v1 `bridges` line. - `aura sign-bridges --endpoints HOST:tcp=N:quic=N:udp=N` to mint v3.4 manifests; bridges line is auto-synthesised for v3.3 clients. ## Bug fixes from the live test - macOS TUN naming (#41): the tun crate rejects names that don't match ^utun[0-9]+$. On macOS we now substitute `""` (kernel auto-assigns utunN), capture the assigned name via inner.tun_name(), and propagate it through to os_routes::OsRouteGuard::install — so `route add -interface utunN` uses the real interface, not "aura0". - Packet counters (#42): Stats { tx_packets, rx_packets } are now actually bumped by the data path. `aura status` shows live numbers instead of permanent zeros. - render_client_toml schema (#44): provisioner emits proper `[[tunnel.split.vpn]] cidr = "..."` / `[[tunnel.split.direct]]` blocks from new --vpn-cidrs / --direct-cidrs flags. The v3.3 `vpn_cidrs = [...]` flat array was silently ignored by serde, leaving users with `rules: 0` even when their CIDRs looked right. - #43 / #46 (TCP/443 dial early-eof / no payload back): diagnosed as the sing-box port collision, not an Aura bug. The v3.4 port-scan path makes it go away — the server picks a free port and clients learn it from the manifest. ## Test coverage Three new unit tests for the port-scanner (UDP busy, TCP busy, zero budget); two new tests for v3.4 BridgeManifest round-trip with endpoints; one integration test for the new `[[tunnel.split.vpn]]` rendering; tests for the runtime-state file write/read round-trip; agent-added router-counter tests in aura-tunnel/tests/routes.rs. cargo test --workspace, cargo clippy --workspace -- -D warnings, and cargo fmt --check all pass. #45 (silent client exit when underlying QUIC transport breaks) is still outstanding — needs deeper investigation; deferred to a follow-up. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -695,9 +695,14 @@ impl Default for TransportSection {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
order: default_transport_order(),
|
||||
udp_port: 443,
|
||||
tcp_port: 443,
|
||||
quic_port: 444,
|
||||
// v3.4: defaults moved off 443/444 because in practice 443 is heavily contested
|
||||
// (sing-box, Hysteria2, Cloudflare tunnels, ...). Picking 8443/8444 gives us a free
|
||||
// port on most boxes; servers that *do* want 443 still set it explicitly in
|
||||
// server.toml. The provisioned client.toml is always re-generated from the server's
|
||||
// actually-bound ports (see [crate::bridges::BridgeManifest] v2).
|
||||
udp_port: 8443,
|
||||
tcp_port: 8443,
|
||||
quic_port: 8444,
|
||||
obfuscate: true,
|
||||
masquerade: true,
|
||||
masks: MasksSection::default(),
|
||||
@@ -1547,16 +1552,17 @@ pool_cidr = "10.7.0.0/24"
|
||||
assert_eq!(cfg.tunnel.mtu, 1420);
|
||||
assert!(!cfg.mimicry.padding);
|
||||
|
||||
// Omitting [transport] yields the backward-compatible defaults (udp/tcp/quic on 443/443/444).
|
||||
// v3.4: omitting [transport] yields defaults of udp/tcp/quic on 8443/8443/8444 (was
|
||||
// 443/443/444 before; moved to dodge sing-box/Hysteria2 on 443).
|
||||
assert_eq!(cfg.transport.order, vec!["udp", "tcp", "quic"]);
|
||||
assert_eq!(cfg.transport.udp_port, 443);
|
||||
assert_eq!(cfg.transport.tcp_port, 443);
|
||||
assert_eq!(cfg.transport.quic_port, 444);
|
||||
assert_eq!(cfg.transport.udp_port, 8443);
|
||||
assert_eq!(cfg.transport.tcp_port, 8443);
|
||||
assert_eq!(cfg.transport.quic_port, 8444);
|
||||
assert!(cfg.transport.obfuscate);
|
||||
assert!(cfg.transport.masquerade);
|
||||
let eps = cfg.transport_endpoints().expect("default endpoints");
|
||||
assert_eq!(eps.udp.unwrap().to_string(), "0.0.0.0:443");
|
||||
assert_eq!(eps.quic.unwrap().to_string(), "0.0.0.0:444");
|
||||
assert_eq!(eps.udp.unwrap().to_string(), "0.0.0.0:8443");
|
||||
assert_eq!(eps.quic.unwrap().to_string(), "0.0.0.0:8444");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1709,9 +1715,12 @@ local_ip = "10.7.0.2"
|
||||
dial.order,
|
||||
vec![TransportMode::Udp, TransportMode::Tcp, TransportMode::Quic]
|
||||
);
|
||||
assert_eq!(dial.endpoints.udp.unwrap().to_string(), "1.2.3.4:443");
|
||||
assert_eq!(dial.endpoints.tcp.unwrap().to_string(), "1.2.3.4:443");
|
||||
assert_eq!(dial.endpoints.quic.unwrap().to_string(), "1.2.3.4:444");
|
||||
// v3.4: when [transport] is omitted the defaults are 8443/8443/8444 (was 443/443/444 in
|
||||
// v3.3); the `server_addr` port is informational here — actual transport ports come from
|
||||
// [transport] *_port.
|
||||
assert_eq!(dial.endpoints.udp.unwrap().to_string(), "1.2.3.4:8443");
|
||||
assert_eq!(dial.endpoints.tcp.unwrap().to_string(), "1.2.3.4:8443");
|
||||
assert_eq!(dial.endpoints.quic.unwrap().to_string(), "1.2.3.4:8444");
|
||||
}
|
||||
|
||||
/// `[server.pool]` is parsed in full (cidr + strategy + static reservations) and
|
||||
|
||||
Reference in New Issue
Block a user