use std::collections::HashMap;
use std::io::IoSliceMut;
use std::io::{self, IoSlice};
use std::mem;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::os::unix::io::AsRawFd;
use network_interface::{NetworkInterface, NetworkInterfaceConfig, V4IfAddr};
use socket2::{Domain, Protocol, Socket, Type};
#[cfg(feature = "tokio")]
use tokio::io::Interest;
use nix::sys::socket::{self as sock, RecvMsg};
fn create_on_interfaces(
options: crate::MulticastOptions,
interfaces: Vec<Ipv4Addr>,
multicast_address: SocketAddrV4,
) -> io::Result<MulticastSocket> {
let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?;
socket.set_nonblocking(options.nonblocking)?;
if !options.nonblocking {
socket.set_read_timeout(options.read_timeout)?;
}
socket.set_multicast_loop_v4(options.loopback)?;
socket.set_reuse_address(true)?;
socket.set_reuse_port(true)?;
sock::setsockopt(socket.as_raw_fd(), sock::sockopt::Ipv4PacketInfo, &true)
.map_err(nix_to_io_error)?;
for interface in &interfaces {
socket.join_multicast_v4(multicast_address.ip(), &interface)?;
}
socket.bind(&SocketAddr::new(options.bind_address.into(), multicast_address.port()).into())?;
Ok(MulticastSocket {
socket,
inner: MulticastSocketInner {
interfaces,
multicast_address,
buffer_size: options.buffer_size,
},
})
}
struct MulticastSocketInner {
interfaces: Vec<Ipv4Addr>,
multicast_address: SocketAddrV4,
buffer_size: usize,
}
pub struct MulticastSocket {
socket: socket2::Socket,
inner: MulticastSocketInner,
}
#[derive(Debug, Clone)]
pub enum Interface {
Default,
Ip(Ipv4Addr),
Index(i32),
}
#[derive(Debug, Clone)]
pub struct Message {
pub data: Vec<u8>,
pub origin_address: SocketAddrV4,
pub interface: Interface,
}
pub fn all_ipv4_interfaces() -> io::Result<Vec<Ipv4Addr>> {
let interfaces =
NetworkInterface::show().map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
let mut collected_interfaces = HashMap::with_capacity(interfaces.len());
for iface in interfaces.iter() {
for (name, ipv4addr) in iface.addr.iter().filter_map(|addr| {
if let network_interface::Addr::V4(V4IfAddr { ip, .. }) = addr {
if !ip.is_loopback() {
Some((iface.name.clone(), ip.clone()))
} else {
None
}
} else {
None
}
}) {
if !collected_interfaces.contains_key(&name) {
collected_interfaces.insert(name, ipv4addr);
}
}
}
Ok(collected_interfaces.into_iter().map(|(_, ip)| ip).collect())
}
impl MulticastSocket {
pub fn all_interfaces(multicast_address: SocketAddrV4) -> io::Result<Self> {
let interfaces = all_ipv4_interfaces()?;
create_on_interfaces(Default::default(), interfaces, multicast_address)
}
pub fn with_options(
multicast_address: SocketAddrV4,
interfaces: Vec<Ipv4Addr>,
options: crate::MulticastOptions,
) -> io::Result<Self> {
create_on_interfaces(options, interfaces, multicast_address)
}
}
fn nix_to_io_error(e: nix::Error) -> io::Error {
match e {
nix::errno::Errno::EAGAIN => io::ErrorKind::WouldBlock.into(),
_ => io::Error::new(io::ErrorKind::Other, e),
}
}
impl MulticastSocket {
pub fn receive(&self) -> io::Result<Message> {
let mut data_buffer = vec![0; self.inner.buffer_size];
let mut control_buffer = nix::cmsg_space!(libc::in_pktinfo);
let io_slice = &mut [IoSliceMut::new(&mut data_buffer)];
let message: RecvMsg<sock::SockaddrIn> = sock::recvmsg(
self.socket.as_raw_fd(),
io_slice,
Some(&mut control_buffer),
sock::MsgFlags::empty(),
)
.map_err(nix_to_io_error)?;
let origin_address = match message.address {
Some(sockaddr) => SocketAddrV4::new(
Ipv4Addr::from(sockaddr.ip().to_le()),
sockaddr.port().to_le(),
),
_ => SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0),
};
let mut interface = Interface::Default;
for cmsg in message.cmsgs() {
if let sock::ControlMessageOwned::Ipv4PacketInfo(pktinfo) = cmsg {
interface = Interface::Index(pktinfo.ipi_ifindex as _);
}
}
let mut data = Vec::with_capacity(message.bytes);
for i in 0..message.bytes {
data.push(data_buffer[i]);
}
Ok(Message {
data,
origin_address,
interface,
})
}
pub fn send_to(
&self,
buf: &[u8],
interface: &Interface,
addr: SocketAddrV4,
) -> io::Result<usize> {
let mut pkt_info: libc::in_pktinfo = unsafe { mem::zeroed() };
match interface {
Interface::Default => {}
Interface::Ip(address) => {
pkt_info.ipi_spec_dst = libc::in_addr {
s_addr: u32::from_ne_bytes(address.octets()),
}
}
Interface::Index(index) => pkt_info.ipi_ifindex = *index as _,
};
sock::sendmsg(
self.socket.as_raw_fd(),
&[IoSlice::new(&buf)],
&[sock::ControlMessage::Ipv4PacketInfo(&pkt_info)],
sock::MsgFlags::empty(),
Some(&sock::SockaddrIn::from(SocketAddrV4::from(addr))),
)
.map_err(nix_to_io_error)
}
pub fn send(&self, buf: &[u8], interface: &Interface) -> io::Result<usize> {
self.send_to(buf, interface, self.inner.multicast_address)
}
pub fn broadcast_to(&self, buf: &[u8], addr: SocketAddrV4) -> io::Result<()> {
for interface in &self.inner.interfaces {
self.send_to(buf, &Interface::Ip(*interface), addr)?;
}
Ok(())
}
pub fn broadcast(&self, buf: &[u8]) -> io::Result<()> {
self.broadcast_to(buf, self.inner.multicast_address)
}
}
#[cfg(feature = "tokio")]
pub struct AsyncMulticastSocket {
socket: tokio::net::UdpSocket,
inner: MulticastSocketInner,
}
#[cfg(feature = "tokio")]
impl TryFrom<MulticastSocket> for AsyncMulticastSocket {
type Error = io::Error;
fn try_from(other: MulticastSocket) -> Result<Self, Self::Error> {
other.socket.set_nonblocking(true)?;
let sock = tokio::net::UdpSocket::from_std(other.socket.into())?;
Ok(Self {
socket: sock,
inner: other.inner,
})
}
}
#[cfg(feature = "tokio")]
impl AsyncMulticastSocket {
pub async fn receive(&self) -> io::Result<Message> {
let mut data_buffer = vec![0; self.inner.buffer_size];
self.socket
.async_io(Interest::READABLE, || {
let io_slice = &mut [IoSliceMut::new(&mut data_buffer)];
let mut control_buffer = nix::cmsg_space!(libc::in_pktinfo);
let message: RecvMsg<sock::SockaddrIn> = sock::recvmsg(
self.socket.as_raw_fd(),
io_slice,
Some(&mut control_buffer),
sock::MsgFlags::empty(),
)
.map_err(nix_to_io_error)?;
let origin_address = match message.address {
Some(sockaddr) => SocketAddrV4::new(
Ipv4Addr::from(sockaddr.ip().to_le()),
sockaddr.port().to_le(),
),
_ => SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0),
};
let mut interface = Interface::Default;
for cmsg in message.cmsgs() {
if let sock::ControlMessageOwned::Ipv4PacketInfo(pktinfo) = cmsg {
interface = Interface::Index(pktinfo.ipi_ifindex as _);
}
}
let mut data = Vec::with_capacity(message.bytes);
for i in 0..message.bytes {
data.push(data_buffer[i]);
}
Ok(Message {
data,
origin_address,
interface,
})
})
.await
}
pub async fn send_to(
&self,
buf: &[u8],
interface: &Interface,
addr: SocketAddrV4,
) -> io::Result<usize> {
let mut pkt_info: libc::in_pktinfo = unsafe { mem::zeroed() };
match interface {
Interface::Default => {}
Interface::Ip(address) => {
pkt_info.ipi_spec_dst = libc::in_addr {
s_addr: u32::from_ne_bytes(address.octets()),
}
}
Interface::Index(index) => pkt_info.ipi_ifindex = *index as _,
};
self.socket
.async_io(Interest::WRITABLE, || {
sock::sendmsg(
self.socket.as_raw_fd(),
&[IoSlice::new(&buf)],
&[sock::ControlMessage::Ipv4PacketInfo(&pkt_info)],
sock::MsgFlags::empty(),
Some(&sock::SockaddrIn::from(SocketAddrV4::from(addr))),
)
.map_err(nix_to_io_error)
})
.await
}
pub async fn send(&self, buf: &[u8], interface: &Interface) -> io::Result<usize> {
self.send_to(buf, interface, self.inner.multicast_address)
.await
}
pub async fn broadcast_to(&self, buf: &[u8], addr: SocketAddrV4) -> io::Result<()> {
for interface in &self.inner.interfaces {
self.send_to(buf, &Interface::Ip(*interface), addr).await?;
}
Ok(())
}
pub async fn broadcast(&self, buf: &[u8]) -> io::Result<()> {
self.broadcast_to(buf, self.inner.multicast_address).await
}
}