# End-to-End TURN Flow (UDP + TLS) Dieses Dokument beschreibt den **konkret implementierten** End-to-End Ablauf in `niom-turn` anhand des aktuellen Codes (MVP): - UDP-Control-Plane und UDP-Data-Plane: [src/server.rs](../src/server.rs), [src/alloc.rs](../src/alloc.rs), [src/stun.rs](../src/stun.rs), [src/auth.rs](../src/auth.rs) - TLS-Control-Plane ("turns") mit STUN-Framing: [src/tls.rs](../src/tls.rs) - Test-Builder (wie Requests gebaut werden): [tests/support/stun_builders.rs](../tests/support/stun_builders.rs) ## Begriffe - **Client**: TURN-Client (z.B. WebRTC ICE Agent) - **Server**: `niom-turn` (dieses Projekt) - **Peer**: Gegenstelle, zu der relayed werden soll (typischerweise anderer WebRTC-Endpunkt) - **Allocation**: Server-seitige Sitzung, die ein **Relay-UDP-Socket** bereitstellt - **Permission**: Erlaubnis, zu einem Peer zu senden/Peer-Pakete zu akzeptieren - **Channel Binding**: Zuordnung `channel-number -> peer`, um ChannelData nutzen zu können --- ## UDP: Sequenzgrafik (Happy Path) ```mermaid sequenceDiagram autonumber participant C as Client (TURN) participant S as niom-turn (UDP:3478) participant R as Relay Socket (UDP:ephemeral) participant P as Peer Note over C,S: 1) Allocate ohne Auth → 401 Challenge C->>S: STUN Allocate Request (ohne MI) S->>C: STUN Error Response 401 + REALM + NONCE Note over C,S: 2) Allocate mit Long-Term Auth C->>S: STUN Allocate Request + USERNAME/REALM/NONCE + MESSAGE-INTEGRITY S->>R: bind("0.0.0.0:0"), spawn Relay-Loop S->>C: Allocate Success + XOR-RELAYED-ADDRESS + LIFETIME (+ MESSAGE-INTEGRITY + FINGERPRINT) Note over C,S: 3) CreatePermission (Pflicht vor Send/ChannelBind) C->>S: CreatePermission + XOR-PEER-ADDRESS (+ Auth + MI) S->>C: Success (200) (+ MESSAGE-INTEGRITY + FINGERPRINT) Note over C,S: 4) Send (Client→Peer via Relay) C->>S: Send + XOR-PEER-ADDRESS + DATA (+ Auth + MI) S->>R: relay.send_to(DATA, Peer) R->>P: UDP payload (source = relay_addr) Note over P,C: 5) Rückweg (Peer→Client) P->>R: UDP payload (dest = relay_addr) alt Channel Binding existiert R->>S: recv_from(Peer) S->>C: ChannelData(channel, payload) else Kein Channel Binding R->>S: recv_from(Peer) S->>C: Data Indication (METHOD_DATA|INDICATION) + XOR-PEER-ADDRESS + DATA end Note over C,S: 6) Optional: ChannelBind + ChannelData C->>S: ChannelBind + CHANNEL-NUMBER + XOR-PEER-ADDRESS (+ Auth + MI) S->>C: Success (200) (+ MESSAGE-INTEGRITY + FINGERPRINT) C->>S: ChannelData(channel, payload) S->>R: relay.send_to(payload, Peer) R->>P: UDP payload Note over C,S: 7) Refresh C->>S: Refresh + LIFETIME (+ Auth + MI) S->>C: Success + LIFETIME(applied) (+ MESSAGE-INTEGRITY + FINGERPRINT) ``` ### Was der Server dabei **genau** macht - Eingangspunkt: `udp_reader_loop` in [src/server.rs](../src/server.rs) - Frühe Abzweigung: Wenn `parse_channel_data(...)` erfolgreich ist, wird **kein STUN** geparst, sondern ChannelData direkt weitergeleitet (nur wenn Allocation+Binding+Permission passt). - STUN/TURN Requests werden mit `parse_message(...)` in [src/stun.rs](../src/stun.rs) geparst. ### RFC-Interop Hinweis: FINGERPRINT - Alle vom Server gebauten STUN-Nachrichten enthalten `FINGERPRINT` als letztes Attribut. - Wenn ein Client `FINGERPRINT` mitsendet, wird es validiert; bei ungültigem `FINGERPRINT` wird die Nachricht verworfen (kein Response). ### Auth-Entscheidungen und typische Error-Codes Die Auth-Policy ist zentral in `AuthManager::authenticate` in [src/auth.rs](../src/auth.rs). - **401 Unauthorized**: Wenn `MESSAGE-INTEGRITY` fehlt → Challenge mit `REALM` + `NONCE`. - **438 Stale Nonce**: Wenn `NONCE` abgelaufen/ungültig ist → neue Challenge. - **437 Allocation Mismatch**: Wenn CreatePermission/Send/ChannelBind/Refresh ohne Allocation kommt. - **403 Peer Not Permitted**: Wenn ein Peer nicht (mehr) permitted ist. - **400 Missing/Invalid ...**: Wenn Attribute fehlen oder XOR-PEER-ADDRESS nicht dekodierbar ist. --- ## TLS (turns): Sequenzgrafik (Control-Plane) Wichtig: Die TLS-Implementierung in [src/tls.rs](../src/tls.rs) nutzt denselben TURN-Handler wie TCP und implementiert eine echte **Stream-Data-Plane**. ```mermaid sequenceDiagram autonumber participant C as Client (turns) participant T as niom-turn (TLS:5349) participant R as Relay Socket (UDP:ephemeral) participant P as Peer Note over C,T: STUN framing über TCP/TLS: read → chunk by (len+20) C->>T: STUN Allocate (ohne MI) T->>C: 401 + REALM + NONCE (über TLS) C->>T: STUN Allocate + Auth + MI T->>R: allocate_for(peer, stream-sink) T->>C: Allocate Success + XOR-RELAYED-ADDRESS + LIFETIME (über TLS) C->>T: CreatePermission/Send/Refresh/ChannelBind (über TLS) T->>C: Success/Error (über TLS) Note over P,C: Peer-Daten kommen über den TLS-Stream zurück P->>R: UDP payload an relay_addr R->>T: recv_from(Peer) T->>C: Data Indication / ChannelData (über TLS) ``` ### Konsequenz - Control-Plane über TLS funktioniert (Allocate/Refresh/… werden über TLS beantwortet). - Der **Data-Plane Rückweg** (Peer → Client) läuft ebenfalls über den TLS-Stream (Relay → `ClientSink::Stream`). Mehr Details dazu: [docs/tcp_tls_data_plane.md](tcp_tls_data_plane.md) --- ## Mini-Checkliste: Minimaler Ablauf (praktisch) 1. `ALLOCATE` ohne MI → `401` + `REALM` + `NONCE` 2. `ALLOCATE` mit `USERNAME/REALM/NONCE` + `MESSAGE-INTEGRITY` → `XOR-RELAYED-ADDRESS` + `LIFETIME` 3. `CREATE_PERMISSION` für Peer → `200` 4. `SEND` mit `DATA` → Server sendet via Relay an Peer 5. Peer sendet zurück an Relay → Server liefert an Client als `DATA-INDICATION` oder `CHANNEL-DATA` 6. Optional `CHANNEL_BIND` + ChannelData für effizientere Data-Plane 7. `REFRESH` zum Verlängern oder `LIFETIME=0` zum Freigeben