use std::ffi;
use crate::{TOKIO_RUNTIME, ffi_guard};
pub struct udp_socket(tailscale::netstack::UdpSocket);
#[unsafe(no_mangle)]
pub extern "C" fn ts_udp_bind(
dev: &crate::device,
addr: &crate::sockaddr,
) -> Option<Box<udp_socket>> {
ffi_guard(move || {
let addr = addr.try_into().ok()?;
match TOKIO_RUNTIME.block_on(dev.0.udp_bind(addr)) {
Ok(sock) => Some(Box::new(udp_socket(sock))),
Err(e) => {
tracing::error!(error = %e, "binding udp socket");
None
}
}
})
}
#[unsafe(no_mangle)]
pub extern "C" fn ts_udp_close(sock: Box<udp_socket>) {
ffi_guard(move || {
drop(sock);
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ts_udp_sendto(
sock: &udp_socket,
addr: &crate::sockaddr,
msg: *const u8,
len: usize,
) -> ffi::c_int {
ffi_guard(move || {
let Some(msg) = (unsafe { crate::util::slice(msg, len) }) else {
tracing::error!("null msg buffer with non-zero len");
return -1;
};
let Ok(addr) = addr.try_into() else {
return -1;
};
if let Err(e) = sock.0.send_to_blocking(addr, msg) {
tracing::error!(err = %e, "udp_send failed");
return -1;
}
0
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ts_udp_recvfrom(
sock: &udp_socket,
addr: Option<&mut crate::sockaddr>,
buf: *mut u8,
len: usize,
) -> ffi::c_int {
ffi_guard(move || {
let Some(buf) = (unsafe { crate::util::slice_mut(buf, len) }) else {
tracing::error!("null recv buffer with non-zero len");
return -1;
};
match sock.0.recv_from_blocking(buf) {
Err(e) => {
tracing::error!(err = %e, "udp_recv failed");
-1
}
Ok((who, len)) => {
tracing::trace!(%who);
if let Some(addr) = addr {
*addr = who.into();
}
len as _
}
}
})
}
#[unsafe(no_mangle)]
pub extern "C" fn ts_udp_local_addr(sock: &udp_socket) -> crate::sockaddr {
ffi_guard(move || sock.0.local_addr().into())
}