# chanora_resolver

The library accepts the same kind of raw server input a client UI receives and returns a final `host:port` address suitable for `tsclientlib::Connection::build` or Chanora's `ConnectConfig.address`.

## Resolution Flow

Use `ChanoraResolver::resolve_client_request` or `resolve_client_address` for app code. The resolver owns the decision tree:

1. Normalize raw client input, including `ts3server://host?port=...`.
2. For dotless names such as `6666` or `wwb`, query the myTeamSpeak server-name endpoint first. If it returns a hostname, restart normal resolution with that hostname. If it returns `ip:port`, use that final address.
3. Start plain A/AAAA DNS as the final fallback address.
4. If the user supplied an explicit port and DNS resolved, return that direct target immediately.
5. Prefer `_ts3._udp.<host>` SRV. Its target host and port override the default port, then the target is resolved to a final IP.
6. Try `_tsdns._tcp.<candidate>` SRV on candidate parent/full hosts and query the returned TSDNS server over TCP.
7. Try direct TSDNS TCP on candidate parent/full hosts at port `41144`.
8. Fall back to the A/AAAA result with the default TeamSpeak port `9987`. When DNS fallback is already available, TSDNS discovery is capped so missing or filtered TSDNS cannot add multi-second join delays.

The older explicit API (`Args { host, service, protocol }`) is still available for diagnostic example use.

## DNS Setup Guide

This chart is for choosing DNS records when operating a TeamSpeak 3 server. It is not the client lookup order; the library still implements the client-side resolver behavior above.

```mermaid
flowchart TD
    A["Which DNS type fits your TeamSpeak 3 server?"]

    A --> X["Don't do this!"]
    X --> X1["Do not use simple TSDNS<br/>without SRV TSDNS"]

    A --> B{"How many servers do you have?"}

    B -->|One virtual server| C{"Does your server use<br/>the default port 9987?"}

    C -->|Yes| D{"Do all services under this domain<br/>run on the same server<br/>as the TS3 server?"}
    D -->|Yes| E1["Use A/AAAA or CNAME"]
    D -->|No| E2["Use SRV TS3"]

    C -->|No| F{"Are you okay with<br/>entering the port manually?"}
    F -->|No| E3["Use SRV TS3"]
    F -->|Yes| G{"Do all services under this domain<br/>run on the same server<br/>as the TS3 server?"}
    G -->|Yes| E4["Use A/AAAA or CNAME"]
    G -->|No| E5["Use SRV TSDNS"]

    B -->|More than one virtual server| H{"Are you okay with port numbers?"}

    H -->|No| I1["Use subdomains with SRV TS3<br/>for each subdomain"]

    H -->|Yes| I{"On how many servers<br/>do you run TS3 servers?"}
    I -->|Only one| J{"Do all services under this domain<br/>run on the same server<br/>as the TS3 server?"}
    J -->|Yes| K1["Use A/AAAA or CNAME"]
    J -->|No| K2["Use SRV TSDNS"]

    I -->|More than one| K3["Use subdomains with A/AAAA<br/>or CNAME for each server"]
```

## Library Example

```rust
use chanora_resolver::ChanoraResolver;

let client = ChanoraResolver::new()?;
let address = client.resolve_client_address("voice.teamspeak.com").await?;

// tsclientlib:
// let mut connection = tsclientlib::Connection::build(address).connect()?;

// Chanora:
// let cfg = chanora_core::ConnectConfig {
//     address,
//     nickname,
//     password,
//     identity,
//     ready_timeout,
// };
// let snapshot = session.connect(cfg).await?;
```

## Run

```bash
cargo run --example library -- voice.teamspeak.com
cargo run --example library -- 6666
cargo run --example library -- wwb
cargo run --example library -- kr.teamspeak.app
cargo run --example library -- 'ts3server://voice.teamspeak.com?port=9987'
cargo run --example cli -- -host voice.teamspeak.com -service ts3
```

`example_log` contains live sample outputs for TS3 SRV, DNS fallback, server-name lookup, and unsuccessful TSDNS attempts that must remain non-fatal.

## Fail-Safes

- Bare numeric names such as `6666` are treated as server names first, avoiding OS DNS coercion into numeric IPv4 addresses.
- SRV targets and TSDNS responses are resolved to final IP addresses before handoff, so Chanora can pass a concrete `SocketAddr`-style `host:port` to `tsclientlib`.
- TSDNS TCP lookups use short timeouts and are non-fatal; DNS fallback remains available when TSDNS is absent or unreachable.
- Android uses an explicit SRV resolver configuration instead of relying on system resolver initialization in a raw binary context.

## Test

```bash
cargo fmt --all -- --check
cargo test
cargo clippy --all-targets --all-features -- -D warnings
```
