niom-turn/docs/tcp_tls_data_plane.md

99 lines
4.6 KiB
Markdown

# TCP/TLS Data-Plane (TURN over TCP/TLS) in niom-turn
Dieses Dokument erklärt **wofür** eine TCP/TLS Data-Plane bei TURN gebraucht wird und **wie** `niom-turn` sie (aktuell) implementiert.
## Wofür wird das benötigt?
TURN hat zwei Arten von Traffic:
- **Control-Plane**: STUN/TURN Requests/Responses (Allocate, CreatePermission, ChannelBind, Refresh, …)
- **Data-Plane**: Nutzdaten zwischen Client und Peer, die über das Relay laufen (Send/Data-Indication oder ChannelData)
In vielen realen Netzen ist **UDP eingeschränkt oder komplett blockiert** (Corporate WLAN, Mobilfunk-APNs, Captive Portals, Proxy-Umgebungen). WebRTC/ICE versucht deshalb typischerweise:
1. UDP (schnell, bevorzugt)
2. TURN über TCP
3. TURN über TLS ("turns:") als letzte, aber oft funktionierende Option
Damit TURN über TCP/TLS wirklich nutzbar ist, muss nicht nur die Control-Plane über den Stream laufen, sondern auch der **Rückweg Peer → Client** (Data-Plane) über **dieselbe TCP/TLS Verbindung** beim Client ankommen.
## Was implementiert niom-turn konkret?
- Client ↔ Server Transport kann **UDP**, **TCP** oder **TLS** sein.
- Das Relay zum Peer ist weiterhin **UDP** (klassisches TURN UDP-Relay).
- Bei **TCP/TLS** liefert der Server die Data-Plane zurück an den Client über den **Stream** (statt über UDP an die Client-Adresse zu senden).
Das entspricht dem üblichen WebRTC-Fallback: "Client-Server über TCP/TLS, Peer-Transport über UDP".
## Architektur im Code
- Stream-Handler (TCP/TLS): [src/turn_stream.rs](../src/turn_stream.rs)
- TCP Listener: [src/tcp.rs](../src/tcp.rs)
- TLS Listener: [src/tls.rs](../src/tls.rs)
- Allocation + Relay: [src/alloc.rs](../src/alloc.rs)
### Schlüsselidee: `ClientSink`
Damit der Relay-Loop Peer-Pakete an unterschiedliche Client-Transporte schicken kann, gibt es in [src/alloc.rs](../src/alloc.rs) eine Abstraktion:
- `ClientSink::Udp { sock, addr }` → sendet Peer-Daten per `udp.send_to(..., addr)`
- `ClientSink::Stream { tx }` → queued Bytes in einen Writer-Task, der auf den TCP/TLS Stream schreibt
Wenn ein Client über TCP/TLS allocatet, wird die Allocation mit einem `ClientSink::Stream` erzeugt.
## Framing: STUN vs. ChannelData auf einem Byte-Stream
Auf UDP bekommt man Datagramme; auf TCP/TLS bekommt man einen **kontinuierlichen Byte-Stream**. TURN over TCP/TLS multiplexed:
- STUN/TURN Messages (Control-Plane)
- ChannelData Frames (Data-Plane, Client → Server)
`niom-turn` parst daher im Stream in einer Schleife "nächstes Frame" (siehe `try_pop_next_frame(...)` in [src/turn_stream.rs](../src/turn_stream.rs)):
### STUN Message
- Header ist 20 Bytes
- Length-Feld ist die Body-Länge
- Gesamtlänge ist: $20 + length$
### ChannelData Frame
- 4 Byte Header: `CHANNEL-NUMBER` (2) + `LENGTH` (2)
- Channel-Nummern liegen im Bereich `0x4000..=0x7FFF` (Top-Bits `01`)
- Gesamtlänge ist: $4 + length$
Wichtig: Bei TCP/TLS darf **kein Padding** als "separate Bytes" im Stream verbleiben. Deshalb baut `niom-turn` ChannelData als exakt `4 + len` Bytes (siehe [src/stun.rs](../src/stun.rs)).
### Hardening: Resync & Limits
Da TCP/TLS ein Byte-Stream ist, können kaputte oder bösartige Clients den Parser sonst leicht „desynchronisieren“.
`niom-turn` implementiert daher im Stream-Parser:
- **Magic-Cookie Check** für STUN: Ungültige Cookies führen zu einem Byte-weisen Resync (statt auf riesige Längen zu warten).
- **Frame-Size Limits** (STUN-Body und ChannelData), um Speicher-/DoS-Risiken zu begrenzen.
- **Max Buffer Limit** pro Verbindung: wenn der Eingangspuffer zu groß wird, wird die Verbindung geschlossen.
## Datenfluss (TCP/TLS)
1. Client verbindet sich per TCP oder TLS.
2. Der Stream-Handler liest Frames:
- STUN/TURN Requests → verarbeitet wie UDP-Pfad (Auth, Allocation, Permission, ChannelBind, Send, Refresh)
- ChannelData (Client→Peer) → wird über das UDP-Relay an den Peer geschickt
3. Peer sendet UDP an die Relay-Adresse.
4. Relay-Loop leitet die Bytes an den `ClientSink` weiter:
- bei Stream: `tx.send(bytes)` → Writer-Task schreibt Data-Indication oder ChannelData zurück auf denselben Stream
## Grenzen / Noch nicht implementiert
- Kein TCP-Relay zum Peer (TURN TCP allocations / CONNECT-Methoden wie in RFC6062).
- Fokus liegt auf: Client-Server Transport über TCP/TLS + UDP-Relay.
- IPv6 ist im aktuellen MVP noch nicht vollständig umgesetzt.
## Tests
- TCP Stream Data-Plane: `tests/tcp_turn.rs`
- TLS Stream Data-Plane: `tests/tls_data_plane.rs`
- Gemeinsames Framing (STUN + ChannelData): `tests/support/stream.rs`
Wenn du willst, kann ich als nächsten Schritt die Doku um eine kurze Interop-Checkliste (WebRTC/ICE Verhalten, Candidate-Types, typische Fehlerbilder) ergänzen.