#[cfg(feature = "logging")]
use crate::log::{debug, error, warn};
use crate::{
dns_cache::DnsCache,
dns_parser::{
current_time_millis, ip_address_to_type, split_sub_domain, DnsAddress, DnsIncoming,
DnsOutgoing, DnsPointer, DnsRecordExt, DnsSrv, DnsTxt, CLASS_CACHE_FLUSH, CLASS_IN,
FLAGS_AA, FLAGS_QR_QUERY, FLAGS_QR_RESPONSE, MAX_MSG_ABSOLUTE, TYPE_A, TYPE_AAAA, TYPE_ANY,
TYPE_PTR, TYPE_SRV, TYPE_TXT,
},
error::{Error, Result},
service_info::ServiceInfo,
Receiver,
};
use flume::{bounded, Sender, TrySendError};
use if_addrs::{IfAddr, Interface};
use polling::Poller;
use socket2::{SockAddr, Socket};
use std::{
cmp::{self, Reverse},
collections::{BinaryHeap, HashMap, HashSet},
fmt,
io::Read,
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, UdpSocket},
str, thread,
time::Duration,
vec,
};
macro_rules! e_fmt {
($($arg:tt)+) => {
Error::Msg(format!($($arg)+))
};
}
pub const SERVICE_NAME_LEN_MAX_DEFAULT: u8 = 15;
const MDNS_PORT: u16 = 5353;
const GROUP_ADDR_V4: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 251);
const GROUP_ADDR_V6: Ipv6Addr = Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0xfb);
const LOOPBACK_V4: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
const RESOLVE_WAIT_IN_MILLIS: u64 = 500;
#[derive(Debug)]
pub enum UnregisterStatus {
OK,
NotFound,
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[non_exhaustive]
pub enum DaemonStatus {
Running,
Shutdown,
}
#[derive(Hash, Eq, PartialEq)]
enum Counter {
Register,
RegisterResend,
Unregister,
UnregisterResend,
Browse,
ResolveHostname,
Respond,
CacheRefreshPTR,
CacheRefreshSRV,
CacheRefreshAddr,
KnownAnswerSuppression,
}
impl fmt::Display for Counter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Register => write!(f, "register"),
Self::RegisterResend => write!(f, "register-resend"),
Self::Unregister => write!(f, "unregister"),
Self::UnregisterResend => write!(f, "unregister-resend"),
Self::Browse => write!(f, "browse"),
Self::ResolveHostname => write!(f, "resolve-hostname"),
Self::Respond => write!(f, "respond"),
Self::CacheRefreshPTR => write!(f, "cache-refresh-ptr"),
Self::CacheRefreshSRV => write!(f, "cache-refresh-srv"),
Self::CacheRefreshAddr => write!(f, "cache-refresh-addr"),
Self::KnownAnswerSuppression => write!(f, "known-answer-suppression"),
}
}
}
pub type Metrics = HashMap<String, i64>;
const SIGNAL_SOCK_EVENT_KEY: usize = usize::MAX - 1;
#[derive(Clone)]
pub struct ServiceDaemon {
sender: Sender<Command>,
signal_addr: SocketAddr,
}
impl ServiceDaemon {
pub fn new() -> Result<Self> {
let signal_addr = SocketAddrV4::new(LOOPBACK_V4, 0);
let signal_sock = UdpSocket::bind(signal_addr)
.map_err(|e| e_fmt!("failed to create signal_sock for daemon: {}", e))?;
let signal_addr = signal_sock
.local_addr()
.map_err(|e| e_fmt!("failed to get signal sock addr: {}", e))?;
signal_sock
.set_nonblocking(true)
.map_err(|e| e_fmt!("failed to set nonblocking for signal socket: {}", e))?;
let poller = Poller::new().map_err(|e| e_fmt!("Failed to create Poller: {}", e))?;
let (sender, receiver) = bounded(100);
thread::Builder::new()
.name("mDNS_daemon".to_string())
.spawn(move || Self::daemon_thread(signal_sock, poller, receiver))
.map_err(|e| e_fmt!("thread builder failed to spawn: {}", e))?;
Ok(Self {
sender,
signal_addr,
})
}
fn send_cmd(&self, cmd: Command) -> Result<()> {
let cmd_name = cmd.to_string();
self.sender.try_send(cmd).map_err(|e| match e {
TrySendError::Full(_) => Error::Again,
e => e_fmt!("flume::channel::send failed: {}", e),
})?;
let addr = SocketAddrV4::new(LOOPBACK_V4, 0);
let socket = UdpSocket::bind(addr)
.map_err(|e| e_fmt!("Failed to create socket to send signal: {}", e))?;
socket
.send_to(cmd_name.as_bytes(), self.signal_addr)
.map_err(|e| {
e_fmt!(
"signal socket send_to {} ({}) failed: {}",
self.signal_addr,
cmd_name,
e
)
})?;
Ok(())
}
pub fn browse(&self, service_type: &str) -> Result<Receiver<ServiceEvent>> {
check_domain_suffix(service_type)?;
let (resp_s, resp_r) = bounded(10);
self.send_cmd(Command::Browse(service_type.to_string(), 1, resp_s))?;
Ok(resp_r)
}
pub fn stop_browse(&self, ty_domain: &str) -> Result<()> {
self.send_cmd(Command::StopBrowse(ty_domain.to_string()))
}
pub fn resolve_hostname(
&self,
hostname: &str,
timeout: Option<u64>,
) -> Result<Receiver<HostnameResolutionEvent>> {
check_hostname(hostname)?;
let (resp_s, resp_r) = bounded(10);
self.send_cmd(Command::ResolveHostname(
hostname.to_string(),
1,
resp_s,
timeout,
))?;
Ok(resp_r)
}
pub fn stop_resolve_hostname(&self, hostname: &str) -> Result<()> {
self.send_cmd(Command::StopResolveHostname(hostname.to_string()))
}
pub fn register(&self, mut service_info: ServiceInfo) -> Result<()> {
check_service_name(service_info.get_fullname())?;
check_hostname(service_info.get_hostname())?;
if service_info.is_addr_auto() {
for iface in my_ip_interfaces() {
service_info.insert_ipaddr(iface.ip());
}
}
self.send_cmd(Command::Register(service_info))
}
pub fn unregister(&self, fullname: &str) -> Result<Receiver<UnregisterStatus>> {
let (resp_s, resp_r) = bounded(1);
self.send_cmd(Command::Unregister(fullname.to_lowercase(), resp_s))?;
Ok(resp_r)
}
pub fn monitor(&self) -> Result<Receiver<DaemonEvent>> {
let (resp_s, resp_r) = bounded(100);
self.send_cmd(Command::Monitor(resp_s))?;
Ok(resp_r)
}
pub fn shutdown(&self) -> Result<Receiver<DaemonStatus>> {
let (resp_s, resp_r) = bounded(1);
self.send_cmd(Command::Exit(resp_s))?;
Ok(resp_r)
}
pub fn status(&self) -> Result<Receiver<DaemonStatus>> {
let (resp_s, resp_r) = bounded(1);
if self.sender.is_disconnected() {
resp_s
.send(DaemonStatus::Shutdown)
.map_err(|e| e_fmt!("failed to send daemon status to the client: {}", e))?;
} else {
self.send_cmd(Command::GetStatus(resp_s))?;
}
Ok(resp_r)
}
pub fn get_metrics(&self) -> Result<Receiver<Metrics>> {
let (resp_s, resp_r) = bounded(1);
self.send_cmd(Command::GetMetrics(resp_s))?;
Ok(resp_r)
}
pub fn set_service_name_len_max(&self, len_max: u8) -> Result<()> {
const SERVICE_NAME_LEN_MAX_LIMIT: u8 = 30;
if len_max > SERVICE_NAME_LEN_MAX_LIMIT {
return Err(Error::Msg(format!(
"service name length max {} is too large",
len_max
)));
}
self.send_cmd(Command::SetOption(DaemonOption::ServiceNameLenMax(len_max)))
}
pub fn enable_interface(&self, if_kind: impl IntoIfKindVec) -> Result<()> {
let if_kind_vec = if_kind.into_vec();
self.send_cmd(Command::SetOption(DaemonOption::EnableInterface(
if_kind_vec.kinds,
)))
}
pub fn disable_interface(&self, if_kind: impl IntoIfKindVec) -> Result<()> {
let if_kind_vec = if_kind.into_vec();
self.send_cmd(Command::SetOption(DaemonOption::DisableInterface(
if_kind_vec.kinds,
)))
}
fn daemon_thread(signal_sock: UdpSocket, poller: Poller, receiver: Receiver<Command>) {
let zc = Zeroconf::new(signal_sock, poller);
if let Some(cmd) = Self::run(zc, receiver) {
match cmd {
Command::Exit(resp_s) => {
if let Err(e) = resp_s.send(DaemonStatus::Shutdown) {
error!("exit: failed to send response of shutdown: {}", e);
}
}
_ => {
error!("Unexpected command: {:?}", cmd);
}
}
}
}
fn handle_poller_events(zc: &mut Zeroconf, events: &[polling::Event]) {
for ev in events.iter() {
debug!("event received with key {}", ev.key);
if ev.key == SIGNAL_SOCK_EVENT_KEY {
zc.signal_sock_drain();
if let Err(e) = zc
.poller
.modify(&zc.signal_sock, polling::Event::readable(ev.key))
{
error!("failed to modify poller for signal socket: {}", e);
}
continue; }
let intf = match zc.poll_ids.get(&ev.key) {
Some(interface) => interface.clone(),
None => {
error!("Ip for event key {} not found", ev.key);
break;
}
};
while zc.handle_read(&intf) {}
if let Some(sock) = zc.intf_socks.get(&intf) {
if let Err(e) = zc.poller.modify(sock, polling::Event::readable(ev.key)) {
error!("modify poller for interface {:?}: {}", &intf, e);
break;
}
}
}
}
fn run(mut zc: Zeroconf, receiver: Receiver<Command>) -> Option<Command> {
if let Err(e) = zc.poller.add(
&zc.signal_sock,
polling::Event::readable(SIGNAL_SOCK_EVENT_KEY),
) {
error!("failed to add signal socket to the poller: {}", e);
return None;
}
for (intf, sock) in zc.intf_socks.iter() {
let key =
Zeroconf::add_poll_impl(&mut zc.poll_ids, &mut zc.poll_id_count, intf.clone());
if let Err(e) = zc.poller.add(sock, polling::Event::readable(key)) {
error!("add socket of {:?} to poller: {}", intf, e);
return None;
}
}
const IP_CHECK_INTERVAL_MILLIS: u64 = 30_000;
let mut next_ip_check = current_time_millis() + IP_CHECK_INTERVAL_MILLIS;
zc.add_timer(next_ip_check);
let mut events = Vec::new();
loop {
let now = current_time_millis();
let earliest_timer = zc.peek_earliest_timer();
let timeout = earliest_timer.map(|timer| {
let millis = if timer > now { timer - now } else { 1 };
Duration::from_millis(millis)
});
events.clear();
match zc.poller.wait(&mut events, timeout) {
Ok(_) => Self::handle_poller_events(&mut zc, &events),
Err(e) => error!("failed to select from sockets: {}", e),
}
let now = current_time_millis();
if let Some(timer) = earliest_timer {
if now >= timer {
zc.pop_earliest_timer();
}
}
for hostname in zc
.hostname_resolvers
.clone()
.into_iter()
.filter(|(_, (_, timeout))| timeout.map(|t| now >= t).unwrap_or(false))
.map(|(hostname, _)| hostname)
{
debug!("hostname resolver timeout for {}", &hostname);
call_hostname_resolution_listener(
&zc.hostname_resolvers,
&hostname,
HostnameResolutionEvent::SearchTimeout(hostname.to_owned()),
);
call_hostname_resolution_listener(
&zc.hostname_resolvers,
&hostname,
HostnameResolutionEvent::SearchStopped(hostname.to_owned()),
);
zc.hostname_resolvers.remove(&hostname);
}
while let Ok(command) = receiver.try_recv() {
if matches!(command, Command::Exit(_)) {
zc.status = DaemonStatus::Shutdown;
return Some(command);
}
Self::exec_command(&mut zc, command, false);
}
let mut i = 0;
while i < zc.retransmissions.len() {
if now >= zc.retransmissions[i].next_time {
let rerun = zc.retransmissions.remove(i);
Self::exec_command(&mut zc, rerun.command, true);
} else {
i += 1;
}
}
zc.refresh_active_services();
let mut query_count = 0;
for (hostname, _sender) in zc.hostname_resolvers.iter() {
for (hostname, ip_addr) in
zc.cache.refresh_due_hostname_resolutions(hostname).iter()
{
zc.send_query(hostname, ip_address_to_type(ip_addr));
query_count += 1;
}
}
zc.increase_counter(Counter::CacheRefreshAddr, query_count);
let now = current_time_millis();
let expired_services = zc.cache.evict_expired_services(now);
zc.notify_service_removal(expired_services);
let expired_addrs = zc.cache.evict_expired_addr(now);
for (hostname, addrs) in expired_addrs {
call_hostname_resolution_listener(
&zc.hostname_resolvers,
&hostname,
HostnameResolutionEvent::AddressesRemoved(hostname.clone(), addrs),
);
let instances = zc.cache.get_instances_on_host(&hostname);
let instance_set: HashSet<String> = instances.into_iter().collect();
zc.resolve_updated_instances(instance_set);
}
if now > next_ip_check {
next_ip_check = now + IP_CHECK_INTERVAL_MILLIS;
zc.check_ip_changes();
zc.add_timer(next_ip_check);
}
}
}
fn exec_command(zc: &mut Zeroconf, command: Command, repeating: bool) {
match command {
Command::Browse(ty, next_delay, listener) => {
zc.exec_command_browse(repeating, ty, next_delay, listener);
}
Command::ResolveHostname(hostname, next_delay, listener, timeout) => {
zc.exec_command_resolve_hostname(
repeating, hostname, next_delay, listener, timeout,
);
}
Command::Register(service_info) => {
debug!("register service {:?}", &service_info);
zc.register_service(service_info);
zc.increase_counter(Counter::Register, 1);
}
Command::RegisterResend(fullname) => {
debug!("announce service: {}", &fullname);
zc.exec_command_register_resend(fullname);
}
Command::Unregister(fullname, resp_s) => {
debug!("unregister service {} repeat {}", &fullname, &repeating);
zc.exec_command_unregister(repeating, fullname, resp_s);
}
Command::UnregisterResend(packet, ip) => {
zc.exec_command_unregister_resend(packet, ip);
}
Command::StopBrowse(ty_domain) => zc.exec_command_stop_browse(ty_domain),
Command::StopResolveHostname(hostname) => {
zc.exec_command_stop_resolve_hostname(hostname)
}
Command::Resolve(instance, try_count) => zc.exec_command_resolve(instance, try_count),
Command::GetMetrics(resp_s) => match resp_s.send(zc.counters.clone()) {
Ok(()) => debug!("Sent metrics to the client"),
Err(e) => error!("Failed to send metrics: {}", e),
},
Command::GetStatus(resp_s) => match resp_s.send(zc.status.clone()) {
Ok(()) => debug!("Sent status to the client"),
Err(e) => error!("Failed to send status: {}", e),
},
Command::Monitor(resp_s) => {
zc.monitors.push(resp_s);
}
Command::SetOption(daemon_opt) => {
zc.process_set_option(daemon_opt);
}
_ => {
error!("unexpected command: {:?}", &command);
}
}
}
}
fn new_socket_bind(intf: &Interface) -> Result<Socket> {
let intf_ip = &intf.ip();
match intf_ip {
IpAddr::V4(ip) => {
let addr = SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), MDNS_PORT);
let sock = new_socket(addr.into(), true)?;
sock.join_multicast_v4(&GROUP_ADDR_V4, ip)
.map_err(|e| e_fmt!("join multicast group on addr {}: {}", intf_ip, e))?;
sock.set_multicast_if_v4(ip)
.map_err(|e| e_fmt!("set multicast_if on addr {}: {}", ip, e))?;
let multicast_addr = SocketAddrV4::new(GROUP_ADDR_V4, MDNS_PORT).into();
let test_packets = DnsOutgoing::new(0).to_data_on_wire();
for packet in test_packets {
sock.send_to(&packet, &multicast_addr)
.map_err(|e| e_fmt!("send multicast packet on addr {}: {}", ip, e))?;
}
Ok(sock)
}
IpAddr::V6(ip) => {
let addr = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), MDNS_PORT, 0, 0);
let sock = new_socket(addr.into(), true)?;
sock.join_multicast_v6(&GROUP_ADDR_V6, intf.index.unwrap_or(0))
.map_err(|e| e_fmt!("join multicast group on addr {}: {}", ip, e))?;
sock.set_multicast_if_v6(intf.index.unwrap_or(0))
.map_err(|e| e_fmt!("set multicast_if on addr {}: {}", ip, e))?;
Ok(sock)
}
}
}
fn new_socket(addr: SocketAddr, non_block: bool) -> Result<Socket> {
let domain = match addr {
SocketAddr::V4(_) => socket2::Domain::IPV4,
SocketAddr::V6(_) => socket2::Domain::IPV6,
};
let fd = Socket::new(domain, socket2::Type::DGRAM, None)
.map_err(|e| e_fmt!("create socket failed: {}", e))?;
fd.set_reuse_address(true)
.map_err(|e| e_fmt!("set ReuseAddr failed: {}", e))?;
#[cfg(unix)] fd.set_reuse_port(true)
.map_err(|e| e_fmt!("set ReusePort failed: {}", e))?;
if non_block {
fd.set_nonblocking(true)
.map_err(|e| e_fmt!("set O_NONBLOCK: {}", e))?;
}
fd.bind(&addr.into())
.map_err(|e| e_fmt!("socket bind to {} failed: {}", &addr, e))?;
debug!("new socket bind to {}", &addr);
Ok(fd)
}
struct ReRun {
next_time: u64,
command: Command,
}
#[derive(Debug, Eq, Hash, PartialEq)]
enum IpVersion {
V4,
V6,
}
#[derive(Debug, Eq, Hash, PartialEq)]
struct MulticastSendTracker {
intf_index: u32,
ip_version: IpVersion,
}
fn multicast_send_tracker(intf: &Interface) -> Option<MulticastSendTracker> {
match intf.index {
Some(index) => {
let ip_ver = match intf.addr {
IfAddr::V4(_) => IpVersion::V4,
IfAddr::V6(_) => IpVersion::V6,
};
Some(MulticastSendTracker {
intf_index: index,
ip_version: ip_ver,
})
}
None => None,
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum IfKind {
All,
IPv4,
IPv6,
Name(String),
Addr(IpAddr),
}
impl IfKind {
fn matches(&self, intf: &Interface) -> bool {
match self {
Self::All => true,
Self::IPv4 => intf.ip().is_ipv4(),
Self::IPv6 => intf.ip().is_ipv6(),
Self::Name(ifname) => ifname == &intf.name,
Self::Addr(addr) => addr == &intf.ip(),
}
}
}
impl From<&str> for IfKind {
fn from(val: &str) -> Self {
Self::Name(val.to_string())
}
}
impl From<&String> for IfKind {
fn from(val: &String) -> Self {
Self::Name(val.to_string())
}
}
impl From<IpAddr> for IfKind {
fn from(val: IpAddr) -> Self {
Self::Addr(val)
}
}
pub struct IfKindVec {
kinds: Vec<IfKind>,
}
pub trait IntoIfKindVec {
fn into_vec(self) -> IfKindVec;
}
impl<T: Into<IfKind>> IntoIfKindVec for T {
fn into_vec(self) -> IfKindVec {
let if_kind: IfKind = self.into();
IfKindVec {
kinds: vec![if_kind],
}
}
}
impl<T: Into<IfKind>> IntoIfKindVec for Vec<T> {
fn into_vec(self) -> IfKindVec {
let kinds: Vec<IfKind> = self.into_iter().map(|x| x.into()).collect();
IfKindVec { kinds }
}
}
struct IfSelection {
if_kind: IfKind,
selected: bool,
}
struct Zeroconf {
intf_socks: HashMap<Interface, Socket>,
poll_ids: HashMap<usize, Interface>,
poll_id_count: usize,
my_services: HashMap<String, ServiceInfo>,
cache: DnsCache,
service_queriers: HashMap<String, Sender<ServiceEvent>>,
hostname_resolvers: HashMap<String, (Sender<HostnameResolutionEvent>, Option<u64>)>,
retransmissions: Vec<ReRun>,
counters: Metrics,
poller: Poller,
monitors: Vec<Sender<DaemonEvent>>,
service_name_len_max: u8,
if_selections: Vec<IfSelection>,
signal_sock: UdpSocket,
timers: BinaryHeap<Reverse<u64>>,
status: DaemonStatus,
pending_resolves: HashSet<String>,
}
impl Zeroconf {
fn new(signal_sock: UdpSocket, poller: Poller) -> Self {
let my_ifaddrs = my_ip_interfaces();
let mut intf_socks = HashMap::new();
for intf in my_ifaddrs {
let sock = match new_socket_bind(&intf) {
Ok(s) => s,
Err(e) => {
debug!("bind a socket to {}: {}. Skipped.", &intf.ip(), e);
continue;
}
};
intf_socks.insert(intf, sock);
}
let monitors = Vec::new();
let service_name_len_max = SERVICE_NAME_LEN_MAX_DEFAULT;
let timers = BinaryHeap::new();
let if_selections = vec![];
let status = DaemonStatus::Running;
Self {
intf_socks,
poll_ids: HashMap::new(),
poll_id_count: 0,
my_services: HashMap::new(),
cache: DnsCache::new(),
hostname_resolvers: HashMap::new(),
service_queriers: HashMap::new(),
retransmissions: Vec::new(),
counters: HashMap::new(),
poller,
monitors,
service_name_len_max,
if_selections,
signal_sock,
timers,
status,
pending_resolves: HashSet::new(),
}
}
fn process_set_option(&mut self, daemon_opt: DaemonOption) {
match daemon_opt {
DaemonOption::ServiceNameLenMax(length) => self.service_name_len_max = length,
DaemonOption::EnableInterface(if_kind) => self.enable_interface(if_kind),
DaemonOption::DisableInterface(if_kind) => self.disable_interface(if_kind),
}
}
fn enable_interface(&mut self, kinds: Vec<IfKind>) {
for if_kind in kinds {
self.if_selections.push(IfSelection {
if_kind,
selected: true,
});
}
self.apply_intf_selections(my_ip_interfaces());
}
fn disable_interface(&mut self, kinds: Vec<IfKind>) {
for if_kind in kinds {
self.if_selections.push(IfSelection {
if_kind,
selected: false,
});
}
self.apply_intf_selections(my_ip_interfaces());
}
fn notify_monitors(&mut self, event: DaemonEvent) {
self.monitors.retain(|sender| {
if let Err(e) = sender.try_send(event.clone()) {
error!("notify_monitors: try_send: {}", &e);
if matches!(e, TrySendError::Disconnected(_)) {
return false; }
}
true
});
}
fn add_addr_in_my_services(&mut self, addr: IpAddr) {
for (_, service_info) in self.my_services.iter_mut() {
if service_info.is_addr_auto() {
service_info.insert_ipaddr(addr);
}
}
}
fn del_addr_in_my_services(&mut self, addr: &IpAddr) {
for (_, service_info) in self.my_services.iter_mut() {
if service_info.is_addr_auto() {
service_info.remove_ipaddr(addr);
}
}
}
fn add_poll(&mut self, intf: Interface) -> usize {
Self::add_poll_impl(&mut self.poll_ids, &mut self.poll_id_count, intf)
}
fn add_poll_impl(
poll_ids: &mut HashMap<usize, Interface>,
poll_id_count: &mut usize,
intf: Interface,
) -> usize {
let key = *poll_id_count;
*poll_id_count += 1;
let _ = (*poll_ids).insert(key, intf);
key
}
fn add_timer(&mut self, next_time: u64) {
self.timers.push(Reverse(next_time));
}
fn peek_earliest_timer(&self) -> Option<u64> {
self.timers.peek().map(|Reverse(v)| *v)
}
fn pop_earliest_timer(&mut self) -> Option<u64> {
self.timers.pop().map(|Reverse(v)| v)
}
fn apply_intf_selections(&mut self, interfaces: Vec<Interface>) {
let intf_count = interfaces.len();
let mut intf_selections = vec![true; intf_count];
for selection in self.if_selections.iter() {
for i in 0..intf_count {
if selection.if_kind.matches(&interfaces[i]) {
intf_selections[i] = selection.selected;
}
}
}
for (idx, intf) in interfaces.into_iter().enumerate() {
if intf_selections[idx] {
if !self.intf_socks.contains_key(&intf) {
self.add_new_interface(intf);
}
} else {
if let Some(sock) = self.intf_socks.remove(&intf) {
if let Err(e) = self.poller.delete(&sock) {
error!("process_if_selections: poller.delete {:?}: {}", &intf, e);
}
self.poll_ids.retain(|_, v| v != &intf);
}
}
}
}
fn check_ip_changes(&mut self) {
let my_ifaddrs = my_ip_interfaces();
let poll_ids = &mut self.poll_ids;
let poller = &mut self.poller;
let deleted_addrs = self
.intf_socks
.iter()
.filter_map(|(intf, sock)| {
if !my_ifaddrs.contains(intf) {
if let Err(e) = poller.delete(sock) {
error!("check_ip_changes: poller.delete {:?}: {}", intf, e);
}
poll_ids.retain(|_, v| v != intf);
Some(intf.ip())
} else {
None
}
})
.collect::<Vec<IpAddr>>();
for ip in deleted_addrs.iter() {
self.del_addr_in_my_services(ip);
self.notify_monitors(DaemonEvent::IpDel(*ip));
}
self.intf_socks.retain(|intf, _| my_ifaddrs.contains(intf));
self.apply_intf_selections(my_ifaddrs);
}
fn add_new_interface(&mut self, intf: Interface) {
let new_ip = intf.ip();
let sock = match new_socket_bind(&intf) {
Ok(s) => s,
Err(e) => {
error!("bind a socket to {}: {}. Skipped.", &intf.ip(), e);
return;
}
};
let key = self.add_poll(intf.clone());
if let Err(e) = self.poller.add(&sock, polling::Event::readable(key)) {
error!("check_ip_changes: poller add ip {}: {}", new_ip, e);
return;
}
self.intf_socks.insert(intf, sock);
self.add_addr_in_my_services(new_ip);
self.notify_monitors(DaemonEvent::IpAdd(new_ip));
}
fn register_service(&mut self, info: ServiceInfo) {
if let Err(e) = check_service_name_length(info.get_type(), self.service_name_len_max) {
error!("check_service_name_length: {}", &e);
self.notify_monitors(DaemonEvent::Error(e));
return;
}
let outgoing_addrs = self.send_unsolicited_response(&info);
if !outgoing_addrs.is_empty() {
self.notify_monitors(DaemonEvent::Announce(
info.get_fullname().to_string(),
format!("{:?}", &outgoing_addrs),
));
}
let next_time = current_time_millis() + 1000;
let service_fullname = info.get_fullname().to_lowercase();
self.add_retransmission(next_time, Command::RegisterResend(service_fullname.clone()));
self.my_services.insert(service_fullname, info);
}
fn send_unsolicited_response(&self, info: &ServiceInfo) -> Vec<IpAddr> {
let mut outgoing_addrs = Vec::new();
let mut multicast_sent_trackers = HashSet::new();
for (intf, sock) in self.intf_socks.iter() {
if let Some(tracker) = multicast_send_tracker(intf) {
if multicast_sent_trackers.contains(&tracker) {
continue; }
}
if self.broadcast_service_on_intf(info, intf, sock) {
if let Some(tracker) = multicast_send_tracker(intf) {
multicast_sent_trackers.insert(tracker);
}
outgoing_addrs.push(intf.ip());
}
}
outgoing_addrs
}
fn broadcast_service_on_intf(
&self,
info: &ServiceInfo,
intf: &Interface,
sock: &Socket,
) -> bool {
let service_fullname = info.get_fullname();
debug!("broadcast service {}", service_fullname);
let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE | FLAGS_AA);
out.add_answer_at_time(
DnsPointer::new(
info.get_type(),
TYPE_PTR,
CLASS_IN,
info.get_other_ttl(),
info.get_fullname().to_string(),
),
0,
);
if let Some(sub) = info.get_subtype() {
debug!("Adding subdomain {}", sub);
out.add_answer_at_time(
DnsPointer::new(
sub,
TYPE_PTR,
CLASS_IN,
info.get_other_ttl(),
info.get_fullname().to_string(),
),
0,
);
}
out.add_answer_at_time(
DnsSrv::new(
info.get_fullname(),
CLASS_IN | CLASS_CACHE_FLUSH,
info.get_host_ttl(),
info.get_priority(),
info.get_weight(),
info.get_port(),
info.get_hostname().to_string(),
),
0,
);
out.add_answer_at_time(
DnsTxt::new(
info.get_fullname(),
CLASS_IN | CLASS_CACHE_FLUSH,
info.get_other_ttl(),
info.generate_txt(),
),
0,
);
let intf_addrs = info.get_addrs_on_intf(intf);
if intf_addrs.is_empty() {
debug!("No valid addrs to add on intf {:?}", &intf);
return false;
}
for address in intf_addrs {
out.add_answer_at_time(
DnsAddress::new(
info.get_hostname(),
ip_address_to_type(&address),
CLASS_IN | CLASS_CACHE_FLUSH,
info.get_host_ttl(),
address,
),
0,
);
}
send_dns_outgoing(&out, intf, sock);
true
}
fn unregister_service(&self, info: &ServiceInfo, intf: &Interface, sock: &Socket) -> Vec<u8> {
let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE | FLAGS_AA);
out.add_answer_at_time(
DnsPointer::new(
info.get_type(),
TYPE_PTR,
CLASS_IN,
0,
info.get_fullname().to_string(),
),
0,
);
if let Some(sub) = info.get_subtype() {
debug!("Adding subdomain {}", sub);
out.add_answer_at_time(
DnsPointer::new(sub, TYPE_PTR, CLASS_IN, 0, info.get_fullname().to_string()),
0,
);
}
out.add_answer_at_time(
DnsSrv::new(
info.get_fullname(),
CLASS_IN | CLASS_CACHE_FLUSH,
0,
info.get_priority(),
info.get_weight(),
info.get_port(),
info.get_hostname().to_string(),
),
0,
);
out.add_answer_at_time(
DnsTxt::new(
info.get_fullname(),
CLASS_IN | CLASS_CACHE_FLUSH,
0,
info.generate_txt(),
),
0,
);
for address in info.get_addrs_on_intf(intf) {
out.add_answer_at_time(
DnsAddress::new(
info.get_hostname(),
ip_address_to_type(&address),
CLASS_IN | CLASS_CACHE_FLUSH,
0,
address,
),
0,
);
}
send_dns_outgoing(&out, intf, sock).remove(0)
}
fn add_service_querier(&mut self, ty: String, listener: Sender<ServiceEvent>) {
self.service_queriers.insert(ty, listener);
}
fn add_hostname_resolver(
&mut self,
hostname: String,
listener: Sender<HostnameResolutionEvent>,
timeout: Option<u64>,
) {
let real_timeout = timeout.map(|t| current_time_millis() + t);
self.hostname_resolvers
.insert(hostname, (listener, real_timeout));
if let Some(t) = real_timeout {
self.add_timer(t);
}
}
fn send_query(&self, name: &str, qtype: u16) {
self.send_query_vec(&[(name, qtype)]);
}
fn send_query_vec(&self, questions: &[(&str, u16)]) {
debug!("Sending query questions: {:?}", questions);
let mut out = DnsOutgoing::new(FLAGS_QR_QUERY);
let now = current_time_millis();
for (name, qtype) in questions {
out.add_question(name, *qtype);
for record in self.cache.get_known_answers(name, *qtype, now) {
debug!("add known answer: {:?}", record);
let mut new_record = record.clone();
new_record.get_record_mut().update_ttl(now);
out.add_additional_answer_box(new_record);
}
}
let mut multicast_sent_trackers = HashSet::new();
for (intf, sock) in self.intf_socks.iter() {
if let Some(tracker) = multicast_send_tracker(intf) {
if multicast_sent_trackers.contains(&tracker) {
continue; }
multicast_sent_trackers.insert(tracker);
}
send_dns_outgoing(&out, intf, sock);
}
}
fn handle_read(&mut self, intf: &Interface) -> bool {
let sock = match self.intf_socks.get_mut(intf) {
Some(if_sock) => if_sock,
None => return false,
};
let mut buf = vec![0u8; MAX_MSG_ABSOLUTE];
let sz = match sock.read(&mut buf) {
Ok(sz) => sz,
Err(e) => {
if e.kind() != std::io::ErrorKind::WouldBlock {
error!("listening socket read failed: {}", e);
}
return false;
}
};
debug!("received {} bytes from IP: {}", sz, intf.ip());
if sz == 0 {
error!("socket {:?} was likely shutdown", sock);
if let Err(e) = self.poller.delete(&*sock) {
error!("failed to remove sock {:?} from poller: {}", sock, &e);
}
match new_socket_bind(intf) {
Ok(new_sock) => {
debug!("reset socket for IP {}", intf.ip());
self.intf_socks.insert(intf.clone(), new_sock);
}
Err(e) => error!("re-bind a socket to {:?}: {}", intf, e),
}
return false;
}
buf.truncate(sz);
match DnsIncoming::new(buf) {
Ok(msg) => {
if msg.is_query() {
self.handle_query(msg, intf);
} else if msg.is_response() {
self.handle_response(msg);
} else {
error!("Invalid message: not query and not response");
}
}
Err(e) => error!("Invalid incoming DNS message: {}", e),
}
true
}
fn query_unresolved(&mut self, instance: &str) -> bool {
if !valid_instance_name(instance) {
debug!("instance name {} not valid", instance);
return false;
}
if let Some(records) = self.cache.get_srv(instance) {
for record in records {
if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
if self.cache.get_addr(&srv.host).is_none() {
self.send_query_vec(&[(&srv.host, TYPE_A), (&srv.host, TYPE_AAAA)]);
return true;
}
}
}
} else {
self.send_query(instance, TYPE_ANY);
return true;
}
false
}
fn query_cache_for_service(&mut self, ty_domain: &str, sender: Sender<ServiceEvent>) {
let mut resolved: HashSet<String> = HashSet::new();
let mut unresolved: HashSet<String> = HashSet::new();
if let Some(records) = self.cache.get_ptr(ty_domain) {
for record in records.iter() {
if let Some(ptr) = record.any().downcast_ref::<DnsPointer>() {
let info = match self.create_service_info_from_cache(ty_domain, &ptr.alias) {
Ok(ok) => ok,
Err(err) => {
error!("Error while creating service info from cache: {}", err);
continue;
}
};
match sender.send(ServiceEvent::ServiceFound(
ty_domain.to_string(),
ptr.alias.clone(),
)) {
Ok(()) => debug!("send service found {}", &ptr.alias),
Err(e) => {
error!("failed to send service found: {}", e);
continue;
}
}
if info.is_ready() {
resolved.insert(ptr.alias.clone());
match sender.send(ServiceEvent::ServiceResolved(info)) {
Ok(()) => debug!("sent service resolved: {}", &ptr.alias),
Err(e) => error!("failed to send service resolved: {}", e),
}
} else {
unresolved.insert(ptr.alias.clone());
}
}
}
}
for instance in resolved.drain() {
self.pending_resolves.remove(&instance);
}
for instance in unresolved.drain() {
self.add_pending_resolve(instance);
}
}
fn query_cache_for_hostname(
&mut self,
hostname: &str,
sender: Sender<HostnameResolutionEvent>,
) {
let addresses = self.cache.get_addresses_for_host(hostname);
if !addresses.is_empty() {
match sender.send(HostnameResolutionEvent::AddressesFound(
hostname.to_string(),
addresses,
)) {
Ok(()) => debug!("sent hostname addresses found"),
Err(e) => error!("failed to send hostname addresses found: {}", e),
}
}
}
fn add_pending_resolve(&mut self, instance: String) {
if !self.pending_resolves.contains(&instance) {
let next_time = current_time_millis() + RESOLVE_WAIT_IN_MILLIS;
self.add_retransmission(next_time, Command::Resolve(instance.clone(), 1));
self.pending_resolves.insert(instance);
}
}
fn create_service_info_from_cache(
&self,
ty_domain: &str,
fullname: &str,
) -> Result<ServiceInfo> {
let my_name = {
let name = fullname.trim_end_matches(split_sub_domain(ty_domain).0);
name.strip_suffix('.').unwrap_or(name).to_string()
};
let now = current_time_millis();
let mut info = ServiceInfo::new(ty_domain, &my_name, "", (), 0, None)?;
if let Some(subtype) = self.cache.get_subtype(fullname) {
debug!(
"ty_domain: {} found subtype {} for instance: {}",
ty_domain, subtype, fullname
);
if info.get_subtype().is_none() {
info.set_subtype(subtype.clone());
}
}
if let Some(records) = self.cache.get_srv(fullname) {
if let Some(answer) = records.first() {
if let Some(dns_srv) = answer.any().downcast_ref::<DnsSrv>() {
info.set_hostname(dns_srv.host.clone());
info.set_port(dns_srv.port);
}
}
}
if let Some(records) = self.cache.get_txt(fullname) {
if let Some(record) = records.first() {
if let Some(dns_txt) = record.any().downcast_ref::<DnsTxt>() {
info.set_properties_from_txt(&dns_txt.text);
}
}
}
if let Some(records) = self.cache.get_addr(info.get_hostname()) {
for answer in records.iter() {
if let Some(dns_a) = answer.any().downcast_ref::<DnsAddress>() {
if dns_a.get_record().is_expired(now) {
debug!("Addr expired: {}", &dns_a.address);
} else {
info.insert_ipaddr(dns_a.address);
}
}
}
}
Ok(info)
}
fn handle_response(&mut self, mut msg: DnsIncoming) {
debug!(
"handle_response: {} answers {} authorities {} additionals",
&msg.answers.len(),
&msg.num_authorities,
&msg.num_additionals
);
let now = current_time_millis();
msg.answers.retain(|record| {
if !record.get_record().is_expired(now) {
return true;
}
debug!("record is expired, removing it from cache.");
if self.cache.remove(record) {
if let Some(dns_ptr) = record.any().downcast_ref::<DnsPointer>() {
call_service_listener(
&self.service_queriers,
dns_ptr.get_name(),
ServiceEvent::ServiceRemoved(
dns_ptr.get_name().to_string(),
dns_ptr.alias.clone(),
),
);
}
}
false
});
struct InstanceChange {
ty: u16, name: String, }
let mut changes = Vec::new();
let mut timers = Vec::new();
for record in msg.answers {
match self.cache.add_or_update(record, &mut timers) {
Some((dns_record, true)) => {
timers.push(dns_record.get_record().get_expire_time());
timers.push(dns_record.get_record().get_refresh_time());
let ty = dns_record.get_type();
let name = dns_record.get_name();
if ty == TYPE_PTR {
if self.service_queriers.contains_key(name) {
timers.push(dns_record.get_record().get_refresh_time());
}
if let Some(dns_ptr) = dns_record.any().downcast_ref::<DnsPointer>() {
call_service_listener(
&self.service_queriers,
name,
ServiceEvent::ServiceFound(name.to_string(), dns_ptr.alias.clone()),
);
changes.push(InstanceChange {
ty,
name: dns_ptr.alias.clone(),
});
}
} else {
changes.push(InstanceChange {
ty,
name: name.to_string(),
});
}
}
Some((dns_record, false)) => {
timers.push(dns_record.get_record().get_expire_time());
timers.push(dns_record.get_record().get_refresh_time());
}
_ => {}
}
}
for t in timers {
self.add_timer(t);
}
changes
.iter()
.filter(|change| change.ty == TYPE_A || change.ty == TYPE_AAAA)
.map(|change| change.name.clone())
.collect::<HashSet<String>>()
.iter()
.map(|hostname| (hostname, self.cache.get_addresses_for_host(hostname)))
.for_each(|(hostname, addresses)| {
call_hostname_resolution_listener(
&self.hostname_resolvers,
hostname,
HostnameResolutionEvent::AddressesFound(hostname.to_string(), addresses),
)
});
let mut updated_instances = HashSet::new();
for update in changes {
match update.ty {
TYPE_PTR | TYPE_SRV | TYPE_TXT => {
updated_instances.insert(update.name);
}
TYPE_A | TYPE_AAAA => {
let instances = self.cache.get_instances_on_host(&update.name);
updated_instances.extend(instances);
}
_ => {}
}
}
self.resolve_updated_instances(updated_instances);
}
fn resolve_updated_instances(&mut self, updated_instances: HashSet<String>) {
let mut resolved: HashSet<String> = HashSet::new();
let mut unresolved: HashSet<String> = HashSet::new();
for (ty_domain, records) in self.cache.all_ptr().iter() {
if !self.service_queriers.contains_key(ty_domain) {
continue;
}
for record in records.iter() {
if let Some(dns_ptr) = record.any().downcast_ref::<DnsPointer>() {
if updated_instances.contains(&dns_ptr.alias) {
if let Ok(info) =
self.create_service_info_from_cache(ty_domain, &dns_ptr.alias)
{
if info.is_ready() {
resolved.insert(dns_ptr.alias.clone());
call_service_listener(
&self.service_queriers,
ty_domain,
ServiceEvent::ServiceResolved(info),
);
} else {
unresolved.insert(dns_ptr.alias.clone());
}
}
}
}
}
}
for instance in resolved.drain() {
self.pending_resolves.remove(&instance);
}
for instance in unresolved.drain() {
self.add_pending_resolve(instance);
}
}
fn handle_query(&mut self, msg: DnsIncoming, intf: &Interface) {
let sock = match self.intf_socks.get(intf) {
Some(sock) => sock,
None => return,
};
let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE | FLAGS_AA);
const META_QUERY: &str = "_services._dns-sd._udp.local.";
for question in msg.questions.iter() {
debug!("query question: {:?}", &question);
let qtype = question.entry.ty;
if qtype == TYPE_PTR {
for service in self.my_services.values() {
if question.entry.name == service.get_type()
|| service
.get_subtype()
.as_ref()
.map_or(false, |v| v == &question.entry.name)
{
out.add_answer_with_additionals(&msg, service, intf);
} else if question.entry.name == META_QUERY {
let ptr_added = out.add_answer(
&msg,
DnsPointer::new(
&question.entry.name,
TYPE_PTR,
CLASS_IN,
service.get_other_ttl(),
service.get_type().to_string(),
),
);
if !ptr_added {
debug!("answer was not added for meta-query {:?}", &question);
}
}
}
} else {
if qtype == TYPE_A || qtype == TYPE_AAAA || qtype == TYPE_ANY {
for service in self.my_services.values() {
if service.get_hostname().to_lowercase()
== question.entry.name.to_lowercase()
{
let intf_addrs = service.get_addrs_on_intf(intf);
if intf_addrs.is_empty() && (qtype == TYPE_A || qtype == TYPE_AAAA) {
let t = match qtype {
TYPE_A => "TYPE_A",
TYPE_AAAA => "TYPE_AAAA",
_ => "invalid_type",
};
error!(
"Cannot find valid addrs for {} response on intf {:?}",
t, &intf
);
return;
}
for address in intf_addrs {
out.add_answer(
&msg,
DnsAddress::new(
&question.entry.name,
ip_address_to_type(&address),
CLASS_IN | CLASS_CACHE_FLUSH,
service.get_host_ttl(),
address,
),
);
}
}
}
}
let name_to_find = question.entry.name.to_lowercase();
let service = match self.my_services.get(&name_to_find) {
Some(s) => s,
None => continue,
};
if qtype == TYPE_SRV || qtype == TYPE_ANY {
out.add_answer(
&msg,
DnsSrv::new(
&question.entry.name,
CLASS_IN | CLASS_CACHE_FLUSH,
service.get_host_ttl(),
service.get_priority(),
service.get_weight(),
service.get_port(),
service.get_hostname().to_string(),
),
);
}
if qtype == TYPE_TXT || qtype == TYPE_ANY {
out.add_answer(
&msg,
DnsTxt::new(
&question.entry.name,
CLASS_IN | CLASS_CACHE_FLUSH,
service.get_host_ttl(),
service.generate_txt(),
),
);
}
if qtype == TYPE_SRV {
let intf_addrs = service.get_addrs_on_intf(intf);
if intf_addrs.is_empty() {
error!(
"Cannot find valid addrs for TYPE_SRV response on intf {:?}",
&intf
);
return;
}
for address in intf_addrs {
out.add_additional_answer(DnsAddress::new(
service.get_hostname(),
ip_address_to_type(&address),
CLASS_IN | CLASS_CACHE_FLUSH,
service.get_host_ttl(),
address,
));
}
}
}
}
if !out.answers.is_empty() {
out.id = msg.id;
send_dns_outgoing(&out, intf, sock);
self.increase_counter(Counter::Respond, 1);
}
self.increase_counter(Counter::KnownAnswerSuppression, out.known_answer_count);
}
fn increase_counter(&mut self, counter: Counter, count: i64) {
let key = counter.to_string();
match self.counters.get_mut(&key) {
Some(v) => *v += count,
None => {
self.counters.insert(key, count);
}
}
}
fn signal_sock_drain(&self) {
let mut signal_buf = [0; 1024];
while let Ok(sz) = self.signal_sock.recv(&mut signal_buf) {
debug!(
"signal socket recvd: {}",
String::from_utf8_lossy(&signal_buf[0..sz])
);
}
}
fn add_retransmission(&mut self, next_time: u64, command: Command) {
self.retransmissions.push(ReRun { next_time, command });
self.add_timer(next_time);
}
fn notify_service_removal(&self, expired: HashMap<String, HashSet<String>>) {
for (ty_domain, sender) in self.service_queriers.iter() {
if let Some(instances) = expired.get(ty_domain) {
for instance_name in instances {
let event = ServiceEvent::ServiceRemoved(
ty_domain.to_string(),
instance_name.to_string(),
);
match sender.send(event) {
Ok(()) => debug!("Sent ServiceRemoved to listener successfully"),
Err(e) => error!("Failed to send event: {}", e),
}
}
}
}
}
fn exec_command_browse(
&mut self,
repeating: bool,
ty: String,
next_delay: u32,
listener: Sender<ServiceEvent>,
) {
let pretty_addrs: Vec<String> = self
.intf_socks
.keys()
.map(|itf| format!("{} ({})", itf.ip(), itf.name))
.collect();
if let Err(e) = listener.send(ServiceEvent::SearchStarted(format!(
"{} on addrs [{}]",
&ty,
pretty_addrs.join(", ")
))) {
error!(
"Failed to send SearchStarted({})(repeating:{}): {}",
&ty, repeating, e
);
return;
}
if !repeating {
self.add_service_querier(ty.clone(), listener.clone());
self.query_cache_for_service(&ty, listener.clone());
}
self.send_query(&ty, TYPE_PTR);
self.increase_counter(Counter::Browse, 1);
let next_time = current_time_millis() + (next_delay * 1000) as u64;
let max_delay = 60 * 60;
let delay = cmp::min(next_delay * 2, max_delay);
self.add_retransmission(next_time, Command::Browse(ty, delay, listener));
}
fn exec_command_resolve_hostname(
&mut self,
repeating: bool,
hostname: String,
next_delay: u32,
listener: Sender<HostnameResolutionEvent>,
timeout: Option<u64>,
) {
let addr_list: Vec<_> = self.intf_socks.keys().collect();
if let Err(e) = listener.send(HostnameResolutionEvent::SearchStarted(format!(
"{} on addrs {:?}",
&hostname, &addr_list
))) {
error!(
"Failed to send ResolveStarted({})(repeating:{}): {}",
&hostname, repeating, e
);
return;
}
if !repeating {
self.add_hostname_resolver(hostname.to_owned(), listener.clone(), timeout);
self.query_cache_for_hostname(&hostname, listener.clone());
}
self.send_query_vec(&[(&hostname, TYPE_A), (&hostname, TYPE_AAAA)]);
self.increase_counter(Counter::ResolveHostname, 1);
let now = current_time_millis();
let next_time = now + u64::from(next_delay) * 1000;
let max_delay = 60 * 60;
let delay = cmp::min(next_delay * 2, max_delay);
if self
.hostname_resolvers
.get(&hostname)
.and_then(|(_sender, timeout)| *timeout)
.map(|timeout| next_time < timeout)
.unwrap_or(true)
{
self.add_retransmission(
next_time,
Command::ResolveHostname(hostname, delay, listener, None),
);
}
}
fn exec_command_resolve(&mut self, instance: String, try_count: u16) {
let pending_query = self.query_unresolved(&instance);
let max_try = 3;
if pending_query && try_count < max_try {
let next_time = current_time_millis() + RESOLVE_WAIT_IN_MILLIS;
self.add_retransmission(next_time, Command::Resolve(instance, try_count + 1));
}
}
fn exec_command_unregister(
&mut self,
repeating: bool,
fullname: String,
resp_s: Sender<UnregisterStatus>,
) {
let response = match self.my_services.remove_entry(&fullname) {
None => {
error!("unregister: cannot find such service {}", &fullname);
UnregisterStatus::NotFound
}
Some((_k, info)) => {
let mut timers = Vec::new();
let mut multicast_sent_trackers = HashSet::new();
for (intf, sock) in self.intf_socks.iter() {
if let Some(tracker) = multicast_send_tracker(intf) {
if multicast_sent_trackers.contains(&tracker) {
continue; }
multicast_sent_trackers.insert(tracker);
}
let packet = self.unregister_service(&info, intf, sock);
if !repeating && !packet.is_empty() {
let next_time = current_time_millis() + 120;
self.retransmissions.push(ReRun {
next_time,
command: Command::UnregisterResend(packet, intf.clone()),
});
timers.push(next_time);
}
}
for t in timers {
self.add_timer(t);
}
self.increase_counter(Counter::Unregister, 1);
UnregisterStatus::OK
}
};
if let Err(e) = resp_s.send(response) {
error!("unregister: failed to send response: {}", e);
}
}
fn exec_command_unregister_resend(&mut self, packet: Vec<u8>, intf: Interface) {
if let Some(sock) = self.intf_socks.get(&intf) {
error!("UnregisterResend from {}", &intf.ip());
multicast_on_intf(&packet[..], &intf, sock);
self.increase_counter(Counter::UnregisterResend, 1);
}
}
fn exec_command_stop_browse(&mut self, ty_domain: String) {
match self.service_queriers.remove_entry(&ty_domain) {
None => error!("StopBrowse: cannot find querier for {}", &ty_domain),
Some((ty, sender)) => {
debug!("StopBrowse: removed queryer for {}", &ty);
let mut i = 0;
while i < self.retransmissions.len() {
if let Command::Browse(t, _, _) = &self.retransmissions[i].command {
if t == &ty {
self.retransmissions.remove(i);
debug!("StopBrowse: removed retransmission for {}", &ty);
continue;
}
}
i += 1;
}
match sender.send(ServiceEvent::SearchStopped(ty_domain)) {
Ok(()) => debug!("Sent SearchStopped to the listener"),
Err(e) => warn!("Failed to send SearchStopped: {}", e),
}
}
}
}
fn exec_command_stop_resolve_hostname(&mut self, hostname: String) {
if let Some((host, (sender, _timeout))) = self.hostname_resolvers.remove_entry(&hostname) {
debug!("StopResolve: removed queryer for {}", &host);
let mut i = 0;
while i < self.retransmissions.len() {
if let Command::Resolve(t, _) = &self.retransmissions[i].command {
if t == &host {
self.retransmissions.remove(i);
debug!("StopResolve: removed retransmission for {}", &host);
continue;
}
}
i += 1;
}
match sender.send(HostnameResolutionEvent::SearchStopped(hostname)) {
Ok(()) => debug!("Sent SearchStopped to the listener"),
Err(e) => warn!("Failed to send SearchStopped: {}", e),
}
}
}
fn exec_command_register_resend(&mut self, fullname: String) {
match self.my_services.get(&fullname) {
Some(info) => {
let outgoing_addrs = self.send_unsolicited_response(info);
if !outgoing_addrs.is_empty() {
self.notify_monitors(DaemonEvent::Announce(
fullname,
format!("{:?}", &outgoing_addrs),
));
}
self.increase_counter(Counter::RegisterResend, 1);
}
None => debug!("announce: cannot find such service {}", &fullname),
}
}
fn refresh_active_services(&mut self) {
let mut query_ptr_count = 0;
let mut query_srv_count = 0;
let mut new_timers = HashSet::new();
let mut query_addr_count = 0;
for (ty_domain, _sender) in self.service_queriers.iter() {
let refreshed_timers = self.cache.refresh_due_ptr(ty_domain);
if !refreshed_timers.is_empty() {
debug!("sending refresh query for PTR: {}", ty_domain);
self.send_query(ty_domain, TYPE_PTR);
query_ptr_count += 1;
new_timers.extend(refreshed_timers);
}
let (instances, timers) = self.cache.refresh_due_srv(ty_domain);
for instance in instances.iter() {
debug!("sending refresh query for SRV: {}", instance);
self.send_query(instance, TYPE_SRV);
query_srv_count += 1;
}
new_timers.extend(timers);
let (hostnames, timers) = self.cache.refresh_due_hosts(ty_domain);
for hostname in hostnames.iter() {
debug!("sending refresh queries for A and AAAA: {}", hostname);
self.send_query_vec(&[(hostname, TYPE_A), (hostname, TYPE_AAAA)]);
query_addr_count += 2;
}
new_timers.extend(timers);
}
for timer in new_timers {
self.add_timer(timer);
}
self.increase_counter(Counter::CacheRefreshPTR, query_ptr_count);
self.increase_counter(Counter::CacheRefreshSRV, query_srv_count);
self.increase_counter(Counter::CacheRefreshAddr, query_addr_count);
}
}
#[derive(Debug)]
pub enum ServiceEvent {
SearchStarted(String),
ServiceFound(String, String),
ServiceResolved(ServiceInfo),
ServiceRemoved(String, String),
SearchStopped(String),
}
#[derive(Debug)]
#[non_exhaustive]
pub enum HostnameResolutionEvent {
SearchStarted(String),
AddressesFound(String, HashSet<IpAddr>),
AddressesRemoved(String, HashSet<IpAddr>),
SearchTimeout(String),
SearchStopped(String),
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum DaemonEvent {
Announce(String, String),
Error(Error),
IpAdd(IpAddr),
IpDel(IpAddr),
}
#[derive(Debug)]
enum Command {
Browse(String, u32, Sender<ServiceEvent>),
ResolveHostname(String, u32, Sender<HostnameResolutionEvent>, Option<u64>),
Register(ServiceInfo),
Unregister(String, Sender<UnregisterStatus>),
RegisterResend(String),
UnregisterResend(Vec<u8>, Interface),
StopBrowse(String),
StopResolveHostname(String),
Resolve(String, u16),
GetMetrics(Sender<Metrics>),
GetStatus(Sender<DaemonStatus>),
Monitor(Sender<DaemonEvent>),
SetOption(DaemonOption),
Exit(Sender<DaemonStatus>),
}
impl fmt::Display for Command {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Browse(_, _, _) => write!(f, "Command Browse"),
Self::ResolveHostname(_, _, _, _) => write!(f, "Command ResolveHostname"),
Self::Exit(_) => write!(f, "Command Exit"),
Self::GetStatus(_) => write!(f, "Command GetStatus"),
Self::GetMetrics(_) => write!(f, "Command GetMetrics"),
Self::Monitor(_) => write!(f, "Command Monitor"),
Self::Register(_) => write!(f, "Command Register"),
Self::RegisterResend(_) => write!(f, "Command RegisterResend"),
Self::SetOption(_) => write!(f, "Command SetOption"),
Self::StopBrowse(_) => write!(f, "Command StopBrowse"),
Self::StopResolveHostname(_) => write!(f, "Command StopResolveHostname"),
Self::Unregister(_, _) => write!(f, "Command Unregister"),
Self::UnregisterResend(_, _) => write!(f, "Command UnregisterResend"),
Self::Resolve(_, _) => write!(f, "Command Resolve"),
}
}
}
#[derive(Debug)]
enum DaemonOption {
ServiceNameLenMax(u8),
EnableInterface(Vec<IfKind>),
DisableInterface(Vec<IfKind>),
}
const DOMAIN_LEN: usize = "._tcp.local.".len();
fn check_service_name_length(ty_domain: &str, limit: u8) -> Result<()> {
if ty_domain.len() <= DOMAIN_LEN + 1 {
return Err(e_fmt!("Service type name cannot be empty: {}", ty_domain));
}
let service_name_len = ty_domain.len() - DOMAIN_LEN - 1; if service_name_len > limit as usize {
return Err(e_fmt!("Service name length must be <= {} bytes", limit));
}
Ok(())
}
fn check_domain_suffix(name: &str) -> Result<()> {
if !(name.ends_with("._tcp.local.") || name.ends_with("._udp.local.")) {
return Err(e_fmt!(
"mDNS service {} must end with '._tcp.local.' or '._udp.local.'",
name
));
}
Ok(())
}
fn check_service_name(fullname: &str) -> Result<()> {
check_domain_suffix(fullname)?;
let remaining: Vec<&str> = fullname[..fullname.len() - DOMAIN_LEN].split('.').collect();
let name = remaining.last().ok_or_else(|| e_fmt!("No service name"))?;
if &name[0..1] != "_" {
return Err(e_fmt!("Service name must start with '_'"));
}
let name = &name[1..];
if name.contains("--") {
return Err(e_fmt!("Service name must not contain '--'"));
}
if name.starts_with('-') || name.ends_with('-') {
return Err(e_fmt!("Service name (%s) may not start or end with '-'"));
}
let ascii_count = name.chars().filter(|c| c.is_ascii_alphabetic()).count();
if ascii_count < 1 {
return Err(e_fmt!(
"Service name must contain at least one letter (eg: 'A-Za-z')"
));
}
Ok(())
}
fn check_hostname(hostname: &str) -> Result<()> {
if !hostname.ends_with(".local.") {
return Err(e_fmt!("Hostname must end with '.local.'"));
}
if hostname == ".local." {
return Err(e_fmt!(
"The part of the hostname before '.local.' cannot be empty"
));
}
if hostname.len() > 255 {
return Err(e_fmt!("Hostname length must be <= 255 bytes"));
}
Ok(())
}
fn call_service_listener(
listeners_map: &HashMap<String, Sender<ServiceEvent>>,
ty_domain: &str,
event: ServiceEvent,
) {
if let Some(listener) = listeners_map.get(ty_domain) {
match listener.send(event) {
Ok(()) => debug!("Sent event to listener successfully"),
Err(e) => error!("Failed to send event: {}", e),
}
}
}
fn call_hostname_resolution_listener(
listeners_map: &HashMap<String, (Sender<HostnameResolutionEvent>, Option<u64>)>,
hostname: &str,
event: HostnameResolutionEvent,
) {
if let Some(listener) = listeners_map.get(hostname).map(|(l, _)| l) {
match listener.send(event) {
Ok(()) => debug!("Sent event to listener successfully"),
Err(e) => error!("Failed to send event: {}", e),
}
}
}
fn my_ip_interfaces() -> Vec<Interface> {
if_addrs::get_if_addrs()
.unwrap_or_default()
.into_iter()
.filter(|i| !i.is_loopback())
.collect()
}
fn send_dns_outgoing(out: &DnsOutgoing, intf: &Interface, sock: &Socket) -> Vec<Vec<u8>> {
let qtype = if out.is_query() { "query" } else { "response" };
debug!(
"Multicasting {}: {} questions {} answers {} authorities {} additional",
qtype,
out.questions.len(),
out.answers.len(),
out.authorities.len(),
out.additionals.len()
);
let packet_list = out.to_data_on_wire();
for packet in packet_list.iter() {
multicast_on_intf(packet, intf, sock);
}
packet_list
}
fn multicast_on_intf(packet: &[u8], intf: &Interface, socket: &Socket) {
if packet.len() > MAX_MSG_ABSOLUTE {
error!("Drop over-sized packet ({})", packet.len());
return;
}
let addr: SocketAddr = match intf.addr {
if_addrs::IfAddr::V4(_) => SocketAddrV4::new(GROUP_ADDR_V4, MDNS_PORT).into(),
if_addrs::IfAddr::V6(_) => {
let mut sock = SocketAddrV6::new(GROUP_ADDR_V6, MDNS_PORT, 0, 0);
sock.set_scope_id(intf.index.unwrap_or(0)); sock.into()
}
};
send_packet(packet, addr, intf, socket);
}
fn send_packet(packet: &[u8], addr: SocketAddr, intf: &Interface, sock: &Socket) {
let sockaddr = SockAddr::from(addr);
match sock.send_to(packet, &sockaddr) {
Ok(sz) => debug!("sent out {} bytes on interface {:?}", sz, intf),
Err(e) => error!("Failed to send to {} via {:?}: {}", addr, &intf, e),
}
}
fn valid_instance_name(name: &str) -> bool {
name.split('.').count() >= 5
}
#[cfg(test)]
mod tests {
use super::{
check_domain_suffix, check_service_name_length, my_ip_interfaces, new_socket_bind,
send_dns_outgoing, valid_instance_name, HostnameResolutionEvent, ServiceDaemon,
ServiceEvent, ServiceInfo, GROUP_ADDR_V4, MDNS_PORT,
};
use crate::{
dns_parser::{DnsOutgoing, DnsPointer, CLASS_IN, FLAGS_AA, FLAGS_QR_RESPONSE, TYPE_PTR},
service_daemon::check_hostname,
};
use std::{
net::{SocketAddr, SocketAddrV4},
time::Duration,
};
use test_log::test;
#[test]
fn test_socketaddr_print() {
let addr: SocketAddr = SocketAddrV4::new(GROUP_ADDR_V4, MDNS_PORT).into();
let print = format!("{}", addr);
assert_eq!(print, "224.0.0.251:5353");
}
#[test]
fn test_instance_name() {
assert!(valid_instance_name("my-laser._printer._tcp.local."));
assert!(valid_instance_name("my-laser.._printer._tcp.local."));
assert!(!valid_instance_name("_printer._tcp.local."));
}
#[test]
fn test_check_service_name_length() {
let result = check_service_name_length("_tcp", 100);
assert!(result.is_err());
if let Err(e) = result {
println!("{}", e);
}
}
#[test]
fn test_check_hostname() {
for hostname in &[
"my_host.local.",
&("A".repeat(255 - ".local.".len()) + ".local."),
] {
let result = check_hostname(hostname);
assert!(result.is_ok());
}
for hostname in &[
"my_host.local",
".local.",
&("A".repeat(256 - ".local.".len()) + ".local."),
] {
let result = check_hostname(hostname);
assert!(result.is_err());
if let Err(e) = result {
println!("{}", e);
}
}
}
#[test]
fn test_check_domain_suffix() {
assert!(check_domain_suffix("_missing_dot._tcp.local").is_err());
assert!(check_domain_suffix("_missing_bar.tcp.local.").is_err());
assert!(check_domain_suffix("_mis_spell._tpp.local.").is_err());
assert!(check_domain_suffix("_mis_spell._upp.local.").is_err());
assert!(check_domain_suffix("_has_dot._tcp.local.").is_ok());
assert!(check_domain_suffix("_goodname._udp.local.").is_ok());
}
#[test]
fn service_with_temporarily_invalidated_ptr() {
let d = ServiceDaemon::new().expect("Failed to create daemon");
let service = "_test_inval_ptr._udp.local.";
let host_name = "my_host_tmp_invalidated_ptr.local.";
let intfs: Vec<_> = my_ip_interfaces();
let intf_ips: Vec<_> = intfs.iter().map(|intf| intf.ip()).collect();
let port = 5201;
let my_service =
ServiceInfo::new(service, "my_instance", host_name, &intf_ips[..], port, None)
.expect("invalid service info")
.enable_addr_auto();
let result = d.register(my_service.clone());
assert!(result.is_ok());
let browse_chan = d.browse(service).unwrap();
let timeout = Duration::from_secs(2);
let mut resolved = false;
while let Ok(event) = browse_chan.recv_timeout(timeout) {
match event {
ServiceEvent::ServiceResolved(info) => {
resolved = true;
println!("Resolved a service of {}", &info.get_fullname());
break;
}
e => {
println!("Received event {:?}", e);
}
}
}
assert!(resolved);
println!("Stopping browse of {}", service);
d.stop_browse(service).unwrap();
let mut stopped = false;
while let Ok(event) = browse_chan.recv_timeout(timeout) {
match event {
ServiceEvent::SearchStopped(_) => {
stopped = true;
println!("Stopped browsing service");
break;
}
e => {
println!("Received event {:?}", e);
}
}
}
assert!(stopped);
let invalidate_ptr_packet = DnsPointer::new(
my_service.get_type(),
TYPE_PTR,
CLASS_IN,
0,
my_service.get_fullname().to_string(),
);
let mut packet_buffer = DnsOutgoing::new(FLAGS_QR_RESPONSE | FLAGS_AA);
packet_buffer.add_additional_answer(invalidate_ptr_packet);
for intf in intfs {
let sock = new_socket_bind(&intf).unwrap();
send_dns_outgoing(&packet_buffer, &intf, &sock);
}
println!(
"Sent PTR record invalidation. Starting second browse for {}",
service
);
let browse_chan = d.browse(service).unwrap();
resolved = false;
while let Ok(event) = browse_chan.recv_timeout(timeout) {
match event {
ServiceEvent::ServiceResolved(info) => {
resolved = true;
println!("Resolved a service of {}", &info.get_fullname());
break;
}
e => {
println!("Received event {:?}", e);
}
}
}
assert!(resolved);
d.shutdown().unwrap();
}
#[test]
fn test_expired_srv() {
let service_type = "_expired-srv._udp.local.";
let instance = "test_instance";
let host_name = "expired_srv_host.local.";
let mut my_service = ServiceInfo::new(service_type, instance, host_name, "", 5023, None)
.unwrap()
.enable_addr_auto();
let new_ttl = 2; my_service._set_host_ttl(new_ttl);
let mdns_server = ServiceDaemon::new().expect("Failed to create mdns server");
let result = mdns_server.register(my_service);
assert!(result.is_ok());
let mdns_client = ServiceDaemon::new().expect("Failed to create mdns client");
let browse_chan = mdns_client.browse(service_type).unwrap();
let timeout = Duration::from_secs(1);
let mut resolved = false;
while let Ok(event) = browse_chan.recv_timeout(timeout) {
match event {
ServiceEvent::ServiceResolved(info) => {
resolved = true;
println!("Resolved a service of {}", &info.get_fullname());
break;
}
_ => {}
}
}
assert!(resolved);
mdns_server.shutdown().unwrap();
let expire_timeout = Duration::from_secs(new_ttl as u64);
while let Ok(event) = browse_chan.recv_timeout(expire_timeout) {
match event {
ServiceEvent::ServiceRemoved(service_type, full_name) => {
println!("Service removed: {}: {}", &service_type, &full_name);
break;
}
_ => {}
}
}
}
#[test]
fn test_hostname_resolution_address_removed() {
let server = ServiceDaemon::new().expect("Failed to create server");
let hostname = "addr_remove_host._tcp.local.";
let service_ip_addr = my_ip_interfaces()
.iter()
.find(|iface| iface.ip().is_ipv4())
.map(|iface| iface.ip())
.unwrap();
let mut my_service = ServiceInfo::new(
"_host_res_test._tcp.local.",
"my_instance",
hostname,
&service_ip_addr,
1234,
None,
)
.expect("invalid service info");
let addr_ttl = 2;
my_service._set_host_ttl(addr_ttl);
server.register(my_service).unwrap();
let client = ServiceDaemon::new().expect("Failed to create client");
let event_receiver = client.resolve_hostname(hostname, None).unwrap();
let resolved = loop {
match event_receiver.recv() {
Ok(HostnameResolutionEvent::AddressesFound(found_hostname, addresses)) => {
assert!(found_hostname == hostname);
assert!(addresses.contains(&service_ip_addr));
println!("address found: {:?}", &addresses);
break true;
}
Ok(HostnameResolutionEvent::SearchStopped(_)) => break false,
Ok(_event) => {}
Err(_) => break false,
}
};
assert!(resolved);
server.shutdown().unwrap();
let timeout = Duration::from_secs(addr_ttl as u64);
let removed = loop {
match event_receiver.recv_timeout(timeout) {
Ok(HostnameResolutionEvent::AddressesRemoved(removed_host, addresses)) => {
assert!(removed_host == hostname);
assert!(addresses.contains(&service_ip_addr));
println!(
"address removed: hostname: {} addresses: {:?}",
&hostname, &addresses
);
break true;
}
Ok(_event) => {}
Err(_) => {
break false;
}
}
};
assert!(removed);
client.shutdown().unwrap();
}
#[test]
fn test_refresh_ptr() {
let service_type = "_refresh-ptr._udp.local.";
let instance = "test_instance";
let host_name = "refresh_ptr_host.local.";
let service_ip_addr = my_ip_interfaces()
.iter()
.find(|iface| iface.ip().is_ipv4())
.map(|iface| iface.ip())
.unwrap();
let mut my_service = ServiceInfo::new(
service_type,
instance,
host_name,
&service_ip_addr,
5023,
None,
)
.unwrap();
let new_ttl = 2; my_service._set_other_ttl(new_ttl);
let mdns_server = ServiceDaemon::new().expect("Failed to create mdns server");
let result = mdns_server.register(my_service);
assert!(result.is_ok());
let mdns_client = ServiceDaemon::new().expect("Failed to create mdns client");
let browse_chan = mdns_client.browse(service_type).unwrap();
let timeout = Duration::from_secs(1);
let mut resolved = false;
while let Ok(event) = browse_chan.recv_timeout(timeout) {
match event {
ServiceEvent::ServiceResolved(info) => {
resolved = true;
println!("Resolved a service of {}", &info.get_fullname());
break;
}
_ => {}
}
}
assert!(resolved);
let timeout = Duration::from_millis(1800);
while let Ok(event) = browse_chan.recv_timeout(timeout) {
println!("event: {:?}", &event);
}
let metrics_chan = mdns_client.get_metrics().unwrap();
let metrics = metrics_chan.recv_timeout(timeout).unwrap();
let refresh_counter = metrics["cache-refresh-ptr"];
assert_eq!(refresh_counter, 1);
mdns_server.shutdown().unwrap();
mdns_client.shutdown().unwrap();
}
}