#![feature(try_trait)]
extern crate byteorder;
extern crate net2;
const READ_BUF_SIZE: usize = 4_096;
#[cfg(not(target_os = "windows"))]
use net2::unix::UnixUdpBuilderExt;
use std::net::ToSocketAddrs;
pub mod error;
pub use error::{MulticastDnsError, MulticastDnsResult};
pub mod dns;
pub use dns::*;
#[derive(Clone, Debug)]
pub struct Config {
pub bind_address: String,
pub bind_port: u16,
pub multicast_loop: bool,
pub multicast_ttl: u32,
pub multicast_address: String,
}
pub struct Builder {
config: Config,
}
impl Builder {
pub fn new() -> Self {
Builder {
config: Config {
bind_address: "0.0.0.0".to_string(),
bind_port: 5353,
multicast_loop: true,
multicast_ttl: 255,
multicast_address: "224.0.0.251".to_string(),
},
}
}
pub fn set_bind_address(&mut self, address: &str) -> &mut Self {
self.config.bind_address = address.to_string();
self
}
pub fn set_bind_port(&mut self, port: u16) -> &mut Self {
self.config.bind_port = port;
self
}
pub fn set_multicast_loop(&mut self, should_loop: bool) -> &mut Self {
self.config.multicast_loop = should_loop;
self
}
pub fn set_multicast_ttl(&mut self, ttl: u32) -> &mut Self {
self.config.multicast_ttl = ttl;
self
}
pub fn set_multicast_address(&mut self, address: &str) -> &mut Self {
self.config.multicast_address = address.to_string();
self
}
pub fn build(&mut self) -> Result<MulticastDns, MulticastDnsError> {
MulticastDns::new(self.config.clone())
}
}
pub struct MulticastDns {
config: Config,
socket: std::net::UdpSocket,
read_buf: [u8; READ_BUF_SIZE],
}
impl MulticastDns {
pub fn new(config: Config) -> Result<Self, MulticastDnsError> {
let socket = create_socket(&config.bind_address, config.bind_port)?;
socket.set_nonblocking(true)?;
socket.set_multicast_loop_v4(config.multicast_loop)?;
socket.set_multicast_ttl_v4(config.multicast_ttl)?;
socket.join_multicast_v4(
&config.multicast_address.parse()?,
&config.bind_address.parse()?,
)?;
Ok(MulticastDns {
config,
socket,
read_buf: [0; READ_BUF_SIZE],
})
}
pub fn send(&mut self, packet: &Packet) -> Result<(), MulticastDnsError> {
let addr = (
self.config.multicast_address.as_ref(),
self.config.bind_port,
)
.to_socket_addrs()?
.next()?;
let data = packet.to_raw()?;
self.socket.send_to(&data, &addr)?;
Ok(())
}
pub fn recv(&mut self) -> Result<Option<Packet>, MulticastDnsError> {
let (read, _) = match self.socket.recv_from(&mut self.read_buf) {
Ok(r) => r,
Err(e) => {
if e.kind() == std::io::ErrorKind::WouldBlock {
return Ok(None);
}
return Err(e.into());
}
};
if read > 0 {
let packet = Packet::with_raw(&self.read_buf[0..read])?;
return Ok(Some(packet));
}
Ok(None)
}
}
#[cfg(not(target_os = "windows"))]
fn create_socket(addr: &str, port: u16) -> Result<std::net::UdpSocket, MulticastDnsError> {
Ok(net2::UdpBuilder::new_v4()?
.reuse_address(true)?
.reuse_port(true)?
.bind((addr, port))?)
}
#[cfg(target_os = "windows")]
fn create_socket(addr: &str, port: u16) -> Result<std::net::UdpSocket, MulticastDnsError> {
Ok(net2::UdpBuilder::new_v4()?
.reuse_address(true)?
.bind((addr, port))?)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_should_loop_question() {
let mut mdns = Builder::new()
.set_bind_address("0.0.0.0")
.set_bind_port(55000)
.set_multicast_loop(true)
.set_multicast_ttl(255)
.set_multicast_address("224.0.0.251")
.build()
.expect("build fail");
let mut packet = dns::Packet::new();
packet.is_query = true;
packet.questions.push(dns::Question::Srv(dns::SrvDataQ {
name: b"lib3h.test.service".to_vec(),
}));
mdns.send(&packet).expect("send fail");
std::thread::sleep(std::time::Duration::from_millis(100));
let resp = mdns.recv().expect("recv fail");
match resp.unwrap().questions[0] {
Question::Srv(ref q) => {
assert_eq!(b"lib3h.test.service".to_vec(), q.name);
}
_ => panic!("BAD TYPE"),
}
}
#[test]
fn it_should_loop_answer() {
let mut mdns = Builder::new()
.set_bind_address("0.0.0.0")
.set_bind_port(55001)
.set_multicast_loop(true)
.set_multicast_ttl(255)
.set_multicast_address("224.0.0.251")
.build()
.expect("build fail");
let mut packet = dns::Packet::new();
packet.id = 0xbdbd;
packet.is_query = false;
packet.answers.push(dns::Answer::Srv(dns::SrvDataA {
name: b"lib3h.test.service".to_vec(),
ttl_seconds: 0x12345678,
priority: 0x1111,
weight: 0x2222,
port: 0x3333,
target: b"lib3h.test.target".to_vec(),
}));
mdns.send(&packet).expect("send fail");
std::thread::sleep(std::time::Duration::from_millis(100));
let resp = mdns.recv().expect("recv fail");
match resp.unwrap().answers[0] {
Answer::Srv(ref a) => {
assert_eq!(b"lib3h.test.service".to_vec(), a.name);
assert_eq!(0x12345678, a.ttl_seconds);
assert_eq!(0x1111, a.priority);
assert_eq!(0x2222, a.weight);
assert_eq!(0x3333, a.port);
assert_eq!(b"lib3h.test.target".to_vec(), a.target);
}
_ => panic!("BAD TYPE"),
}
}
}