From 9b3a09ae1be57adc2c95b1a0bb744e7caa3bdbbe Mon Sep 17 00:00:00 2001 From: pixsperdavid Date: Wed, 3 Apr 2024 16:20:40 +0100 Subject: [PATCH 1/3] Implementation of unicast response to direct unicast and legacy unicast queries --- Cargo.toml | 1 + README.md | 3 - src/dns_parser.rs | 7 ++ src/service_daemon.rs | 157 ++++++++++++++++++++++++++++++++++-------- 4 files changed, 137 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a4fab9b..9bad0e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ if-addrs = { version = "0.10", features = ["link-local"] } # get local IP addres log = { version = "0.4", optional = true } # logging polling = "2.1" # select/poll sockets socket2 = { version = "0.5.5", features = ["all"] } # socket APIs +socket-pktinfo = { version = "0.2.1" } # socket packet info extension [dev-dependencies] fastrand = "1.8" diff --git a/README.md b/README.md index 86b411b..c7bdf4b 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,6 @@ This implementation is based on the following RFCs: This is still beta software. We focus on the common use cases at hand. And we tested with some existing common tools (e.g. `Avahi` on Linux, `dns-sd` on MacOS, and `Bonjour` library on iOS) to verify the basic compatibility. -Currently this library has the following limitations: -- Only support multicast, no unicast send/recv. - ## License Licensed under either of diff --git a/src/dns_parser.rs b/src/dns_parser.rs index a8db442..1bd3add 100644 --- a/src/dns_parser.rs +++ b/src/dns_parser.rs @@ -48,6 +48,7 @@ pub(crate) const MAX_MSG_ABSOLUTE: usize = 8972; pub(crate) const FLAGS_QR_MASK: u16 = 0x8000; // mask for query/response bit pub(crate) const FLAGS_QR_QUERY: u16 = 0x0000; pub(crate) const FLAGS_QR_RESPONSE: u16 = 0x8000; +pub(crate) const FLAGS_UNICAST_RESPONSE_MASK: u16 = 0x8000; // mask for question unicast response bit pub(crate) const FLAGS_AA: u16 = 0x0400; // mask for Authoritative answer bit pub(crate) type DnsRecordBox = Box; @@ -77,6 +78,12 @@ pub(crate) struct DnsQuestion { pub(crate) entry: DnsEntry, } +impl DnsQuestion { + pub fn is_unicast_response_requested(&self) -> bool { + self.entry.class & FLAGS_UNICAST_RESPONSE_MASK == FLAGS_UNICAST_RESPONSE_MASK + } +} + /// A DNS Resource Record - like a DNS entry, but has a TTL. /// RFC: https://www.rfc-editor.org/rfc/rfc1035#section-3.2.1 /// https://www.rfc-editor.org/rfc/rfc1035#section-4.1.3 diff --git a/src/service_daemon.rs b/src/service_daemon.rs index 9d93933..b2f548c 100644 --- a/src/service_daemon.rs +++ b/src/service_daemon.rs @@ -42,14 +42,15 @@ use crate::{ Receiver, }; use flume::{bounded, Sender, TrySendError}; -use if_addrs::Interface; +use if_addrs::{IfAddr, Interface}; use polling::Poller; -use socket2::{SockAddr, Socket}; +use socket2::SockAddr; +use socket_pktinfo::PktInfoUdpSocket; +use std::cmp::min; use std::{ cmp, collections::{HashMap, HashSet}, fmt, - io::Read, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, UdpSocket}, str, thread, time::Duration, @@ -75,6 +76,8 @@ const LOOPBACK_V4: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); const RESOLVE_WAIT_IN_MILLIS: u64 = 500; +const LEGACY_UNICAST_RR_TTL: u32 = 10; + /// Response status code for the service `unregister` call. #[derive(Debug)] pub enum UnregisterStatus { @@ -105,6 +108,8 @@ enum Counter { UnregisterResend, Browse, Respond, + RespondUnicast, + RespondLegacyUnicast, CacheRefreshQuery, } @@ -117,6 +122,8 @@ impl fmt::Display for Counter { Counter::UnregisterResend => write!(f, "unregister-resend"), Counter::Browse => write!(f, "browse"), Counter::Respond => write!(f, "respond"), + Counter::RespondUnicast => write!(f, "respond-unicast"), + Counter::RespondLegacyUnicast => write!(f, "respond-legacy-unicast"), Counter::CacheRefreshQuery => write!(f, "cache-refresh"), } } @@ -691,7 +698,7 @@ impl ServiceDaemon { } /// Creates a new UDP socket that uses `intf` to send and recv multicast. -fn new_socket_bind(intf: &Interface) -> Result { +fn new_socket_bind(intf: &Interface) -> Result { // Use the same socket for receiving and sending multicast packets. // Such socket has to bind to INADDR_ANY or IN6ADDR_ANY. let intf_ip = &intf.ip(); @@ -738,31 +745,30 @@ fn new_socket_bind(intf: &Interface) -> Result { /// Creates a new UDP socket to bind to `port` with REUSEPORT option. /// `non_block` indicates whether to set O_NONBLOCK for the socket. -fn new_socket(addr: SocketAddr, non_block: bool) -> Result { +fn new_socket(addr: SocketAddr, non_block: bool) -> Result { 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))?; + let sock = PktInfoUdpSocket::new(domain).map_err(|e| e_fmt!("create socket failed: {}", e))?; - fd.set_reuse_address(true) + sock.set_reuse_address(true) .map_err(|e| e_fmt!("set ReuseAddr failed: {}", e))?; #[cfg(unix)] // this is currently restricted to Unix's in socket2 - fd.set_reuse_port(true) + sock.set_reuse_port(true) .map_err(|e| e_fmt!("set ReusePort failed: {}", e))?; if non_block { - fd.set_nonblocking(true) + sock.set_nonblocking(true) .map_err(|e| e_fmt!("set O_NONBLOCK: {}", e))?; } - fd.bind(&addr.into()) + sock.bind(&addr.into()) .map_err(|e| e_fmt!("socket bind to {} failed: {}", &addr, e))?; debug!("new socket bind to {}", &addr); - Ok(fd) + Ok(sock) } /// Specify a UNIX timestamp in millis to run `command` for the next time. @@ -777,7 +783,7 @@ struct ReRun { #[derive(Debug)] struct IntfSock { intf: Interface, - sock: Socket, + sock: PktInfoUdpSocket, } /// Specify kinds of interfaces. It is used to enable or to disable interfaces in the daemon. @@ -882,7 +888,7 @@ struct Zeroconf { /// Next poll id value poll_id_count: usize, - /// Local registered services, keyed by service full names. + /// Local registered services,Keyed by service full names. my_services: HashMap, cache: DnsCache, @@ -1128,7 +1134,7 @@ impl Zeroconf { fn add_new_interface(&mut self, intf: Interface) { // Bind the new interface. - let new_ip = intf.ip(); + let new_ip = intf.addr.ip(); let sock = match new_socket_bind(&intf) { Ok(s) => s, Err(e) => { @@ -1389,6 +1395,7 @@ impl Zeroconf { Some(if_sock) => if_sock, None => return false, }; + let mut buf = vec![0u8; MAX_MSG_ABSOLUTE]; // Read the next mDNS UDP datagram. @@ -1397,8 +1404,8 @@ impl Zeroconf { // be truncated by the socket layer depending on the platform's libc. // In any case, such large datagram will not be decoded properly and // this function should return false but should not crash. - let sz = match intf_sock.sock.read(&mut buf) { - Ok(sz) => sz, + let (sz, pktinfo) = match intf_sock.sock.recv(&mut buf) { + Ok(r) => r, Err(e) => { if e.kind() != std::io::ErrorKind::WouldBlock { error!("listening socket read failed: {}", e); @@ -1430,12 +1437,48 @@ impl Zeroconf { buf.truncate(sz); // reduce potential processing errors + let is_unicast = !pktinfo.addr_dst.is_multicast(); + + // Ignore unicast packets outside the local link + if is_unicast { + let should_respond = match (pktinfo.addr_src.ip(), &intf_sock.intf.addr) { + (IpAddr::V4(src_ip), IfAddr::V4(intf)) => { + if src_ip.is_loopback() { + true + } else { + let src_ip: u32 = src_ip.into(); + let intf_ip: u32 = intf.ip.into(); + let intf_netmask: u32 = intf.netmask.into(); + // Is src_ip in local subnet? + (intf_ip & intf_netmask) == (src_ip & intf_netmask) + } + } + (IpAddr::V6(src_ip), &IfAddr::V6(_)) => { + if src_ip.is_loopback() { + true + } else { + // Does src_ip have on-link prefix? + src_ip.segments()[0] & 0xffc0 == 0xfe80 + } + } + // Interface and source message IP versions do not match + _ => false, + }; + if !should_respond { + return true; + } + }; + match DnsIncoming::new(buf) { Ok(msg) => { if msg.is_query() { - self.handle_query(msg, ip); + self.handle_query(msg, ip, pktinfo.addr_src, is_unicast); } else if msg.is_response() { - self.handle_response(msg); + if !is_unicast { + self.handle_response(msg); + } else { + error!("Invalid message: unrequested unicast response"); + } } else { error!("Invalid message: not query and not response"); } @@ -1724,12 +1767,24 @@ impl Zeroconf { } } - fn handle_query(&mut self, msg: DnsIncoming, ip: &IpAddr) { + fn handle_query( + &mut self, + msg: DnsIncoming, + ip: &IpAddr, + src_addr: SocketAddr, + is_unicast_query: bool, + ) { let intf_sock = match self.intf_socks.get(ip) { Some(sock) => sock, None => return, }; let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE | FLAGS_AA); + let is_legacy_unicast = is_unicast_query && src_addr.port() != MDNS_PORT; + let is_unicast_reply = is_unicast_query + && msg + .questions + .iter() + .all(|q| q.is_unicast_response_requested()); // Special meta-query "_services._dns-sd._udp.". // See https://datatracker.ietf.org/doc/html/rfc6763#section-9 @@ -1755,7 +1810,11 @@ impl Zeroconf { &question.entry.name, TYPE_PTR, CLASS_IN, - service.get_other_ttl(), + if is_legacy_unicast { + min(LEGACY_UNICAST_RR_TTL, service.get_host_ttl()) + } else { + service.get_other_ttl() + }, service.get_type().to_string(), )), ); @@ -1792,7 +1851,11 @@ impl Zeroconf { &question.entry.name, t, CLASS_IN | CLASS_UNIQUE, - service.get_host_ttl(), + if is_legacy_unicast { + min(LEGACY_UNICAST_RR_TTL, service.get_host_ttl()) + } else { + service.get_host_ttl() + }, address, )), ); @@ -1813,7 +1876,11 @@ impl Zeroconf { Box::new(DnsSrv::new( &question.entry.name, CLASS_IN | CLASS_UNIQUE, - service.get_host_ttl(), + if is_legacy_unicast { + min(LEGACY_UNICAST_RR_TTL, service.get_host_ttl()) + } else { + service.get_host_ttl() + }, service.get_priority(), service.get_weight(), service.get_port(), @@ -1829,7 +1896,11 @@ impl Zeroconf { &question.entry.name, TYPE_TXT, CLASS_IN | CLASS_UNIQUE, - service.get_host_ttl(), + if is_legacy_unicast { + min(LEGACY_UNICAST_RR_TTL, service.get_host_ttl()) + } else { + service.get_host_ttl() + }, service.generate_txt(), )), ); @@ -1853,7 +1924,11 @@ impl Zeroconf { service.get_hostname(), t, CLASS_IN | CLASS_UNIQUE, - service.get_host_ttl(), + if is_legacy_unicast { + min(LEGACY_UNICAST_RR_TTL, service.get_host_ttl()) + } else { + service.get_host_ttl() + }, address, ))); } @@ -1863,9 +1938,18 @@ impl Zeroconf { if !out.answers.is_empty() { out.id = msg.id; - broadcast_dns_on_intf(&out, intf_sock); - - self.increase_counter(Counter::Respond, 1); + if is_unicast_reply { + if is_legacy_unicast { + unicast_dns_on_intf(&out, src_addr.ip(), intf_sock); + self.increase_counter(Counter::RespondLegacyUnicast, 1); + } else { + unicast_dns_on_intf(&out, src_addr.ip(), intf_sock); + self.increase_counter(Counter::RespondUnicast, 1); + } + } else { + broadcast_dns_on_intf(&out, intf_sock); + self.increase_counter(Counter::Respond, 1); + } } } @@ -2282,6 +2366,23 @@ fn broadcast_on_intf<'a>(packet: &'a [u8], intf: &IntfSock) -> &'a [u8] { packet } +/// Send an outgoing unicast DNS query or response, and returns the packet bytes. +fn unicast_dns_on_intf(out: &DnsOutgoing, ip_addr: IpAddr, intf: &IntfSock) -> Vec { + let qtype = if out.is_query() { "query" } else { "response" }; + debug!( + "Unicasting ({}) {}: {} questions {} answers {} authorities {} additional", + ip_addr, + qtype, + out.questions.len(), + out.answers.len(), + out.authorities.len(), + out.additionals.len() + ); + let packet = out.to_packet_data(); + send_packet(&packet[..], SocketAddr::new(ip_addr, MDNS_PORT), intf); + packet +} + /// Sends out `packet` to `addr` on the socket in `intf_sock`. fn send_packet(packet: &[u8], addr: SocketAddr, intf_sock: &IntfSock) { let sockaddr = SockAddr::from(addr); From eadef80376d7845c8481e98d20739714d5f3f62c Mon Sep 17 00:00:00 2001 From: pixsperdavid Date: Thu, 4 Apr 2024 13:56:36 +0100 Subject: [PATCH 2/3] Resolve issues from draft PR review --- src/dns_parser.rs | 28 +++++++++------ src/service_daemon.rs | 80 ++++++++++++++++++++++--------------------- 2 files changed, 58 insertions(+), 50 deletions(-) diff --git a/src/dns_parser.rs b/src/dns_parser.rs index 1bd3add..249a9db 100644 --- a/src/dns_parser.rs +++ b/src/dns_parser.rs @@ -31,7 +31,8 @@ pub(crate) const TYPE_ANY: u16 = 255; pub(crate) const CLASS_IN: u16 = 1; pub(crate) const CLASS_MASK: u16 = 0x7FFF; -pub(crate) const CLASS_UNIQUE: u16 = 0x8000; +pub(crate) const CLASS_CACHE_FLUSH: u16 = 0x8000; +pub(crate) const CLASS_UNICAST_RESPONSE: u16 = 0x8000; /// Max size of UDP datagram payload: 9000 bytes - IP header 20 bytes - UDP header 8 bytes. /// Reference: RFC6762: https://datatracker.ietf.org/doc/html/rfc6762#section-17 @@ -48,7 +49,6 @@ pub(crate) const MAX_MSG_ABSOLUTE: usize = 8972; pub(crate) const FLAGS_QR_MASK: u16 = 0x8000; // mask for query/response bit pub(crate) const FLAGS_QR_QUERY: u16 = 0x0000; pub(crate) const FLAGS_QR_RESPONSE: u16 = 0x8000; -pub(crate) const FLAGS_UNICAST_RESPONSE_MASK: u16 = 0x8000; // mask for question unicast response bit pub(crate) const FLAGS_AA: u16 = 0x0400; // mask for Authoritative answer bit pub(crate) type DnsRecordBox = Box; @@ -67,7 +67,7 @@ impl DnsEntry { name, ty, class: class & CLASS_MASK, - unique: (class & CLASS_UNIQUE) != 0, + unique: (class & CLASS_CACHE_FLUSH) != 0, } } } @@ -80,7 +80,7 @@ pub(crate) struct DnsQuestion { impl DnsQuestion { pub fn is_unicast_response_requested(&self) -> bool { - self.entry.class & FLAGS_UNICAST_RESPONSE_MASK == FLAGS_UNICAST_RESPONSE_MASK + self.entry.class & CLASS_UNICAST_RESPONSE == CLASS_UNICAST_RESPONSE } } @@ -555,7 +555,7 @@ impl DnsOutPacket { self.write_short(record.entry.ty); if record.entry.unique { // check "multicast" - self.write_short(record.entry.class | CLASS_UNIQUE); + self.write_short(record.entry.class | CLASS_CACHE_FLUSH); } else { self.write_short(record.entry.class); } @@ -832,7 +832,7 @@ impl DnsOutgoing { // https://tools.ietf.org/html/rfc6763#section-12.1. self.add_additional_answer(Box::new(DnsSrv::new( service.get_fullname(), - CLASS_IN | CLASS_UNIQUE, + CLASS_IN | CLASS_CACHE_FLUSH, service.get_host_ttl(), service.get_priority(), service.get_weight(), @@ -843,7 +843,7 @@ impl DnsOutgoing { self.add_additional_answer(Box::new(DnsTxt::new( service.get_fullname(), TYPE_TXT, - CLASS_IN | CLASS_UNIQUE, + CLASS_IN | CLASS_CACHE_FLUSH, service.get_host_ttl(), service.generate_txt(), ))); @@ -857,7 +857,7 @@ impl DnsOutgoing { self.add_additional_answer(Box::new(DnsAddress::new( service.get_hostname(), t, - CLASS_IN | CLASS_UNIQUE, + CLASS_IN | CLASS_CACHE_FLUSH, service.get_host_ttl(), address, ))); @@ -1338,7 +1338,7 @@ mod tests { use crate::dns_parser::{TYPE_A, TYPE_AAAA}; use super::{ - DnsIncoming, DnsNSec, DnsOutgoing, DnsSrv, CLASS_IN, CLASS_UNIQUE, FLAGS_QR_QUERY, + DnsIncoming, DnsNSec, DnsOutgoing, DnsSrv, CLASS_CACHE_FLUSH, CLASS_IN, FLAGS_QR_QUERY, FLAGS_QR_RESPONSE, TYPE_PTR, }; @@ -1386,7 +1386,7 @@ mod tests { let mut response = DnsOutgoing::new(FLAGS_QR_RESPONSE); response.add_additional_answer(Box::new(DnsSrv::new( name, - CLASS_IN | CLASS_UNIQUE, + CLASS_IN | CLASS_CACHE_FLUSH, 1, 1, 1, @@ -1414,7 +1414,13 @@ mod tests { let name = "instance1._nsec_test._udp.local."; let next_domain = name.to_string(); let type_bitmap = vec![64, 0, 0, 8]; // Two bits set to '1': bit 1 and bit 28. - let nsec = DnsNSec::new(name, CLASS_IN | CLASS_UNIQUE, 1, next_domain, type_bitmap); + let nsec = DnsNSec::new( + name, + CLASS_IN | CLASS_CACHE_FLUSH, + 1, + next_domain, + type_bitmap, + ); let absent_types = nsec._types(); assert_eq!(absent_types.len(), 2); assert_eq!(absent_types[0], TYPE_A); diff --git a/src/service_daemon.rs b/src/service_daemon.rs index b2f548c..2bdf129 100644 --- a/src/service_daemon.rs +++ b/src/service_daemon.rs @@ -33,7 +33,7 @@ use crate::log::{debug, error, warn}; use crate::{ dns_parser::{ current_time_millis, DnsAddress, DnsIncoming, DnsOutgoing, DnsPointer, DnsRecordBox, - DnsRecordExt, DnsSrv, DnsTxt, CLASS_IN, CLASS_UNIQUE, FLAGS_AA, FLAGS_QR_QUERY, + 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_NSEC, TYPE_PTR, TYPE_SRV, TYPE_TXT, }, @@ -76,7 +76,8 @@ const LOOPBACK_V4: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); const RESOLVE_WAIT_IN_MILLIS: u64 = 500; -const LEGACY_UNICAST_RR_TTL: u32 = 10; +/// Maximum TTL value allowed for resource records in legacy unicast responses +const LEGACY_UNICAST_RR_MAX_TTL: u32 = 10; /// Response status code for the service `unregister` call. #[derive(Debug)] @@ -1247,7 +1248,7 @@ impl Zeroconf { out.add_answer_at_time( Box::new(DnsSrv::new( info.get_fullname(), - CLASS_IN | CLASS_UNIQUE, + CLASS_IN | CLASS_CACHE_FLUSH, info.get_host_ttl(), info.get_priority(), info.get_weight(), @@ -1260,7 +1261,7 @@ impl Zeroconf { Box::new(DnsTxt::new( info.get_fullname(), TYPE_TXT, - CLASS_IN | CLASS_UNIQUE, + CLASS_IN | CLASS_CACHE_FLUSH, info.get_other_ttl(), info.generate_txt(), )), @@ -1281,7 +1282,7 @@ impl Zeroconf { Box::new(DnsAddress::new( info.get_hostname(), t, - CLASS_IN | CLASS_UNIQUE, + CLASS_IN | CLASS_CACHE_FLUSH, info.get_host_ttl(), addr, )), @@ -1323,7 +1324,7 @@ impl Zeroconf { out.add_answer_at_time( Box::new(DnsSrv::new( info.get_fullname(), - CLASS_IN | CLASS_UNIQUE, + CLASS_IN | CLASS_CACHE_FLUSH, 0, info.get_priority(), info.get_weight(), @@ -1336,7 +1337,7 @@ impl Zeroconf { Box::new(DnsTxt::new( info.get_fullname(), TYPE_TXT, - CLASS_IN | CLASS_UNIQUE, + CLASS_IN | CLASS_CACHE_FLUSH, 0, info.generate_txt(), )), @@ -1352,7 +1353,7 @@ impl Zeroconf { Box::new(DnsAddress::new( info.get_hostname(), t, - CLASS_IN | CLASS_UNIQUE, + CLASS_IN | CLASS_CACHE_FLUSH, 0, addr, )), @@ -1779,7 +1780,11 @@ impl Zeroconf { None => return, }; let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE | FLAGS_AA); + + // Legacy unicast query responses require special handling + // See https://datatracker.ietf.org/doc/html/rfc6762#section-6.7 let is_legacy_unicast = is_unicast_query && src_addr.port() != MDNS_PORT; + let is_unicast_reply = is_unicast_query && msg .questions @@ -1796,6 +1801,12 @@ impl Zeroconf { if qtype == TYPE_PTR { for service in self.my_services.values() { + let ttl = if is_legacy_unicast { + min(LEGACY_UNICAST_RR_MAX_TTL, service.get_other_ttl()) + } else { + service.get_other_ttl() + }; + if question.entry.name == service.get_type() || service .get_subtype() @@ -1810,11 +1821,7 @@ impl Zeroconf { &question.entry.name, TYPE_PTR, CLASS_IN, - if is_legacy_unicast { - min(LEGACY_UNICAST_RR_TTL, service.get_host_ttl()) - } else { - service.get_other_ttl() - }, + ttl, service.get_type().to_string(), )), ); @@ -1826,6 +1833,12 @@ impl Zeroconf { } else { if qtype == TYPE_A || qtype == TYPE_AAAA || qtype == TYPE_ANY { for service in self.my_services.values() { + let ttl = if is_legacy_unicast { + min(LEGACY_UNICAST_RR_MAX_TTL, service.get_host_ttl()) + } else { + service.get_host_ttl() + }; + if service.get_hostname() == question.entry.name.to_lowercase() { let intf_addrs = service.get_addrs_on_intf(&intf_sock.intf); if intf_addrs.is_empty() && (qtype == TYPE_A || qtype == TYPE_AAAA) { @@ -1850,12 +1863,8 @@ impl Zeroconf { Box::new(DnsAddress::new( &question.entry.name, t, - CLASS_IN | CLASS_UNIQUE, - if is_legacy_unicast { - min(LEGACY_UNICAST_RR_TTL, service.get_host_ttl()) - } else { - service.get_host_ttl() - }, + CLASS_IN | CLASS_CACHE_FLUSH, + ttl, address, )), ); @@ -1870,17 +1879,19 @@ impl Zeroconf { None => continue, }; + let ttl = match (qtype, is_legacy_unicast) { + (TYPE_TXT, true) => min(LEGACY_UNICAST_RR_MAX_TTL, service.get_other_ttl()), + (_, true) => min(LEGACY_UNICAST_RR_MAX_TTL, service.get_host_ttl()), + (_, false) => service.get_host_ttl(), + }; + if qtype == TYPE_SRV || qtype == TYPE_ANY { out.add_answer( &msg, Box::new(DnsSrv::new( &question.entry.name, - CLASS_IN | CLASS_UNIQUE, - if is_legacy_unicast { - min(LEGACY_UNICAST_RR_TTL, service.get_host_ttl()) - } else { - service.get_host_ttl() - }, + CLASS_IN | CLASS_CACHE_FLUSH, + ttl, service.get_priority(), service.get_weight(), service.get_port(), @@ -1895,12 +1906,8 @@ impl Zeroconf { Box::new(DnsTxt::new( &question.entry.name, TYPE_TXT, - CLASS_IN | CLASS_UNIQUE, - if is_legacy_unicast { - min(LEGACY_UNICAST_RR_TTL, service.get_host_ttl()) - } else { - service.get_host_ttl() - }, + CLASS_IN | CLASS_CACHE_FLUSH, + ttl, service.generate_txt(), )), ); @@ -1923,12 +1930,8 @@ impl Zeroconf { out.add_additional_answer(Box::new(DnsAddress::new( service.get_hostname(), t, - CLASS_IN | CLASS_UNIQUE, - if is_legacy_unicast { - min(LEGACY_UNICAST_RR_TTL, service.get_host_ttl()) - } else { - service.get_host_ttl() - }, + CLASS_IN | CLASS_CACHE_FLUSH, + ttl, address, ))); } @@ -1939,11 +1942,10 @@ impl Zeroconf { if !out.answers.is_empty() { out.id = msg.id; if is_unicast_reply { + unicast_dns_on_intf(&out, src_addr.ip(), intf_sock); if is_legacy_unicast { - unicast_dns_on_intf(&out, src_addr.ip(), intf_sock); self.increase_counter(Counter::RespondLegacyUnicast, 1); } else { - unicast_dns_on_intf(&out, src_addr.ip(), intf_sock); self.increase_counter(Counter::RespondUnicast, 1); } } else { From 8aae44ee4bd005daf973ca92f7295eea8bc6a522 Mon Sep 17 00:00:00 2001 From: pixsperdavid Date: Mon, 22 Apr 2024 11:44:30 +0200 Subject: [PATCH 3/3] Fix fmt error --- src/service_daemon.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/service_daemon.rs b/src/service_daemon.rs index cd49fb5..17ca3e2 100644 --- a/src/service_daemon.rs +++ b/src/service_daemon.rs @@ -2010,7 +2010,6 @@ impl Zeroconf { } } - /// Handle incoming query packets, figure out whether and what to respond. fn handle_query( &mut self,