Files
chanora/crates/chanora_protocol/Cargo.toml
T
EdisonJwa bc0da50cdb feat(protocol): A.1 — fix hostname resolution on Android and iOS
Resolves the Beta-blocking issue surfaced during Android v0.2.0-beta.1
verification: hostnames could not be used, only literal IPs.

Root cause:
  tsclientlib's built-in resolver uses hickory-resolver, which reads
  /etc/resolv.conf. That file does not exist on Android or iOS, so
  any connect by hostname exited the connection task before
  signalling ready and surfaced the cryptic error
    BridgeError.connection(field0: protocol backend:
      connection task exited before signalling ready)

Fix:
  crates/chanora_protocol/src/resolver.rs (new):
    Resolves hostnames via tokio::net::lookup_host, which uses the
    platform's getaddrinfo. Works on every platform Chanora targets.
    Tiny in-process positive-result cache (5 min TTL) keeps
    reconnects cheap. IPv4 sorted ahead of IPv6 in the returned list
    to favour the more reliable path on dual-stack networks.

  crates/chanora_protocol/src/adapter.rs:
    connection_task now resolves the hostname itself and passes the
    resulting SocketAddr (not the hostname String) to
    tsclientlib::Connection::build. tsclientlib's ServerAddress enum
    accepts SocketAddr via its From impl, so the upstream resolver
    is skipped entirely.

  crates/chanora_protocol/src/lib.rs:
    New typed error arm ProtocolError::DnsFailed { host, reason }
    so the UI can distinguish 'server not found' from 'server
    refused our packets'.

  crates/chanora_bridge/src/lib.rs:
    Matching BridgeError::DnsFailed { host, reason } DTO surfaced
    to Dart, with explicit From<CoreError::Protocol(DnsFailed)>
    mapping so the UI gets the structured fields rather than a
    stringified mess.

Tests added (crates/chanora_protocol/src/resolver.rs::tests):
  - rejects_empty
  - literal_ipv4_short_circuits
  - literal_ipv4_default_port_path
  - unresolvable_returns_dns_failed
  - resolves_known_hostname  (#[ignore], --ignored to run; hits net)

Empirical verification (2026-05-14):
  Workspace: cargo check + cargo test --workspace clean.
  Live resolver test: cn.teamspeak.app → 175.178.125.23:9987 (passes).
  cargo test -p chanora_core --test alpha_smoke -- --ignored:
    server='Vigorous Pro' channels=42 clients=20 (passes by hostname).
  flutter test: alpha_e2e_test + beta_e2e_test both green.
  Physical Moto G Stylus 5G (Android 14 arm64-v8a):
    APK rebuilt (48.9 MB). adb install + launch.
    Connect form left at default 'cn.teamspeak.app'.
    logcat shows:
      chanora_protocol: dns resolved input=cn.teamspeak.app
                                    resolved=175.178.125.23:9987
      tsclientlib: starting connection to 175.178.125.23:9987
      tsproto::resend: Connecting → Connected
      chanora_protocol: initial state snapshot received
    UI shows 'Connected to Vigorous Pro' / '42 channels • 20 online'.

This is the first item in Category A (post-Beta polish bundle).
Pause point: review before A.6 (full reconnect).
2026-05-15 00:17:13 +08:00

32 lines
1.2 KiB
TOML

[package]
name = "chanora_protocol"
description = "Chanora protocol adapter — isolates tsclientlib behind a typed boundary. No tsclientlib types may cross out of this crate per SAD-067 / SysDes-011 / SysDes-029."
version.workspace = true
edition.workspace = true
rust-version.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
publish.workspace = true
[dependencies]
thiserror.workspace = true
tracing.workspace = true
serde.workspace = true
once_cell = "1"
# tsclientlib is git-only and not on crates.io. The "audio" feature
# pulls in `audiopus` only — `sdl2` is a dev-dep used by upstream
# examples; the library itself does not link SDL.
tsclientlib = { git = "https://github.com/ReSpeak/tsclientlib.git", rev = "04aa2491", default-features = false, features = ["default-tls", "audio"] }
# tsproto_packets exposes OutAudio / InAudioBuf / AudioData /
# CodecType / Direction. Pinning to the same git rev as tsclientlib
# avoids any version-skew confusion.
tsproto-packets = { git = "https://github.com/ReSpeak/tsclientlib.git", rev = "04aa2491" }
# Async runtime utilities used by the connection task.
tokio = { version = "1", features = ["macros", "rt-multi-thread", "time", "sync"] }
futures = "0.3"
async-trait = "0.1"