mod broadcast;
mod constants;
mod packet;
mod query;
mod socket;
use std::io;
use std::net::Ipv4Addr;
use std::sync::Arc;
use std::time::Duration;
use socket2::Socket;
use tokio::sync::mpsc;
use socket::{create_send_socket, create_recv_socket};
#[derive(Debug, Clone, PartialEq)]
pub struct MdnsResponse {
pub hostname: String,
pub ip: Ipv4Addr,
}
pub struct MdnsHandle {
send_socket: Arc<Socket>,
domain: String,
pub discoveries: mpsc::Receiver<MdnsResponse>,
pub responses: mpsc::Receiver<MdnsResponse>,
}
impl MdnsHandle {
pub async fn query(&self, hostname: &str) {
query::query(&self.send_socket, hostname, &self.domain).await;
}
}
pub struct Mdns {
name: String,
domain: String,
broadcast_interval: Duration,
send_socket: Arc<Socket>,
recv_socket: Arc<Socket>,
local_ip: Ipv4Addr,
}
impl Mdns {
pub fn new<S: Into<String>>(name: S, ip: Ipv4Addr) -> io::Result<Self> {
Self::with_interval(name, ip, Duration::from_secs(120))
}
pub fn with_interval<S: Into<String>>(name: S, ip: Ipv4Addr, interval: Duration) -> io::Result<Self> {
let send_socket = create_send_socket()?;
let recv_socket = create_recv_socket()?;
Ok(Self {
name: name.into(),
domain: "local".to_string(),
broadcast_interval: interval,
send_socket: Arc::new(send_socket),
recv_socket: Arc::new(recv_socket),
local_ip: ip,
})
}
pub fn with_domain<S: Into<String>>(mut self, domain: S) -> Self {
self.domain = domain.into();
self
}
pub fn name(&self) -> &str {
&self.name
}
pub fn broadcast_interval(&self) -> Duration {
self.broadcast_interval
}
pub fn run(self) -> MdnsHandle {
let (disc_tx, disc_rx) = mpsc::channel(100);
let (resp_tx, resp_rx) = mpsc::channel(100);
let send_socket = self.send_socket.clone();
let recv_socket = self.recv_socket.clone();
let name = Arc::new(self.name);
let domain = Arc::new(self.domain.clone());
let local_ip = self.local_ip;
let broadcast_interval = self.broadcast_interval;
let send_socket_clone = send_socket.clone();
let name_clone = name.clone();
let domain_clone = domain.clone();
tokio::spawn(async move {
broadcast::broadcast_loop(&send_socket_clone, &name_clone, &domain_clone, local_ip, broadcast_interval).await;
});
let send_socket_clone = send_socket.clone();
let name_clone = name.clone();
let domain_clone = domain.clone();
tokio::spawn(async move {
query::listen(&recv_socket, &send_socket_clone, &name_clone, &domain_clone, local_ip, disc_tx, resp_tx).await;
});
MdnsHandle {
send_socket,
domain: self.domain,
discoveries: disc_rx,
responses: resp_rx,
}
}
pub fn local_ip(&self) -> Ipv4Addr {
self.local_ip
}
pub async fn goodbye(&self) {
broadcast::send_goodbye(&self.send_socket, &self.name, &self.domain, self.local_ip).await;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_with_name() {
let ip = Ipv4Addr::new(192, 168, 1, 100);
let mdns = Mdns::new("myhost", ip).unwrap();
assert_eq!(mdns.name(), "myhost");
assert_eq!(mdns.local_ip(), ip);
assert_eq!(mdns.broadcast_interval(), Duration::from_secs(120));
}
#[test]
fn test_with_custom_interval() {
let ip = Ipv4Addr::new(10, 0, 0, 5);
let mdns = Mdns::with_interval("myhost", ip, Duration::from_secs(60)).unwrap();
assert_eq!(mdns.name(), "myhost");
assert_eq!(mdns.local_ip(), ip);
assert_eq!(mdns.broadcast_interval(), Duration::from_secs(60));
}
}