1#[cfg(feature = "logging")]
4use crate::log::{debug, trace};
5use crate::{
6 dns_parser::{DnsRecordBox, DnsRecordExt, DnsSrv, RRType, ScopedIp},
7 Error, IfKind, InterfaceId, Result,
8};
9use if_addrs::{IfAddr, Interface};
10use std::net::Ipv6Addr;
11use std::{
12 cmp,
13 collections::{HashMap, HashSet},
14 convert::TryInto,
15 fmt,
16 net::{IpAddr, Ipv4Addr},
17 str::FromStr,
18};
19
20const DNS_HOST_TTL: u32 = 120; const DNS_OTHER_TTL: u32 = 4500; #[derive(Debug)]
26pub(crate) struct MyIntf {
27 pub(crate) name: String,
29
30 pub(crate) index: u32,
32
33 pub(crate) addrs: HashSet<IfAddr>,
35}
36
37impl MyIntf {
38 pub(crate) fn next_ifaddr_v4(&self) -> Option<&IfAddr> {
39 self.addrs.iter().find(|a| a.ip().is_ipv4())
40 }
41
42 pub(crate) fn next_ifaddr_v6(&self) -> Option<&IfAddr> {
43 self.addrs.iter().find(|a| a.ip().is_ipv6())
44 }
45}
46
47impl From<&MyIntf> for InterfaceId {
48 fn from(my_intf: &MyIntf) -> Self {
49 InterfaceId {
50 name: my_intf.name.clone(),
51 index: my_intf.index,
52 }
53 }
54}
55
56#[derive(Debug, Clone)]
61pub struct ServiceInfo {
62 ty_domain: String, sub_domain: Option<String>, fullname: String, server: String, addresses: HashSet<IpAddr>,
71 port: u16,
72 host_ttl: u32, other_ttl: u32, priority: u16,
75 weight: u16,
76 txt_properties: TxtProperties,
77 addr_auto: bool, status: HashMap<u32, ServiceStatus>, requires_probe: bool,
83
84 supported_intfs: Vec<IfKind>,
86
87 is_link_local_only: bool,
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub(crate) enum ServiceStatus {
93 Probing,
94 Announced,
95 Unknown,
96}
97
98impl ServiceInfo {
99 pub fn new<Ip: AsIpAddrs, P: IntoTxtProperties>(
134 ty_domain: &str,
135 my_name: &str,
136 host_name: &str,
137 ip: Ip,
138 port: u16,
139 properties: P,
140 ) -> Result<Self> {
141 let (ty_domain, sub_domain) = split_sub_domain(ty_domain);
142
143 let fullname = format!("{my_name}.{ty_domain}");
144 let ty_domain = ty_domain.to_string();
145 let sub_domain = sub_domain.map(str::to_string);
146 let server = normalize_hostname(host_name.to_string());
147 let addresses = ip.as_ip_addrs()?;
148 let txt_properties = properties.into_txt_properties();
149
150 for prop in txt_properties.iter() {
154 let key = prop.key();
155 if !key.is_ascii() {
156 return Err(Error::Msg(format!(
157 "TXT property key {} is not ASCII",
158 prop.key()
159 )));
160 }
161 if key.contains('=') {
162 return Err(Error::Msg(format!(
163 "TXT property key {} contains '='",
164 prop.key()
165 )));
166 }
167 }
168
169 let this = Self {
170 ty_domain,
171 sub_domain,
172 fullname,
173 server,
174 addresses,
175 port,
176 host_ttl: DNS_HOST_TTL,
177 other_ttl: DNS_OTHER_TTL,
178 priority: 0,
179 weight: 0,
180 txt_properties,
181 addr_auto: false,
182 status: HashMap::new(),
183 requires_probe: true,
184 is_link_local_only: false,
185 supported_intfs: vec![IfKind::All],
186 };
187
188 Ok(this)
189 }
190
191 pub const fn enable_addr_auto(mut self) -> Self {
195 self.addr_auto = true;
196 self
197 }
198
199 pub const fn is_addr_auto(&self) -> bool {
202 self.addr_auto
203 }
204
205 pub fn set_requires_probe(&mut self, enable: bool) {
210 self.requires_probe = enable;
211 }
212
213 pub fn set_link_local_only(&mut self, is_link_local_only: bool) {
217 self.is_link_local_only = is_link_local_only;
218 }
219
220 pub fn set_interfaces(&mut self, intfs: Vec<IfKind>) {
225 self.supported_intfs = intfs;
226 }
227
228 pub const fn requires_probe(&self) -> bool {
232 self.requires_probe
233 }
234
235 #[inline]
239 pub fn get_type(&self) -> &str {
240 &self.ty_domain
241 }
242
243 #[inline]
248 pub const fn get_subtype(&self) -> &Option<String> {
249 &self.sub_domain
250 }
251
252 #[inline]
256 pub fn get_fullname(&self) -> &str {
257 &self.fullname
258 }
259
260 #[inline]
262 pub const fn get_properties(&self) -> &TxtProperties {
263 &self.txt_properties
264 }
265
266 pub fn get_property(&self, key: &str) -> Option<&TxtProperty> {
271 self.txt_properties.get(key)
272 }
273
274 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
279 self.txt_properties.get_property_val(key)
280 }
281
282 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
287 self.txt_properties.get_property_val_str(key)
288 }
289
290 #[inline]
292 pub fn get_hostname(&self) -> &str {
293 &self.server
294 }
295
296 #[inline]
298 pub const fn get_port(&self) -> u16 {
299 self.port
300 }
301
302 #[inline]
304 pub const fn get_addresses(&self) -> &HashSet<IpAddr> {
305 &self.addresses
306 }
307
308 pub fn get_addresses_v4(&self) -> HashSet<&Ipv4Addr> {
310 let mut ipv4_addresses = HashSet::new();
311
312 for ip in &self.addresses {
313 if let IpAddr::V4(ipv4) = ip {
314 ipv4_addresses.insert(ipv4);
315 }
316 }
317
318 ipv4_addresses
319 }
320
321 #[inline]
323 pub const fn get_host_ttl(&self) -> u32 {
324 self.host_ttl
325 }
326
327 #[inline]
329 pub const fn get_other_ttl(&self) -> u32 {
330 self.other_ttl
331 }
332
333 #[inline]
335 pub const fn get_priority(&self) -> u16 {
336 self.priority
337 }
338
339 #[inline]
341 pub const fn get_weight(&self) -> u16 {
342 self.weight
343 }
344
345 pub(crate) fn get_addrs_on_my_intf_v4(&self, my_intf: &MyIntf) -> Vec<IpAddr> {
347 self.addresses
348 .iter()
349 .filter(|a| a.is_ipv4() && my_intf.addrs.iter().any(|x| valid_ip_on_intf(a, x)))
350 .copied()
351 .collect()
352 }
353
354 pub(crate) fn get_addrs_on_my_intf_v6(&self, my_intf: &MyIntf) -> Vec<IpAddr> {
355 self.addresses
356 .iter()
357 .filter(|a| a.is_ipv6() && my_intf.addrs.iter().any(|x| valid_ip_on_intf(a, x)))
358 .copied()
359 .collect()
360 }
361
362 pub(crate) fn _is_ready(&self) -> bool {
364 let some_missing = self.ty_domain.is_empty()
365 || self.fullname.is_empty()
366 || self.server.is_empty()
367 || self.addresses.is_empty();
368 !some_missing
369 }
370
371 pub(crate) fn insert_ipaddr(&mut self, intf: &Interface) {
373 if self.is_address_supported(intf) {
374 self.addresses.insert(intf.addr.ip());
375 } else {
376 trace!(
377 "skipping unsupported address {} for service {}",
378 intf.addr.ip(),
379 self.fullname
380 );
381 }
382 }
383
384 pub(crate) fn remove_ipaddr(&mut self, addr: &IpAddr) {
385 self.addresses.remove(addr);
386 }
387
388 pub(crate) fn generate_txt(&self) -> Vec<u8> {
389 encode_txt(self.get_properties().iter())
390 }
391
392 pub(crate) fn _set_port(&mut self, port: u16) {
393 self.port = port;
394 }
395
396 pub(crate) fn _set_hostname(&mut self, hostname: String) {
397 self.server = normalize_hostname(hostname);
398 }
399
400 pub(crate) fn _set_properties_from_txt(&mut self, txt: &[u8]) -> bool {
402 let properties = decode_txt_unique(txt);
403 if self.txt_properties.properties != properties {
404 self.txt_properties = TxtProperties { properties };
405 true
406 } else {
407 false
408 }
409 }
410
411 pub(crate) fn _set_subtype(&mut self, subtype: String) {
412 self.sub_domain = Some(subtype);
413 }
414
415 pub(crate) fn _set_host_ttl(&mut self, ttl: u32) {
418 self.host_ttl = ttl;
419 }
420
421 pub(crate) fn _set_other_ttl(&mut self, ttl: u32) {
423 self.other_ttl = ttl;
424 }
425
426 pub(crate) fn set_status(&mut self, if_index: u32, status: ServiceStatus) {
427 match self.status.get_mut(&if_index) {
428 Some(service_status) => {
429 *service_status = status;
430 }
431 None => {
432 self.status.entry(if_index).or_insert(status);
433 }
434 }
435 }
436
437 pub(crate) fn get_status(&self, intf: u32) -> ServiceStatus {
438 self.status
439 .get(&intf)
440 .cloned()
441 .unwrap_or(ServiceStatus::Unknown)
442 }
443
444 pub fn as_resolved_service(self) -> ResolvedService {
446 let addresses: HashSet<ScopedIp> = self.addresses.into_iter().map(|a| a.into()).collect();
447 ResolvedService {
448 ty_domain: self.ty_domain,
449 sub_ty_domain: self.sub_domain,
450 fullname: self.fullname,
451 host: self.server,
452 port: self.port,
453 addresses,
454 txt_properties: self.txt_properties,
455 }
456 }
457
458 fn is_address_supported(&self, intf: &Interface) -> bool {
459 let addr = intf.ip();
460 let interface_supported = self.supported_intfs.iter().any(|i| match i {
461 IfKind::Name(name) => *name == intf.name,
462 IfKind::IPv4 => addr.is_ipv4(),
463 IfKind::IPv6 => addr.is_ipv6(),
464 IfKind::Addr(a) => *a == addr,
465 IfKind::LoopbackV4 => matches!(addr, IpAddr::V4(ipv4) if ipv4.is_loopback()),
466 IfKind::LoopbackV6 => matches!(addr, IpAddr::V6(ipv6) if ipv6.is_loopback()),
467 IfKind::All => true,
468 });
469
470 let passes_link_local = !self.is_link_local_only
471 || match &addr {
472 IpAddr::V4(ipv4) => ipv4.is_link_local(),
473 IpAddr::V6(ipv6) => is_unicast_link_local(ipv6),
474 };
475 debug!(
476 "matching inserted address {} on intf {}: passes_link_local={}, interface_supported={}",
477 addr, addr, passes_link_local, interface_supported
478 );
479 interface_supported && passes_link_local
480 }
481}
482
483fn normalize_hostname(mut hostname: String) -> String {
485 if hostname.ends_with(".local.local.") {
486 let new_len = hostname.len() - "local.".len();
487 hostname.truncate(new_len);
488 }
489 hostname
490}
491
492pub trait AsIpAddrs {
494 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>>;
495}
496
497impl<T: AsIpAddrs> AsIpAddrs for &T {
498 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
499 (*self).as_ip_addrs()
500 }
501}
502
503impl AsIpAddrs for &str {
508 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
509 let mut addrs = HashSet::new();
510
511 if !self.is_empty() {
512 let iter = self.split(',').map(str::trim).map(IpAddr::from_str);
513 for addr in iter {
514 let addr = addr.map_err(|err| Error::ParseIpAddr(err.to_string()))?;
515 addrs.insert(addr);
516 }
517 }
518
519 Ok(addrs)
520 }
521}
522
523impl AsIpAddrs for String {
524 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
525 self.as_str().as_ip_addrs()
526 }
527}
528
529impl<I: AsIpAddrs> AsIpAddrs for &[I] {
531 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
532 let mut addrs = HashSet::new();
533
534 for result in self.iter().map(I::as_ip_addrs) {
535 addrs.extend(result?);
536 }
537
538 Ok(addrs)
539 }
540}
541
542impl AsIpAddrs for () {
545 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
546 Ok(HashSet::new())
547 }
548}
549
550impl AsIpAddrs for std::net::IpAddr {
551 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
552 let mut ips = HashSet::new();
553 ips.insert(*self);
554
555 Ok(ips)
556 }
557}
558
559impl AsIpAddrs for Box<dyn AsIpAddrs> {
560 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
561 self.as_ref().as_ip_addrs()
562 }
563}
564
565#[derive(Debug, Clone, PartialEq, Eq)]
573pub struct TxtProperties {
574 properties: Vec<TxtProperty>,
576}
577
578impl Default for TxtProperties {
579 fn default() -> Self {
580 TxtProperties::new()
581 }
582}
583
584impl TxtProperties {
585 pub fn new() -> Self {
586 TxtProperties {
587 properties: Vec::new(),
588 }
589 }
590
591 pub fn iter(&self) -> impl Iterator<Item = &TxtProperty> {
593 self.properties.iter()
594 }
595
596 pub fn len(&self) -> usize {
598 self.properties.len()
599 }
600
601 pub fn is_empty(&self) -> bool {
603 self.properties.is_empty()
604 }
605
606 pub fn get(&self, key: &str) -> Option<&TxtProperty> {
609 let key = key.to_lowercase();
610 self.properties
611 .iter()
612 .find(|&prop| prop.key.to_lowercase() == key)
613 }
614
615 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
621 self.get(key).map(|x| x.val())
622 }
623
624 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
630 self.get(key).map(|x| x.val_str())
631 }
632
633 pub fn into_property_map_str(self) -> HashMap<String, String> {
638 self.properties
639 .into_iter()
640 .filter_map(|property| {
641 let val_string = property.val.map_or(Some(String::new()), |val| {
642 String::from_utf8(val)
643 .map_err(|e| {
644 debug!("Property value contains invalid UTF-8: {e}");
645 })
646 .ok()
647 })?;
648 Some((property.key, val_string))
649 })
650 .collect()
651 }
652}
653
654impl fmt::Display for TxtProperties {
655 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
656 let delimiter = ", ";
657 let props: Vec<String> = self.properties.iter().map(|p| p.to_string()).collect();
658 write!(f, "({})", props.join(delimiter))
659 }
660}
661
662impl From<&[u8]> for TxtProperties {
663 fn from(txt: &[u8]) -> Self {
664 let properties = decode_txt_unique(txt);
665 TxtProperties { properties }
666 }
667}
668
669#[derive(Clone, PartialEq, Eq)]
671pub struct TxtProperty {
672 key: String,
674
675 val: Option<Vec<u8>>,
679}
680
681impl TxtProperty {
682 pub fn key(&self) -> &str {
684 &self.key
685 }
686
687 pub fn val(&self) -> Option<&[u8]> {
691 self.val.as_deref()
692 }
693
694 pub fn val_str(&self) -> &str {
696 self.val
697 .as_ref()
698 .map_or("", |v| std::str::from_utf8(&v[..]).unwrap_or_default())
699 }
700}
701
702impl<K, V> From<&(K, V)> for TxtProperty
704where
705 K: ToString,
706 V: ToString,
707{
708 fn from(prop: &(K, V)) -> Self {
709 Self {
710 key: prop.0.to_string(),
711 val: Some(prop.1.to_string().into_bytes()),
712 }
713 }
714}
715
716impl<K, V> From<(K, V)> for TxtProperty
717where
718 K: ToString,
719 V: AsRef<[u8]>,
720{
721 fn from(prop: (K, V)) -> Self {
722 Self {
723 key: prop.0.to_string(),
724 val: Some(prop.1.as_ref().into()),
725 }
726 }
727}
728
729impl From<&str> for TxtProperty {
731 fn from(key: &str) -> Self {
732 Self {
733 key: key.to_string(),
734 val: None,
735 }
736 }
737}
738
739impl fmt::Display for TxtProperty {
740 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
741 write!(f, "{}={}", self.key, self.val_str())
742 }
743}
744
745impl fmt::Debug for TxtProperty {
749 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
750 let val_string = self.val.as_ref().map_or_else(
751 || "None".to_string(),
752 |v| {
753 std::str::from_utf8(&v[..]).map_or_else(
754 |_| format!("Some({})", u8_slice_to_hex(&v[..])),
755 |s| format!("Some(\"{s}\")"),
756 )
757 },
758 );
759
760 write!(
761 f,
762 "TxtProperty {{key: \"{}\", val: {}}}",
763 &self.key, &val_string,
764 )
765 }
766}
767
768const HEX_TABLE: [char; 16] = [
769 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
770];
771
772fn u8_slice_to_hex(slice: &[u8]) -> String {
776 let mut hex = String::with_capacity(slice.len() * 2 + 2);
777 hex.push_str("0x");
778 for b in slice {
779 hex.push(HEX_TABLE[(b >> 4) as usize]);
780 hex.push(HEX_TABLE[(b & 0x0F) as usize]);
781 }
782 hex
783}
784
785pub trait IntoTxtProperties {
787 fn into_txt_properties(self) -> TxtProperties;
788}
789
790impl IntoTxtProperties for HashMap<String, String> {
791 fn into_txt_properties(mut self) -> TxtProperties {
792 let properties = self
793 .drain()
794 .map(|(key, val)| TxtProperty {
795 key,
796 val: Some(val.into_bytes()),
797 })
798 .collect();
799 TxtProperties { properties }
800 }
801}
802
803impl IntoTxtProperties for Option<HashMap<String, String>> {
805 fn into_txt_properties(self) -> TxtProperties {
806 self.map_or_else(
807 || TxtProperties {
808 properties: Vec::new(),
809 },
810 |h| h.into_txt_properties(),
811 )
812 }
813}
814
815impl<'a, T: 'a> IntoTxtProperties for &'a [T]
817where
818 TxtProperty: From<&'a T>,
819{
820 fn into_txt_properties(self) -> TxtProperties {
821 let mut properties = Vec::new();
822 let mut keys = HashSet::new();
823 for t in self.iter() {
824 let prop = TxtProperty::from(t);
825 let key = prop.key.to_lowercase();
826 if keys.insert(key) {
827 properties.push(prop);
835 }
836 }
837 TxtProperties { properties }
838 }
839}
840
841impl IntoTxtProperties for Vec<TxtProperty> {
842 fn into_txt_properties(self) -> TxtProperties {
843 TxtProperties { properties: self }
844 }
845}
846
847fn encode_txt<'a>(properties: impl Iterator<Item = &'a TxtProperty>) -> Vec<u8> {
849 let mut bytes = Vec::new();
850 for prop in properties {
851 let mut s = prop.key.clone().into_bytes();
852 if let Some(v) = &prop.val {
853 s.extend(b"=");
854 s.extend(v);
855 }
856
857 let sz: u8 = s.len().try_into().unwrap_or_else(|_| {
859 debug!("Property {} is too long, truncating to 255 bytes", prop.key);
860 s.resize(u8::MAX as usize, 0);
861 u8::MAX
862 });
863
864 bytes.push(sz);
867 bytes.extend(s);
868 }
869 if bytes.is_empty() {
870 bytes.push(0);
871 }
872 bytes
873}
874
875pub(crate) fn decode_txt(txt: &[u8]) -> Vec<TxtProperty> {
877 let mut properties = Vec::new();
878 let mut offset = 0;
879 while offset < txt.len() {
880 let length = txt[offset] as usize;
881 if length == 0 {
882 break; }
884 offset += 1; let offset_end = offset + length;
887 if offset_end > txt.len() {
888 debug!("DNS TXT record contains invalid data: Size given for property would be out of range. (offset={}, length={}, offset_end={}, record length={})", offset, length, offset_end, txt.len());
889 break; }
891 let kv_bytes = &txt[offset..offset_end];
892
893 let (k, v) = kv_bytes.iter().position(|&x| x == b'=').map_or_else(
895 || (kv_bytes.to_vec(), None),
896 |idx| (kv_bytes[..idx].to_vec(), Some(kv_bytes[idx + 1..].to_vec())),
897 );
898
899 match String::from_utf8(k) {
901 Ok(k_string) => {
902 properties.push(TxtProperty {
903 key: k_string,
904 val: v,
905 });
906 }
907 Err(e) => debug!("failed to convert to String from key: {}", e),
908 }
909
910 offset += length;
911 }
912
913 properties
914}
915
916fn decode_txt_unique(txt: &[u8]) -> Vec<TxtProperty> {
917 let mut properties = decode_txt(txt);
918
919 let mut keys = HashSet::new();
922 properties.retain(|p| {
923 let key = p.key().to_lowercase();
924 keys.insert(key) });
926 properties
927}
928
929pub(crate) fn valid_ip_on_intf(addr: &IpAddr, if_addr: &IfAddr) -> bool {
931 match (addr, if_addr) {
932 (IpAddr::V4(addr), IfAddr::V4(if_v4)) => {
933 let netmask = u32::from(if_v4.netmask);
934 let intf_net = u32::from(if_v4.ip) & netmask;
935 let addr_net = u32::from(*addr) & netmask;
936 addr_net == intf_net
937 }
938 (IpAddr::V6(addr), IfAddr::V6(if_v6)) => {
939 let netmask = u128::from(if_v6.netmask);
940 let intf_net = u128::from(if_v6.ip) & netmask;
941 let addr_net = u128::from(*addr) & netmask;
942 addr_net == intf_net
943 }
944 _ => false,
945 }
946}
947
948#[derive(Debug)]
950pub(crate) struct Probe {
951 pub(crate) records: Vec<DnsRecordBox>,
953
954 pub(crate) waiting_services: HashSet<String>,
957
958 pub(crate) start_time: u64,
960
961 pub(crate) next_send: u64,
963}
964
965impl Probe {
966 pub(crate) fn new(start_time: u64) -> Self {
967 let next_send = start_time;
974
975 Self {
976 records: Vec::new(),
977 waiting_services: HashSet::new(),
978 start_time,
979 next_send,
980 }
981 }
982
983 pub(crate) fn insert_record(&mut self, record: DnsRecordBox) {
985 let insert_position = self
995 .records
996 .binary_search_by(
997 |existing| match existing.get_class().cmp(&record.get_class()) {
998 std::cmp::Ordering::Equal => existing.get_type().cmp(&record.get_type()),
999 other => other,
1000 },
1001 )
1002 .unwrap_or_else(|pos| pos);
1003
1004 self.records.insert(insert_position, record);
1005 }
1006
1007 pub(crate) fn tiebreaking(&mut self, incoming: &[&DnsRecordBox], now: u64, probe_name: &str) {
1009 let min_len = self.records.len().min(incoming.len());
1019
1020 let mut cmp_result = cmp::Ordering::Equal;
1022 for (i, incoming_record) in incoming.iter().enumerate().take(min_len) {
1023 match self.records[i].compare(incoming_record.as_ref()) {
1024 cmp::Ordering::Equal => continue,
1025 other => {
1026 cmp_result = other;
1027 break; }
1029 }
1030 }
1031
1032 if cmp_result == cmp::Ordering::Equal {
1033 cmp_result = self.records.len().cmp(&incoming.len());
1035 }
1036
1037 match cmp_result {
1038 cmp::Ordering::Less => {
1039 debug!("tiebreaking '{probe_name}': LOST, will wait for one second",);
1040 self.start_time = now + 1000; self.next_send = now + 1000;
1042 }
1043 ordering => {
1044 debug!("tiebreaking '{probe_name}': {:?}", ordering);
1045 }
1046 }
1047 }
1048
1049 pub(crate) fn update_next_send(&mut self, now: u64) {
1050 self.next_send = now + 250;
1051 }
1052
1053 pub(crate) fn expired(&self, now: u64) -> bool {
1055 now >= self.start_time + 750
1058 }
1059}
1060
1061pub(crate) struct DnsRegistry {
1063 pub(crate) probing: HashMap<String, Probe>,
1074
1075 pub(crate) active: HashMap<String, Vec<DnsRecordBox>>,
1077
1078 pub(crate) new_timers: Vec<u64>,
1080
1081 pub(crate) name_changes: HashMap<String, String>,
1083}
1084
1085impl DnsRegistry {
1086 pub(crate) fn new() -> Self {
1087 Self {
1088 probing: HashMap::new(),
1089 active: HashMap::new(),
1090 new_timers: Vec::new(),
1091 name_changes: HashMap::new(),
1092 }
1093 }
1094
1095 pub(crate) fn is_probing_done<T>(
1096 &mut self,
1097 answer: &T,
1098 service_name: &str,
1099 start_time: u64,
1100 ) -> bool
1101 where
1102 T: DnsRecordExt + Send + 'static,
1103 {
1104 if let Some(active_records) = self.active.get(answer.get_name()) {
1105 for record in active_records.iter() {
1106 if answer.matches(record.as_ref()) {
1107 debug!(
1108 "found active record {} {}",
1109 answer.get_type(),
1110 answer.get_name(),
1111 );
1112 return true;
1113 }
1114 }
1115 }
1116
1117 let probe = self
1118 .probing
1119 .entry(answer.get_name().to_string())
1120 .or_insert_with(|| {
1121 debug!("new probe of {}", answer.get_name());
1122 Probe::new(start_time)
1123 });
1124
1125 self.new_timers.push(probe.next_send);
1126
1127 for record in probe.records.iter() {
1128 if answer.matches(record.as_ref()) {
1129 debug!(
1130 "found existing record {} in probe of '{}'",
1131 answer.get_type(),
1132 answer.get_name(),
1133 );
1134 probe.waiting_services.insert(service_name.to_string());
1135 return false; }
1137 }
1138
1139 debug!(
1140 "insert record {} into probe of {}",
1141 answer.get_type(),
1142 answer.get_name(),
1143 );
1144 probe.insert_record(answer.clone_box());
1145 probe.waiting_services.insert(service_name.to_string());
1146
1147 false
1148 }
1149
1150 pub(crate) fn update_hostname(
1154 &mut self,
1155 original: &str,
1156 new_name: &str,
1157 probe_time: u64,
1158 ) -> bool {
1159 let mut found_records = Vec::new();
1160 let mut new_timer_added = false;
1161
1162 for (_name, probe) in self.probing.iter_mut() {
1163 probe.records.retain(|record| {
1164 if record.get_type() == RRType::SRV {
1165 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1166 if srv.host() == original {
1167 let mut new_record = srv.clone();
1168 new_record.set_host(new_name.to_string());
1169 found_records.push(new_record);
1170 return false;
1171 }
1172 }
1173 }
1174 true
1175 });
1176 }
1177
1178 for (_name, records) in self.active.iter_mut() {
1179 records.retain(|record| {
1180 if record.get_type() == RRType::SRV {
1181 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1182 if srv.host() == original {
1183 let mut new_record = srv.clone();
1184 new_record.set_host(new_name.to_string());
1185 found_records.push(new_record);
1186 return false;
1187 }
1188 }
1189 }
1190 true
1191 });
1192 }
1193
1194 for record in found_records {
1195 let probe = match self.probing.get_mut(record.get_name()) {
1196 Some(p) => {
1197 p.start_time = probe_time; p
1199 }
1200 None => {
1201 let new_probe = self
1202 .probing
1203 .entry(record.get_name().to_string())
1204 .or_insert_with(|| Probe::new(probe_time));
1205 new_timer_added = true;
1206 new_probe
1207 }
1208 };
1209
1210 debug!(
1211 "insert record {} with new hostname {new_name} into probe for: {}",
1212 record.get_type(),
1213 record.get_name()
1214 );
1215 probe.insert_record(record.boxed());
1216 }
1217
1218 new_timer_added
1219 }
1220}
1221
1222pub(crate) fn split_sub_domain(domain: &str) -> (&str, Option<&str>) {
1224 if let Some((_, ty_domain)) = domain.rsplit_once("._sub.") {
1225 (ty_domain, Some(domain))
1226 } else {
1227 (domain, None)
1228 }
1229}
1230
1231fn is_unicast_link_local(addr: &Ipv6Addr) -> bool {
1237 (addr.segments()[0] & 0xffc0) == 0xfe80
1238}
1239
1240#[derive(Clone, Debug)]
1243#[non_exhaustive]
1244pub struct ResolvedService {
1245 pub ty_domain: String,
1247
1248 pub sub_ty_domain: Option<String>,
1254
1255 pub fullname: String,
1257
1258 pub host: String,
1260
1261 pub port: u16,
1263
1264 pub addresses: HashSet<ScopedIp>,
1266
1267 pub txt_properties: TxtProperties,
1269}
1270
1271impl ResolvedService {
1272 pub fn is_valid(&self) -> bool {
1274 let some_missing = self.ty_domain.is_empty()
1275 || self.fullname.is_empty()
1276 || self.host.is_empty()
1277 || self.addresses.is_empty();
1278 !some_missing
1279 }
1280
1281 #[inline]
1282 pub const fn get_subtype(&self) -> &Option<String> {
1283 &self.sub_ty_domain
1284 }
1285
1286 #[inline]
1287 pub fn get_fullname(&self) -> &str {
1288 &self.fullname
1289 }
1290
1291 #[inline]
1292 pub fn get_hostname(&self) -> &str {
1293 &self.host
1294 }
1295
1296 #[inline]
1297 pub fn get_port(&self) -> u16 {
1298 self.port
1299 }
1300
1301 #[inline]
1302 pub fn get_addresses(&self) -> &HashSet<ScopedIp> {
1303 &self.addresses
1304 }
1305
1306 pub fn get_addresses_v4(&self) -> HashSet<Ipv4Addr> {
1307 self.addresses
1308 .iter()
1309 .filter_map(|ip| match ip {
1310 ScopedIp::V4(ipv4) => Some(*ipv4.addr()),
1311 _ => None,
1312 })
1313 .collect()
1314 }
1315
1316 #[inline]
1317 pub fn get_properties(&self) -> &TxtProperties {
1318 &self.txt_properties
1319 }
1320
1321 #[inline]
1322 pub fn get_property(&self, key: &str) -> Option<&TxtProperty> {
1323 self.txt_properties.get(key)
1324 }
1325
1326 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
1327 self.txt_properties.get_property_val(key)
1328 }
1329
1330 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
1331 self.txt_properties.get_property_val_str(key)
1332 }
1333}
1334
1335#[cfg(test)]
1336mod tests {
1337 use super::{decode_txt, encode_txt, u8_slice_to_hex, ServiceInfo, TxtProperty};
1338 use crate::IfKind;
1339 use if_addrs::{IfAddr, IfOperStatus, Ifv4Addr, Ifv6Addr, Interface};
1340 use std::net::{Ipv4Addr, Ipv6Addr};
1341
1342 #[test]
1343 fn test_txt_encode_decode() {
1344 let properties = [
1345 TxtProperty::from(&("key1", "value1")),
1346 TxtProperty::from(&("key2", "value2")),
1347 ];
1348
1349 let property_count = properties.len();
1351 let encoded = encode_txt(properties.iter());
1352 assert_eq!(
1353 encoded.len(),
1354 "key1=value1".len() + "key2=value2".len() + property_count
1355 );
1356 assert_eq!(encoded[0] as usize, "key1=value1".len());
1357
1358 let decoded = decode_txt(&encoded);
1360 assert!(properties[..] == decoded[..]);
1361
1362 let properties = vec![TxtProperty::from(&("key3", ""))];
1364 let property_count = properties.len();
1365 let encoded = encode_txt(properties.iter());
1366 assert_eq!(encoded.len(), "key3=".len() + property_count);
1367
1368 let decoded = decode_txt(&encoded);
1369 assert_eq!(properties, decoded);
1370
1371 let binary_val: Vec<u8> = vec![123, 234, 0];
1373 let binary_len = binary_val.len();
1374 let properties = vec![TxtProperty::from(("key4", binary_val))];
1375 let property_count = properties.len();
1376 let encoded = encode_txt(properties.iter());
1377 assert_eq!(encoded.len(), "key4=".len() + binary_len + property_count);
1378
1379 let decoded = decode_txt(&encoded);
1380 assert_eq!(properties, decoded);
1381
1382 let properties = vec![TxtProperty::from(("key5", "val=5"))];
1384 let property_count = properties.len();
1385 let encoded = encode_txt(properties.iter());
1386 assert_eq!(
1387 encoded.len(),
1388 "key5=".len() + "val=5".len() + property_count
1389 );
1390
1391 let decoded = decode_txt(&encoded);
1392 assert_eq!(properties, decoded);
1393
1394 let properties = vec![TxtProperty::from("key6")];
1396 let property_count = properties.len();
1397 let encoded = encode_txt(properties.iter());
1398 assert_eq!(encoded.len(), "key6".len() + property_count);
1399 let decoded = decode_txt(&encoded);
1400 assert_eq!(properties, decoded);
1401
1402 let properties = [TxtProperty::from(
1404 String::from_utf8(vec![0x30; 1024]).unwrap().as_str(), )];
1406 let property_count = properties.len();
1407 let encoded = encode_txt(properties.iter());
1408 assert_eq!(encoded.len(), 255 + property_count);
1409 let decoded = decode_txt(&encoded);
1410 assert_eq!(
1411 vec![TxtProperty::from(
1412 String::from_utf8(vec![0x30; 255]).unwrap().as_str()
1413 )],
1414 decoded
1415 );
1416 }
1417
1418 #[test]
1419 fn test_set_properties_from_txt() {
1420 let properties = [
1422 TxtProperty::from(&("one", "1")),
1423 TxtProperty::from(&("ONE", "2")),
1424 TxtProperty::from(&("One", "3")),
1425 ];
1426 let encoded = encode_txt(properties.iter());
1427
1428 let decoded = decode_txt(&encoded);
1430 assert_eq!(decoded.len(), 3);
1431
1432 let mut service_info =
1434 ServiceInfo::new("_test._tcp", "prop_test", "localhost", "", 1234, None).unwrap();
1435 service_info._set_properties_from_txt(&encoded);
1436 assert_eq!(service_info.get_properties().len(), 1);
1437
1438 let prop = service_info.get_properties().iter().next().unwrap();
1440 assert_eq!(prop.key, "one");
1441 assert_eq!(prop.val_str(), "1");
1442 }
1443
1444 #[test]
1445 fn test_u8_slice_to_hex() {
1446 let bytes = [0x01u8, 0x02u8, 0x03u8];
1447 let hex = u8_slice_to_hex(&bytes);
1448 assert_eq!(hex.as_str(), "0x010203");
1449
1450 let slice = "abcdefghijklmnopqrstuvwxyz";
1451 let hex = u8_slice_to_hex(slice.as_bytes());
1452 assert_eq!(hex.len(), slice.len() * 2 + 2);
1453 assert_eq!(
1454 hex.as_str(),
1455 "0x6162636465666768696a6b6c6d6e6f707172737475767778797a"
1456 );
1457 }
1458
1459 #[test]
1460 fn test_txt_property_debug() {
1461 let prop_1 = TxtProperty {
1463 key: "key1".to_string(),
1464 val: Some("val1".to_string().into()),
1465 };
1466 let prop_1_debug = format!("{:?}", &prop_1);
1467 assert_eq!(
1468 prop_1_debug,
1469 "TxtProperty {key: \"key1\", val: Some(\"val1\")}"
1470 );
1471
1472 let prop_2 = TxtProperty {
1474 key: "key2".to_string(),
1475 val: Some(vec![150u8, 151u8, 152u8]),
1476 };
1477 let prop_2_debug = format!("{:?}", &prop_2);
1478 assert_eq!(
1479 prop_2_debug,
1480 "TxtProperty {key: \"key2\", val: Some(0x969798)}"
1481 );
1482 }
1483
1484 #[test]
1485 fn test_txt_decode_property_size_out_of_bounds() {
1486 let encoded: Vec<u8> = vec![
1488 0x0b, b'k', b'e', b'y', b'1', b'=', b'v', b'a', b'l', b'u', b'e',
1490 b'1', 0x10, b'k', b'e', b'y', b'2', b'=', b'v', b'a', b'l', b'u', b'e',
1493 b'2', ];
1495 let decoded = decode_txt(&encoded);
1497 assert_eq!(decoded.len(), 1);
1500 assert_eq!(decoded[0].key, "key1");
1502 }
1503
1504 #[test]
1505 fn test_is_address_supported() {
1506 let mut service_info =
1507 ServiceInfo::new("_test._tcp", "prop_test", "testhost", "", 1234, None).unwrap();
1508
1509 let intf_v6 = Interface {
1510 name: "foo".to_string(),
1511 index: Some(1),
1512 addr: IfAddr::V6(Ifv6Addr {
1513 ip: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0x1234, 0, 0, 1),
1514 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1515 broadcast: None,
1516 prefixlen: 16,
1517 }),
1518 oper_status: IfOperStatus::Up,
1519 #[cfg(windows)]
1520 adapter_name: String::new(),
1521 };
1522
1523 let intf_v4 = Interface {
1524 name: "bar".to_string(),
1525 index: Some(1),
1526 addr: IfAddr::V4(Ifv4Addr {
1527 ip: Ipv4Addr::new(192, 1, 2, 3),
1528 netmask: Ipv4Addr::new(255, 255, 0, 0),
1529 broadcast: None,
1530 prefixlen: 16,
1531 }),
1532 oper_status: IfOperStatus::Up,
1533 #[cfg(windows)]
1534 adapter_name: String::new(),
1535 };
1536
1537 let intf_baz = Interface {
1538 name: "baz".to_string(),
1539 index: Some(1),
1540 addr: IfAddr::V6(Ifv6Addr {
1541 ip: Ipv6Addr::new(0x2003, 0xdb8, 0, 0, 0x1234, 0, 0, 1),
1542 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1543 broadcast: None,
1544 prefixlen: 16,
1545 }),
1546 oper_status: IfOperStatus::Up,
1547 #[cfg(windows)]
1548 adapter_name: String::new(),
1549 };
1550
1551 let intf_loopback_v4 = Interface {
1552 name: "foo".to_string(),
1553 index: Some(1),
1554 addr: IfAddr::V4(Ifv4Addr {
1555 ip: Ipv4Addr::new(127, 0, 0, 1),
1556 netmask: Ipv4Addr::new(255, 255, 255, 255),
1557 broadcast: None,
1558 prefixlen: 16,
1559 }),
1560 oper_status: IfOperStatus::Up,
1561 #[cfg(windows)]
1562 adapter_name: String::new(),
1563 };
1564
1565 let intf_loopback_v6 = Interface {
1566 name: "foo".to_string(),
1567 index: Some(1),
1568 addr: IfAddr::V6(Ifv6Addr {
1569 ip: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1),
1570 netmask: Ipv6Addr::new(
1571 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1572 ),
1573 broadcast: None,
1574 prefixlen: 16,
1575 }),
1576 oper_status: IfOperStatus::Up,
1577 #[cfg(windows)]
1578 adapter_name: String::new(),
1579 };
1580
1581 let intf_link_local_v4 = Interface {
1582 name: "foo".to_string(),
1583 index: Some(1),
1584 addr: IfAddr::V4(Ifv4Addr {
1585 ip: Ipv4Addr::new(169, 254, 0, 1),
1586 netmask: Ipv4Addr::new(255, 255, 0, 0),
1587 broadcast: None,
1588 prefixlen: 16,
1589 }),
1590 oper_status: IfOperStatus::Up,
1591 #[cfg(windows)]
1592 adapter_name: String::new(),
1593 };
1594
1595 let intf_link_local_v6 = Interface {
1596 name: "foo".to_string(),
1597 index: Some(1),
1598 addr: IfAddr::V6(Ifv6Addr {
1599 ip: Ipv6Addr::new(0xfe80, 0, 0, 0, 0x1234, 0, 0, 1),
1600 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1601 broadcast: None,
1602 prefixlen: 16,
1603 }),
1604 oper_status: IfOperStatus::Up,
1605 #[cfg(windows)]
1606 adapter_name: String::new(),
1607 };
1608
1609 assert!(service_info.is_address_supported(&intf_v6));
1611
1612 service_info.set_interfaces(vec![
1614 IfKind::Name("foo".to_string()),
1615 IfKind::Name("bar".to_string()),
1616 ]);
1617 assert!(!service_info.is_address_supported(&intf_baz));
1618
1619 service_info.set_link_local_only(true);
1621 assert!(!service_info.is_address_supported(&intf_v4));
1622 assert!(!service_info.is_address_supported(&intf_v6));
1623 assert!(service_info.is_address_supported(&intf_link_local_v4));
1624 assert!(service_info.is_address_supported(&intf_link_local_v6));
1625 service_info.set_link_local_only(false);
1626
1627 service_info.set_interfaces(vec![IfKind::All]);
1629 assert!(service_info.is_address_supported(&intf_v6));
1630 assert!(service_info.is_address_supported(&intf_v4));
1631
1632 service_info.set_interfaces(vec![IfKind::IPv6]);
1634 assert!(service_info.is_address_supported(&intf_v6));
1635 assert!(!service_info.is_address_supported(&intf_v4));
1636
1637 service_info.set_interfaces(vec![IfKind::IPv4]);
1639 assert!(service_info.is_address_supported(&intf_v4));
1640 assert!(!service_info.is_address_supported(&intf_v6));
1641
1642 service_info.set_interfaces(vec![IfKind::Addr(intf_v6.ip())]);
1644 assert!(service_info.is_address_supported(&intf_v6));
1645 assert!(!service_info.is_address_supported(&intf_v4));
1646
1647 service_info.set_interfaces(vec![IfKind::LoopbackV4]);
1649 assert!(service_info.is_address_supported(&intf_loopback_v4));
1650 assert!(!service_info.is_address_supported(&intf_loopback_v6));
1651
1652 service_info.set_interfaces(vec![IfKind::LoopbackV6]);
1654 assert!(!service_info.is_address_supported(&intf_loopback_v4));
1655 assert!(service_info.is_address_supported(&intf_loopback_v6));
1656 }
1657}