use super::shared_tcp::peer_ufrag_from_binding_request;
use anyhow::{Context, Result, bail};
use parking_lot::Mutex;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
use std::sync::{Arc, OnceLock};
use std::time::Duration;
use tokio::net::UdpSocket;
use tokio::sync::mpsc;
use tracing::{debug, trace};
pub(crate) type SharedUdpPacket = (Vec<u8>, SocketAddr);
const SHARED_UDP_CHANNEL_CAPACITY: usize = 2048;
type PeerMap = Arc<Mutex<HashMap<SocketAddr, String>>>;
static SHARED_PORTS: OnceLock<Mutex<HashMap<SocketAddr, Arc<SharedUdpPort>>>> = OnceLock::new();
fn registry() -> &'static Mutex<HashMap<SocketAddr, Arc<SharedUdpPort>>> {
SHARED_PORTS.get_or_init(|| Mutex::new(HashMap::new()))
}
struct Session {
tx: mpsc::Sender<SharedUdpPacket>,
}
struct SharedUdpPort {
socket: Arc<UdpSocket>,
sessions: Mutex<HashMap<String, Session>>,
peers: PeerMap,
ref_count: AtomicUsize,
shutting_down: AtomicBool,
dropped_full: AtomicU64,
}
impl SharedUdpPort {
fn new(socket: Arc<UdpSocket>) -> Self {
Self {
socket,
sessions: Mutex::new(HashMap::new()),
peers: Arc::new(Mutex::new(HashMap::new())),
ref_count: AtomicUsize::new(0),
shutting_down: AtomicBool::new(false),
dropped_full: AtomicU64::new(0),
}
}
fn spawn_recv_loop(self: &Arc<Self>) {
let port = Arc::clone(self);
tokio::spawn(async move {
let mut buf = [0u8; 1500];
loop {
if port.shutting_down.load(Ordering::Relaxed) {
break;
}
let recv = port.socket.recv_from(&mut buf);
tokio::select! {
biased;
_ = port.shutdown_signal() => break,
res = recv => {
match res {
Ok((len, peer_addr)) => {
if len == 0 {
continue;
}
port.dispatch(&buf[..len], peer_addr);
}
Err(e) => {
if port.shutting_down.load(Ordering::Relaxed) {
break;
}
debug!("shared UDP recv error: {}", e);
tokio::time::sleep(Duration::from_millis(50)).await;
}
}
}
}
}
debug!("shared UDP recv loop exited");
});
}
async fn shutdown_signal(&self) {
loop {
if self.shutting_down.load(Ordering::Relaxed) {
return;
}
tokio::time::sleep(Duration::from_millis(250)).await;
}
}
fn dispatch(&self, packet: &[u8], peer_addr: SocketAddr) {
let target_ufrag = if packet[0] < 2 {
peer_ufrag_from_binding_request(packet)
} else {
None
};
let ufrag = if let Some(u) = target_ufrag {
self.peers.lock().insert(peer_addr, u.clone());
Some(u)
} else {
self.peers.lock().get(&peer_addr).cloned()
};
let Some(ufrag) = ufrag else {
trace!(
"shared UDP: no session for peer {} (len={}, first_byte={})",
peer_addr,
packet.len(),
packet[0]
);
return;
};
let tx = {
let sessions = self.sessions.lock();
sessions.get(&ufrag).map(|s| s.tx.clone())
};
if let Some(tx) = tx {
match tx.try_send((packet.to_vec(), peer_addr)) {
Ok(()) => {}
Err(mpsc::error::TrySendError::Full(_)) => {
let prev = self.dropped_full.fetch_add(1, Ordering::Relaxed);
if prev.is_multiple_of(1024) {
debug!(
"shared UDP: session {} channel full — dropped packet from {} \
(total dropped so far: {})",
ufrag,
peer_addr,
prev + 1
);
}
}
Err(mpsc::error::TrySendError::Closed(_)) => {
trace!(
"shared UDP: session {} channel closed while forwarding packet from {}",
ufrag, peer_addr
);
}
}
}
}
}
pub struct SharedUdpHandle {
socket: Arc<UdpSocket>,
rx: Arc<tokio::sync::Mutex<mpsc::Receiver<SharedUdpPacket>>>,
peers: PeerMap,
ufrag: String,
}
impl std::fmt::Debug for SharedUdpHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SharedUdpHandle")
.field("ufrag", &self.ufrag)
.field(
"local_addr",
&self
.socket
.local_addr()
.map(|a| a.to_string())
.unwrap_or_else(|_| "?".into()),
)
.finish_non_exhaustive()
}
}
impl SharedUdpHandle {
pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
self.socket.local_addr()
}
pub fn socket(&self) -> &Arc<UdpSocket> {
&self.socket
}
pub async fn send_to(&self, data: &[u8], dest: SocketAddr) -> std::io::Result<usize> {
self.peers.lock().insert(dest, self.ufrag.clone());
self.socket.send_to(data, dest).await
}
pub async fn recv(&self) -> Option<SharedUdpPacket> {
self.rx.lock().await.recv().await
}
}
pub(crate) struct SharedUdpRegistration {
port: Arc<SharedUdpPort>,
listen_key: SocketAddr,
ufrag: String,
}
impl std::fmt::Debug for SharedUdpRegistration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SharedUdpRegistration")
.field("listen_key", &self.listen_key)
.field("ufrag", &self.ufrag)
.finish_non_exhaustive()
}
}
impl Drop for SharedUdpRegistration {
fn drop(&mut self) {
self.port.sessions.lock().remove(&self.ufrag);
self.port
.peers
.lock()
.retain(|_, ufrag| ufrag != &self.ufrag);
let prev = self.port.ref_count.fetch_sub(1, Ordering::SeqCst);
if prev == 1 {
self.port.shutting_down.store(true, Ordering::SeqCst);
registry().lock().remove(&self.listen_key);
}
}
}
pub(crate) async fn acquire(
bind_addr: SocketAddr,
local_ufrag: String,
) -> Result<(SocketAddr, SharedUdpHandle, SharedUdpRegistration)> {
let maybe_existing = registry().lock().get(&bind_addr).cloned();
let port = if let Some(existing) = maybe_existing {
existing
} else {
let socket = UdpSocket::bind(bind_addr)
.await
.with_context(|| format!("bind shared UDP socket {bind_addr}"))?;
let socket = Arc::new(socket);
let port = Arc::new(SharedUdpPort::new(socket));
let mut reg = registry().lock();
if let Some(existing) = reg.get(&bind_addr) {
existing.clone()
} else {
reg.insert(bind_addr, port.clone());
port.spawn_recv_loop();
port
}
};
let local_addr = port
.socket
.local_addr()
.context("shared UDP socket local_addr")?;
if port.sessions.lock().contains_key(&local_ufrag) {
bail!("ufrag {local_ufrag} already registered on shared UDP socket {bind_addr}");
}
let (tx, rx) = mpsc::channel(SHARED_UDP_CHANNEL_CAPACITY);
port.ref_count.fetch_add(1, Ordering::SeqCst);
port.sessions
.lock()
.insert(local_ufrag.clone(), Session { tx });
let handle = SharedUdpHandle {
socket: port.socket.clone(),
rx: Arc::new(tokio::sync::Mutex::new(rx)),
peers: port.peers.clone(),
ufrag: local_ufrag.clone(),
};
if local_addr.ip().is_unspecified() {
trace!("shared UDP socket bound on wildcard: {}", local_addr);
}
Ok((
local_addr,
handle,
SharedUdpRegistration {
port,
listen_key: bind_addr,
ufrag: local_ufrag,
},
))
}
#[cfg(test)]
pub(crate) fn session_count(bind_addr: SocketAddr) -> usize {
registry()
.lock()
.get(&bind_addr)
.map(|p| p.sessions.lock().len())
.unwrap_or(0)
}
#[cfg(test)]
pub(crate) fn ufrag_for_peer(bind_addr: SocketAddr, peer_addr: SocketAddr) -> Option<String> {
registry()
.lock()
.get(&bind_addr)?
.peers
.lock()
.get(&peer_addr)
.cloned()
}