use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
use std::time::Duration;
use socket2::{Domain, Protocol, Socket, Type};
use super::error::TransportError;
pub fn bind_multicast_reuse(port: u16, group: [u8; 4]) -> Result<UdpSocket, TransportError> {
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port));
let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?;
socket.set_reuse_address(true)?;
#[cfg(not(target_os = "windows"))]
socket.set_reuse_port(true)?;
socket.bind(&addr.into())?;
let iface = Ipv4Addr::UNSPECIFIED;
socket.join_multicast_v4(&Ipv4Addr::from(group), &iface)?;
socket.set_multicast_loop_v4(true)?;
let udp: UdpSocket = socket.into();
Ok(udp)
}
pub fn probe_multicast_loopback(group: [u8; 4], timeout: Duration) -> bool {
let probe_port: u16 = 29301;
let make_sock = || -> Option<UdpSocket> {
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, probe_port));
let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP)).ok()?;
socket.set_reuse_address(true).ok()?;
#[cfg(not(target_os = "windows"))]
socket.set_reuse_port(true).ok()?;
socket.bind(&addr.into()).ok()?;
socket
.join_multicast_v4(&Ipv4Addr::from(group), &Ipv4Addr::UNSPECIFIED)
.ok()?;
socket.set_multicast_loop_v4(true).ok()?;
let udp: UdpSocket = socket.into();
udp.set_read_timeout(Some(timeout)).ok()?;
Some(udp)
};
let sender = match make_sock() {
Some(s) => s,
None => return false,
};
let receiver = match make_sock() {
Some(s) => s,
None => return false,
};
let dst = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(group), probe_port));
if sender.send_to(b"oxictl-mc-probe", dst).is_err() {
return false;
}
let mut buf = [0u8; 64];
receiver.recv_from(&mut buf).is_ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bind_multicast_reuse_succeeds() {
let port = crate::protocol::dds::transport::metatraffic_multicast_port(200);
let group = crate::protocol::dds::transport::SPDP_MULTICAST_IPV4;
let _ = bind_multicast_reuse(port, group);
}
#[test]
fn two_binds_same_port_with_reuseaddr() {
let port = crate::protocol::dds::transport::metatraffic_multicast_port(201);
let group = crate::protocol::dds::transport::SPDP_MULTICAST_IPV4;
match (
bind_multicast_reuse(port, group),
bind_multicast_reuse(port, group),
) {
(Ok(_), Ok(_)) => { }
_ => { }
}
}
}