use std::net::SocketAddr;
use std::sync::Arc;
use tokio::net::UdpSocket;
use crate::bridge::{QuicBridgeReceiver, QuicBridgeSender};
use crate::error::{DvpnError, Result};
const MAX_WG_PACKET: usize = 65535;
#[derive(Clone)]
pub struct SocketProtector(Arc<dyn Fn(i32) + Send + Sync>);
impl SocketProtector {
pub fn new(f: impl Fn(i32) + Send + Sync + 'static) -> Self {
Self(Arc::new(f))
}
#[cfg_attr(not(unix), allow(dead_code))]
fn protect(&self, fd: i32) {
(self.0)(fd)
}
}
impl std::fmt::Debug for SocketProtector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("SocketProtector(..)")
}
}
#[allow(clippy::large_enum_variant)]
pub(crate) enum WgSender {
Direct(Arc<UdpSocket>),
Quic(QuicBridgeSender),
}
#[allow(clippy::large_enum_variant)]
pub(crate) enum WgReceiver {
Direct {
sock: Arc<UdpSocket>,
buf: Box<[u8]>,
},
Quic(QuicBridgeReceiver),
}
impl WgSender {
pub(crate) async fn send(&mut self, packet: &[u8]) -> Result<()> {
match self {
WgSender::Direct(sock) => {
sock.send(packet).await?;
Ok(())
}
WgSender::Quic(sender) => sender.send(packet).await,
}
}
}
impl WgReceiver {
pub(crate) fn is_bridge(&self) -> bool {
matches!(self, WgReceiver::Quic(_))
}
pub(crate) async fn recv(&mut self) -> Result<Vec<u8>> {
match self {
WgReceiver::Direct { sock, buf } => {
let n = sock.recv(buf).await?;
Ok(buf[..n].to_vec())
}
WgReceiver::Quic(receiver) => receiver.recv().await,
}
}
}
pub(crate) async fn direct_transport(
entry_endpoint: SocketAddr,
protector: Option<&SocketProtector>,
) -> Result<(WgSender, WgReceiver)> {
let bind: SocketAddr = if entry_endpoint.is_ipv4() {
"0.0.0.0:0".parse().expect("valid bind addr")
} else {
"[::]:0".parse().expect("valid bind addr")
};
let sock = UdpSocket::bind(bind).await?;
#[cfg(unix)]
if let Some(p) = protector {
use std::os::fd::AsRawFd;
p.protect(sock.as_raw_fd());
}
#[cfg(not(unix))]
let _ = protector;
sock.connect(entry_endpoint)
.await
.map_err(|e| DvpnError::Transport(format!("connect to entry gateway failed: {e}")))?;
let sock = Arc::new(sock);
Ok((
WgSender::Direct(sock.clone()),
WgReceiver::Direct {
sock,
buf: vec![0u8; MAX_WG_PACKET].into_boxed_slice(),
},
))
}