use crate::{
RuntimeError,
futures::{
net::{
address::{NetAddress, peer_of},
socket::{get_option, set_option, set_raw},
},
udp::udp_task::{RecvFromTask, SendToTask, UdpConnectTask, UdpRecvTask, UdpSendTask},
},
modules::fd::Fd,
};
use std::{
fmt,
net::{Ipv4Addr, Ipv6Addr, SocketAddr},
sync::Arc,
};
fn v4_request(group: Ipv4Addr, interface: Ipv4Addr) -> libc::ip_mreq {
libc::ip_mreq {
imr_multiaddr: libc::in_addr {
s_addr: u32::from(group).to_be(),
},
imr_interface: libc::in_addr {
s_addr: u32::from(interface).to_be(),
},
}
}
fn v6_request(group: Ipv6Addr, interface: u32) -> libc::ipv6_mreq {
libc::ipv6_mreq {
ipv6mr_multiaddr: libc::in6_addr {
s6_addr: group.octets(),
},
ipv6mr_interface: interface,
}
}
struct Datagrams {
fd: Fd,
local: SocketAddr,
}
#[derive(Clone)]
pub struct UdpSocket {
socket: Arc<Datagrams>,
}
impl UdpSocket {
pub(crate) fn new(fd: Fd, local: SocketAddr) -> Self {
Self {
socket: Arc::new(Datagrams { fd, local }),
}
}
#[inline(always)]
pub(crate) fn fd(&self) -> libc::c_int {
self.socket.fd.raw()
}
pub fn send_to(&self, addr: impl NetAddress, data: impl Into<Arc<[u8]>>) -> SendToTask {
SendToTask::new(self.clone(), addr.target(), data.into())
}
pub fn recv_from(&self) -> RecvFromTask {
RecvFromTask::new(self.clone())
}
pub fn connect(&self, addr: impl NetAddress) -> UdpConnectTask {
UdpConnectTask::new(self.clone(), addr.target())
}
pub fn send(&self, data: impl Into<Arc<[u8]>>) -> UdpSendTask {
UdpSendTask::new(self.clone(), data.into())
}
pub fn recv(&self) -> UdpRecvTask {
UdpRecvTask::new(self.clone())
}
pub fn peer_addr(&self) -> Result<SocketAddr, RuntimeError> {
peer_of(self.fd())
}
#[inline(always)]
pub fn local_addr(&self) -> SocketAddr {
self.socket.local
}
pub fn set_broadcast(&self, broadcast: bool) -> Result<(), RuntimeError> {
set_option(
self.fd(),
libc::SOL_SOCKET,
libc::SO_BROADCAST,
broadcast as libc::c_int,
)
}
pub fn set_ttl(&self, hops: u32) -> Result<(), RuntimeError> {
let hops = libc::c_int::try_from(hops).map_err(|_| RuntimeError::BadArgument)?;
let (level, option) = self.hop_option();
set_option(self.fd(), level, option, hops)
}
pub fn ttl(&self) -> Result<u32, RuntimeError> {
let (level, option) = self.hop_option();
Ok(get_option(self.fd(), level, option)? as u32)
}
fn hop_option(&self) -> (libc::c_int, libc::c_int) {
match self.socket.local {
SocketAddr::V4(_) => (libc::IPPROTO_IP, libc::IP_TTL),
SocketAddr::V6(_) => (libc::IPPROTO_IPV6, libc::IPV6_UNICAST_HOPS),
}
}
pub fn join_multicast_v4(
&self,
group: Ipv4Addr,
interface: Ipv4Addr,
) -> Result<(), RuntimeError> {
set_raw(
self.fd(),
libc::IPPROTO_IP,
libc::IP_ADD_MEMBERSHIP,
&v4_request(group, interface),
)
}
pub fn leave_multicast_v4(
&self,
group: Ipv4Addr,
interface: Ipv4Addr,
) -> Result<(), RuntimeError> {
set_raw(
self.fd(),
libc::IPPROTO_IP,
libc::IP_DROP_MEMBERSHIP,
&v4_request(group, interface),
)
}
pub fn join_multicast_v6(&self, group: Ipv6Addr, interface: u32) -> Result<(), RuntimeError> {
set_raw(
self.fd(),
libc::IPPROTO_IPV6,
libc::IPV6_JOIN_GROUP,
&v6_request(group, interface),
)
}
pub fn leave_multicast_v6(&self, group: Ipv6Addr, interface: u32) -> Result<(), RuntimeError> {
set_raw(
self.fd(),
libc::IPPROTO_IPV6,
libc::IPV6_LEAVE_GROUP,
&v6_request(group, interface),
)
}
pub fn close(self) {
drop(self);
}
}
impl fmt::Debug for UdpSocket {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("UdpSocket")
.field("local", &self.socket.local)
.finish()
}
}