use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
use crate::MDNS_PORT;
use crate::proto::MDNS_MULTICAST_IPV4;
use socket2::{Domain, Protocol, Socket, Type};
#[derive(Debug, Clone)]
pub struct MulticastSocket {
multicast_local_ipv4: Option<Ipv4Addr>,
multicast_local_port: Option<u16>,
interface: Option<Ipv4Addr>,
}
impl Default for MulticastSocket {
fn default() -> Self {
Self::new()
}
}
impl MulticastSocket {
pub fn new() -> Self {
Self {
multicast_local_ipv4: None,
multicast_local_port: None,
interface: None,
}
}
pub fn with_multicast_local_ipv4(mut self, multicast_local_ipv4: Ipv4Addr) -> Self {
self.multicast_local_ipv4 = Some(multicast_local_ipv4);
self
}
pub fn with_multicast_local_port(mut self, multicast_local_port: u16) -> Self {
self.multicast_local_port = Some(multicast_local_port);
self
}
pub fn with_interface(mut self, interface: Ipv4Addr) -> Self {
self.interface = Some(interface);
self
}
pub fn into_std(self) -> io::Result<UdpSocket> {
let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?;
socket.set_reuse_address(true)?;
#[cfg(all(unix, not(target_os = "solaris"), not(target_os = "illumos")))]
socket.set_reuse_port(true)?;
socket.set_nonblocking(true)?;
let multicast_local_ip = if let Some(multicast_local_ipv4) = self.multicast_local_ipv4 {
IpAddr::V4(multicast_local_ipv4)
} else if cfg!(target_os = "linux") {
IpAddr::V4(MDNS_MULTICAST_IPV4)
} else {
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
};
let multicast_local_port = if let Some(multicast_local_port) = self.multicast_local_port {
multicast_local_port
} else {
MDNS_PORT
};
let multicast_local_addr = SocketAddr::new(multicast_local_ip, multicast_local_port);
socket.bind(&multicast_local_addr.into())?;
let iface = self.interface.unwrap_or(Ipv4Addr::UNSPECIFIED);
socket.join_multicast_v4(&MDNS_MULTICAST_IPV4, &iface)?;
Ok(socket.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::proto::MDNS_PORT;
use std::str::FromStr;
#[test]
fn test_multicast_constants() {
assert_eq!(MDNS_MULTICAST_IPV4, Ipv4Addr::new(224, 0, 0, 251));
assert_eq!(MDNS_PORT, 5353);
}
#[test]
fn test_multicast_socket_builder() {
let builder = MulticastSocket::new()
.with_multicast_local_ipv4(Ipv4Addr::from_str("0.0.0.0").unwrap())
.with_multicast_local_port(5353);
assert!(builder.multicast_local_ipv4.is_some());
assert!(builder.multicast_local_port.is_some());
assert!(builder.interface.is_none());
}
#[test]
fn test_multicast_socket_with_interface() {
let interface = Ipv4Addr::new(192, 168, 1, 100);
let builder = MulticastSocket::new()
.with_multicast_local_ipv4(Ipv4Addr::from_str("0.0.0.0").unwrap())
.with_multicast_local_port(5353)
.with_interface(interface);
assert_eq!(builder.interface, Some(interface));
}
}