use std::io;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, UdpSocket};
use std::sync::{Arc, Barrier};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{self, JoinHandle};
use std::time::Duration;
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
lazy_static! {
pub static ref STOP: &'static str = "stop";
}
fn new_socket(addr: &SocketAddr) -> io::Result<Socket> {
let domain = if addr.is_ipv4() {
Domain::ipv4()
} else {
Domain::ipv6()
};
let socket = Socket::new(domain, Type::dgram(), Some(Protocol::udp()))?;
socket.set_read_timeout(Some(Duration::from_millis(100)))?;
Ok(socket)
}
#[cfg(windows)]
fn bind_multicast(socket: &Socket, addr: &SocketAddr) -> io::Result<()> {
let addr = match *addr {
SocketAddr::V4(addr) => SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), addr.port()),
SocketAddr::V6(addr) => {
SocketAddr::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).into(), addr.port())
}
};
socket.bind(&socket2::SockAddr::from(addr))
}
#[cfg(unix)]
fn bind_multicast(socket: &Socket, addr: &SocketAddr) -> io::Result<()> {
socket.bind(&socket2::SockAddr::from(*addr))
}
fn join_multicast(addr: SocketAddr) -> io::Result<UdpSocket> {
let ip_addr = addr.ip();
let socket = new_socket(&addr)?;
match ip_addr {
IpAddr::V4(ref mdns_v4) => {
socket.join_multicast_v4(mdns_v4, &Ipv4Addr::new(0, 0, 0, 0))?;
}
IpAddr::V6(ref mdns_v6) => {
socket.join_multicast_v6(mdns_v6, 0)?;
socket.set_only_v6(true)?;
}
};
socket.set_reuse_address(true);
#[cfg(unix)]
socket.set_reuse_port(true);
#[cfg(unix)]
info!("Warning: Cannot set reuse of ports on Windows.");
bind_multicast(&socket, &addr)?;
Ok(socket.into_udp_socket())
}
pub fn multicast_listener(
addr: SocketAddr,
) -> JoinHandle<()> {
let server_barrier = Arc::new(Barrier::new(2));
let client_barrier = Arc::clone(&server_barrier);
let join_handle = std::thread::Builder::new()
.name(format!("dot:listener"))
.spawn(move || {
let listener = join_multicast(addr).expect("failed to create listener");
println!("dot:listener: joined: {}", addr);
server_barrier.wait();
println!("dot:listener: is ready");
let mut stop = false;
while !stop {
let mut buf = [0u8; 1024];
match listener.recv_from(&mut buf) {
Ok((len, remote_addr)) => {
let data = &buf[..len];
if *STOP.as_bytes() == *data.clone() {
stop = true;
}
println!(
"dot:listener: received request: {} from: {}",
String::from_utf8_lossy(data),
remote_addr
);
}
Err(err) => {
}
}
}
println!("dot:listener: stopped!");
})
.unwrap();
client_barrier.wait();
join_handle
}
pub fn new_sender(addr: &SocketAddr) -> io::Result<UdpSocket> {
let socket = new_socket(addr)?;
if addr.is_ipv4() {
socket.set_multicast_if_v4(&Ipv4Addr::new(0, 0, 0, 0))?;
socket.bind(&SockAddr::from(SocketAddr::new(
Ipv4Addr::new(0, 0, 0, 0).into(),
0,
)))?;
} else {
socket.set_multicast_if_v6(5)?;
socket.bind(&SockAddr::from(SocketAddr::new(
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).into(),
0,
)))?;
}
Ok(socket.into_udp_socket())
}