Skip to main content

turn_client_proto/
udp.rs

1// Copyright (C) 2025 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8//
9// SPDX-License-Identifier: MIT OR Apache-2.0
10
11//! UDP TURN client.
12//!
13//! An implementation of a TURN client suitable for UDP connections.
14
15use alloc::vec::Vec;
16use core::net::{IpAddr, SocketAddr};
17
18use stun_proto::agent::{StunAgent, Transmit};
19use stun_proto::types::data::Data;
20use stun_proto::Instant;
21
22use stun_proto::types::TransportType;
23
24use turn_types::channel::ChannelData;
25use turn_types::stun::message::Message;
26
27use tracing::{trace, warn};
28
29use crate::api::{
30    DataRangeOrOwned, DelayedMessageOrChannelSend, Socket5Tuple, TcpAllocateError, TcpConnectError,
31    TransmitBuild, TurnClientApi, TurnConfig, TurnPeerData,
32};
33use crate::protocol::{TurnClientProtocol, TurnProtocolChannelRecv};
34
35pub use crate::api::{
36    BindChannelError, CreatePermissionError, DeleteError, SendError, TurnEvent, TurnPollRet,
37    TurnRecvRet,
38};
39
40/// A TURN client.
41#[derive(Debug)]
42pub struct TurnClientUdp {
43    protocol: TurnClientProtocol,
44}
45
46impl TurnClientUdp {
47    /// Allocate an address on a TURN server to relay data to and from peers.
48    ///
49    /// # Examples
50    /// ```
51    /// # use turn_types::TurnCredentials;
52    /// # use turn_client_proto::prelude::*;
53    /// # use turn_client_proto::udp::TurnClientUdp;
54    /// # use turn_client_proto::api::TurnConfig;
55    /// # use turn_types::TransportType;
56    /// let credentials = TurnCredentials::new("tuser", "tpass");
57    /// let config = TurnConfig::new(credentials.clone());
58    /// let local_addr = "192.168.0.1:4000".parse().unwrap();
59    /// let remote_addr = "10.0.0.1:3478".parse().unwrap();
60    /// let client = TurnClientUdp::allocate(
61    ///     local_addr,
62    ///     remote_addr,
63    ///     config,
64    /// );
65    /// assert_eq!(client.transport(), TransportType::Udp);
66    /// assert_eq!(client.local_addr(), local_addr);
67    /// assert_eq!(client.remote_addr(), remote_addr);
68    /// ```
69    #[tracing::instrument(
70        name = "turn_client_allocate"
71        skip(config),
72        fields(allocation_transport = %config.allocation_transport(),)
73    )]
74    pub fn allocate(local_addr: SocketAddr, remote_addr: SocketAddr, config: TurnConfig) -> Self {
75        let stun_agent = StunAgent::builder(TransportType::Udp, local_addr)
76            .remote_addr(remote_addr)
77            .build();
78        if config.allocation_transport() != TransportType::Udp {
79            panic!("Attempt made to create a UDP TURN client without a UDP allocation");
80        }
81
82        Self {
83            protocol: TurnClientProtocol::new(stun_agent, config),
84        }
85    }
86}
87
88impl TurnClientApi for TurnClientUdp {
89    fn transport(&self) -> TransportType {
90        self.protocol.transport()
91    }
92
93    fn local_addr(&self) -> SocketAddr {
94        self.protocol.local_addr()
95    }
96
97    fn remote_addr(&self) -> SocketAddr {
98        self.protocol.remote_addr()
99    }
100
101    fn poll(&mut self, now: Instant) -> TurnPollRet {
102        self.protocol.poll(now)
103    }
104
105    fn relayed_addresses(&self) -> impl Iterator<Item = (TransportType, SocketAddr)> + '_ {
106        self.protocol.relayed_addresses()
107    }
108
109    fn permissions(
110        &self,
111        transport: TransportType,
112        relayed: SocketAddr,
113    ) -> impl Iterator<Item = IpAddr> + '_ {
114        self.protocol.permissions(transport, relayed)
115    }
116
117    fn poll_transmit(&mut self, now: Instant) -> Option<Transmit<Data<'static>>> {
118        self.protocol.poll_transmit(now)
119    }
120
121    fn poll_event(&mut self) -> Option<TurnEvent> {
122        self.protocol.poll_event()
123    }
124
125    fn delete(&mut self, now: Instant) -> Result<(), DeleteError> {
126        self.protocol.delete(now)
127    }
128
129    fn create_permission(
130        &mut self,
131        transport: TransportType,
132        peer_addr: IpAddr,
133        now: Instant,
134    ) -> Result<(), CreatePermissionError> {
135        self.protocol.create_permission(transport, peer_addr, now)
136    }
137
138    fn have_permission(&self, transport: TransportType, to: IpAddr) -> bool {
139        self.protocol.have_permission(transport, to)
140    }
141
142    fn bind_channel(
143        &mut self,
144        transport: TransportType,
145        peer_addr: SocketAddr,
146        now: Instant,
147    ) -> Result<(), BindChannelError> {
148        self.protocol.bind_channel(transport, peer_addr, now)
149    }
150
151    fn tcp_connect(&mut self, peer_addr: SocketAddr, now: Instant) -> Result<(), TcpConnectError> {
152        self.protocol.tcp_connect(peer_addr, now)
153    }
154
155    fn allocated_tcp_socket(
156        &mut self,
157        id: u32,
158        five_tuple: Socket5Tuple,
159        peer_addr: SocketAddr,
160        local_addr: Option<SocketAddr>,
161        now: Instant,
162    ) -> Result<(), TcpAllocateError> {
163        self.protocol
164            .allocated_tcp_socket(id, five_tuple, peer_addr, local_addr, now)
165    }
166
167    fn tcp_closed(&mut self, local_addr: SocketAddr, remote_addr: SocketAddr, now: Instant) {
168        self.protocol.tcp_closed(local_addr, remote_addr, now)
169    }
170
171    fn send_to<T: AsRef<[u8]> + core::fmt::Debug>(
172        &mut self,
173        transport: TransportType,
174        to: SocketAddr,
175        data: T,
176        now: Instant,
177    ) -> Result<Option<TransmitBuild<DelayedMessageOrChannelSend<T>>>, SendError> {
178        self.protocol.send_to(transport, to, data, now).map(Some)
179    }
180
181    fn recv<T: AsRef<[u8]> + core::fmt::Debug>(
182        &mut self,
183        transmit: Transmit<T>,
184        now: Instant,
185    ) -> TurnRecvRet<T> {
186        /* is this data for our client? */
187        if transmit.to != self.local_addr()
188            || self.transport() != transmit.transport
189            || transmit.from != self.remote_addr()
190        {
191            trace!(
192                "received data not directed at us ({:?}) but for {:?}!",
193                self.local_addr(),
194                transmit.to
195            );
196            return TurnRecvRet::Ignored(transmit);
197        }
198
199        let data = transmit.data.as_ref();
200        let Ok(msg) = Message::from_bytes(data) else {
201            let Ok(channel) = ChannelData::parse(data) else {
202                return TurnRecvRet::Ignored(transmit);
203            };
204            let ret = self.protocol.handle_channel(channel, now);
205            match ret {
206                TurnProtocolChannelRecv::Ignored => return TurnRecvRet::Ignored(transmit),
207                TurnProtocolChannelRecv::PeerData {
208                    range,
209                    transport,
210                    peer,
211                } => {
212                    return TurnRecvRet::PeerData(TurnPeerData {
213                        data: DataRangeOrOwned::Range {
214                            data: transmit.data,
215                            range,
216                        },
217                        transport,
218                        peer,
219                    })
220                }
221            }
222        };
223
224        let msg_transmit = Transmit::new(msg, transmit.transport, transmit.from, transmit.to);
225        TurnRecvRet::from_protocol_recv(self.protocol.handle_message(msg_transmit, now), transmit)
226    }
227
228    fn poll_recv(&mut self, _now: Instant) -> Option<TurnPeerData<Vec<u8>>> {
229        None
230    }
231
232    fn protocol_error(&mut self) {
233        self.protocol.protocol_error()
234    }
235}