#![warn(missing_docs)]
use std::net::SocketAddr;
use std::sync::Arc;
use bytes::Bytes;
use rings_core::dht::Did;
use tokio::net::UdpSocket;
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;
use super::inject_accepted;
use super::send_frame;
use super::Outbound;
use super::Pending;
use super::RelayTask;
use super::TransportSessions;
use super::UDP_BUF;
use crate::extension::ext::Scope;
use crate::extension::transport::Frame;
impl TransportSessions {
pub(super) async fn listen_udp(
self: Arc<Self>,
scope: Scope,
local_addr: SocketAddr,
peer: Did,
service: String,
) {
let socket = match UdpSocket::bind(local_addr).await {
Ok(socket) => Arc::new(socket),
Err(e) => {
tracing::error!("transport udp bind {local_addr} failed: {e:?}");
return;
}
};
tokio::spawn(async move {
let mut buf = vec![0u8; UDP_BUF];
loop {
match socket.recv_from(buf.as_mut_slice()).await {
Ok((n, src)) => {
let bytes = Bytes::copy_from_slice(buf.get(..n).unwrap_or_default());
match self.udp_flow(&src) {
Some(key) => {
let _ = send_frame(&scope, key.peer, Frame::Data {
session: key.session,
from_opener: super::opened_by_us(&key),
bytes,
})
.await;
}
None => {
if let Some(token) = self.stash_pending(Pending::Udp {
socket: socket.clone(),
src,
first: bytes,
}) {
inject_accepted(&scope, token, peer, service.clone()).await;
self.evict_pending(token);
}
}
}
}
Err(e) => {
tracing::error!("transport udp recv on {local_addr} failed: {e:?}");
break;
}
}
}
});
}
}
pub(super) async fn relay_udp_connected(task: RelayTask, socket: UdpSocket) {
let RelayTask {
sessions,
scope,
key,
mut outbound_rx,
cancel,
generation,
} = task;
let peer = key.peer;
let session = key.session;
let from_opener = super::opened_by_us(&key);
let mut buf = vec![0u8; UDP_BUF];
loop {
tokio::select! {
biased;
_ = cancel.cancelled() => break,
received = socket.recv(buf.as_mut_slice()) => match received {
Ok(n) => {
let bytes = Bytes::copy_from_slice(buf.get(..n).unwrap_or_default());
if send_frame(&scope, peer, Frame::Data {
session,
from_opener,
bytes,
})
.await
.is_err()
{
break;
}
}
Err(_) => break,
},
outbound = outbound_rx.recv() => match outbound {
Some(Outbound::Data(bytes)) => {
let _ = socket.send(bytes.as_ref()).await;
}
Some(Outbound::Shutdown) | None => break,
},
}
}
if sessions.close_if_current(&scope, &key, generation).await {
let _ = send_frame(&scope, peer, Frame::Close {
session,
from_opener,
})
.await;
}
}
pub(super) fn spawn_udp_sendto(
socket: Arc<UdpSocket>,
dest: SocketAddr,
mut outbound_rx: mpsc::Receiver<Outbound>,
cancel: CancellationToken,
) {
tokio::spawn(async move {
loop {
tokio::select! {
biased;
_ = cancel.cancelled() => break,
outbound = outbound_rx.recv() => match outbound {
Some(Outbound::Data(bytes)) => {
let _ = socket.send_to(bytes.as_ref(), dest).await;
}
Some(Outbound::Shutdown) | None => break,
},
}
}
});
}
pub(super) async fn bind_connected_udp(addr: SocketAddr) -> Option<UdpSocket> {
let bind: SocketAddr = if addr.is_ipv4() {
"0.0.0.0:0".parse().ok()?
} else {
"[::]:0".parse().ok()?
};
let socket = UdpSocket::bind(bind).await.ok()?;
socket.connect(addr).await.ok()?;
Some(socket)
}