1#[cfg(feature = "logging")]
4use crate::log::{debug, trace};
5use crate::{
6 dns_parser::{DnsIncoming, DnsOutgoing, 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 fmt,
15 net::{IpAddr, Ipv4Addr},
16 str::FromStr,
17};
18
19#[cfg(feature = "serde")]
20use serde::{Deserialize, Serialize};
21
22const DNS_HOST_TTL: u32 = 120; const DNS_OTHER_TTL: u32 = 4500; #[derive(Debug)]
28pub(crate) struct MyIntf {
29 pub(crate) name: String,
31
32 pub(crate) index: u32,
34
35 pub(crate) addrs: HashSet<IfAddr>,
37
38 pub(crate) max_packet_size_v4: usize,
40
41 pub(crate) max_packet_size_v6: usize,
43}
44
45impl MyIntf {
46 pub(crate) fn next_ifaddr_v4(&self) -> Option<&IfAddr> {
47 self.addrs.iter().find(|a| a.ip().is_ipv4())
48 }
49
50 pub(crate) fn next_ifaddr_v6(&self) -> Option<&IfAddr> {
51 self.addrs.iter().find(|a| a.ip().is_ipv6())
52 }
53
54 pub(crate) fn max_packet_size(&self, is_ipv4: bool) -> usize {
56 if is_ipv4 {
57 self.max_packet_size_v4
58 } else {
59 self.max_packet_size_v6
60 }
61 }
62}
63
64impl From<&MyIntf> for InterfaceId {
65 fn from(my_intf: &MyIntf) -> Self {
66 InterfaceId {
67 name: my_intf.name.clone(),
68 index: my_intf.index,
69 }
70 }
71}
72
73fn escape_instance_name(name: &str) -> String {
82 let mut result = String::with_capacity(name.len() + 10); for ch in name.chars() {
85 match ch {
86 '.' => {
87 result.push('\\');
88 result.push('.');
89 }
90 '\\' => {
91 result.push('\\');
92 result.push('\\');
93 }
94 _ => result.push(ch),
95 }
96 }
97
98 result
99}
100
101#[derive(Debug, Clone)]
106pub struct ServiceInfo {
107 ty_domain: String,
111
112 sub_domain: Option<String>, fullname: String, server: String, addresses: HashSet<IpAddr>,
119 port: u16,
120 host_ttl: u32, other_ttl: u32, priority: u16,
123 weight: u16,
124 txt_properties: TxtProperties,
125 addr_auto: bool, status: HashMap<u32, ServiceStatus>, requires_probe: bool,
131
132 supported_intfs: Vec<IfKind>,
134
135 is_link_local_only: bool,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub(crate) enum ServiceStatus {
141 Probing,
142 Announced,
143 Unknown,
144}
145
146impl ServiceInfo {
147 pub fn new<Ip: AsIpAddrs, P: IntoTxtProperties>(
183 ty_domain: &str,
184 my_name: &str,
185 host_name: &str,
186 ip: Ip,
187 port: u16,
188 properties: P,
189 ) -> Result<Self> {
190 let (ty_domain, sub_domain) = split_sub_domain(ty_domain);
191
192 let escaped_name = escape_instance_name(my_name);
193 let fullname = format!("{escaped_name}.{ty_domain}");
194 let ty_domain = ty_domain.to_string();
195 let sub_domain = sub_domain.map(str::to_string);
196 let server = normalize_hostname(host_name.to_string());
197 let addresses = ip.as_ip_addrs()?;
198 let txt_properties = properties.into_txt_properties();
199
200 for prop in txt_properties.iter() {
204 let key = prop.key();
205 if !key.is_ascii() {
206 return Err(Error::Msg(format!(
207 "TXT property key {} is not ASCII",
208 prop.key()
209 )));
210 }
211 if key.contains('=') {
212 return Err(Error::Msg(format!(
213 "TXT property key {} contains '='",
214 prop.key()
215 )));
216 }
217
218 let prop_len = key.len() + prop.val().map_or(0, |v| v.len() + 1);
221 if prop_len > u8::MAX as usize {
222 return Err(Error::Msg(format!(
223 "TXT property '{}' has length {} bytes, exceeding the 255-byte limit",
224 key, prop_len
225 )));
226 }
227 }
228
229 let this = Self {
230 ty_domain,
231 sub_domain,
232 fullname,
233 server,
234 addresses,
235 port,
236 host_ttl: DNS_HOST_TTL,
237 other_ttl: DNS_OTHER_TTL,
238 priority: 0,
239 weight: 0,
240 txt_properties,
241 addr_auto: false,
242 status: HashMap::new(),
243 requires_probe: true,
244 is_link_local_only: false,
245 supported_intfs: vec![IfKind::All],
246 };
247
248 Ok(this)
249 }
250
251 pub const fn enable_addr_auto(mut self) -> Self {
255 self.addr_auto = true;
256 self
257 }
258
259 pub const fn is_addr_auto(&self) -> bool {
262 self.addr_auto
263 }
264
265 pub fn set_requires_probe(&mut self, enable: bool) {
270 self.requires_probe = enable;
271 }
272
273 pub fn set_link_local_only(&mut self, is_link_local_only: bool) {
277 self.is_link_local_only = is_link_local_only;
278 }
279
280 pub fn set_interfaces(&mut self, intfs: Vec<IfKind>) {
285 self.supported_intfs = intfs;
286 }
287
288 pub const fn requires_probe(&self) -> bool {
292 self.requires_probe
293 }
294
295 #[inline]
299 pub fn get_type(&self) -> &str {
300 &self.ty_domain
301 }
302
303 #[inline]
308 pub const fn get_subtype(&self) -> &Option<String> {
309 &self.sub_domain
310 }
311
312 pub(crate) fn matches_type_or_subtype(&self, name: &str) -> bool {
314 name == self.get_type() || self.get_subtype().as_ref().is_some_and(|v| v == name)
315 }
316
317 #[inline]
321 pub fn get_fullname(&self) -> &str {
322 &self.fullname
323 }
324
325 #[inline]
327 pub const fn get_properties(&self) -> &TxtProperties {
328 &self.txt_properties
329 }
330
331 pub fn get_property(&self, key: &str) -> Option<&TxtProperty> {
336 self.txt_properties.get(key)
337 }
338
339 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
344 self.txt_properties.get_property_val(key)
345 }
346
347 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
352 self.txt_properties.get_property_val_str(key)
353 }
354
355 #[inline]
357 pub fn get_hostname(&self) -> &str {
358 &self.server
359 }
360
361 #[inline]
363 pub const fn get_port(&self) -> u16 {
364 self.port
365 }
366
367 #[inline]
369 pub const fn get_addresses(&self) -> &HashSet<IpAddr> {
370 &self.addresses
371 }
372
373 pub fn get_addresses_v4(&self) -> HashSet<&Ipv4Addr> {
375 let mut ipv4_addresses = HashSet::new();
376
377 for ip in &self.addresses {
378 if let IpAddr::V4(ipv4) = ip {
379 ipv4_addresses.insert(ipv4);
380 }
381 }
382
383 ipv4_addresses
384 }
385
386 #[inline]
388 pub const fn get_host_ttl(&self) -> u32 {
389 self.host_ttl
390 }
391
392 #[inline]
394 pub const fn get_other_ttl(&self) -> u32 {
395 self.other_ttl
396 }
397
398 #[inline]
400 pub const fn get_priority(&self) -> u16 {
401 self.priority
402 }
403
404 #[inline]
406 pub const fn get_weight(&self) -> u16 {
407 self.weight
408 }
409
410 pub(crate) fn get_addrs_on_my_intf_v4(&self, my_intf: &MyIntf) -> Vec<IpAddr> {
412 self.addresses
413 .iter()
414 .filter(|a| a.is_ipv4() && my_intf.addrs.iter().any(|x| valid_ip_on_intf(a, x)))
415 .copied()
416 .collect()
417 }
418
419 pub(crate) fn get_addrs_on_my_intf_v6(&self, my_intf: &MyIntf) -> Vec<IpAddr> {
420 self.addresses
421 .iter()
422 .filter(|a| a.is_ipv6() && my_intf.addrs.iter().any(|x| valid_ip_on_intf(a, x)))
423 .copied()
424 .collect()
425 }
426
427 pub(crate) fn _is_ready(&self) -> bool {
429 let some_missing = self.ty_domain.is_empty()
430 || self.fullname.is_empty()
431 || self.server.is_empty()
432 || self.addresses.is_empty();
433 !some_missing
434 }
435
436 pub(crate) fn insert_ipaddr(&mut self, intf: &Interface) {
438 if self.is_address_supported(intf) {
439 self.addresses.insert(intf.addr.ip());
440 } else {
441 trace!(
442 "skipping unsupported address {} for service {}",
443 intf.addr.ip(),
444 self.fullname
445 );
446 }
447 }
448
449 pub(crate) fn remove_ipaddr(&mut self, addr: &IpAddr) {
450 self.addresses.remove(addr);
451 }
452
453 pub(crate) fn generate_txt(&self) -> Vec<u8> {
454 encode_txt(self.get_properties().iter())
455 }
456
457 pub(crate) fn _set_port(&mut self, port: u16) {
458 self.port = port;
459 }
460
461 pub(crate) fn _set_hostname(&mut self, hostname: String) {
462 self.server = normalize_hostname(hostname);
463 }
464
465 pub(crate) fn _set_properties_from_txt(&mut self, txt: &[u8]) -> bool {
467 let properties = decode_txt_unique(txt);
468 if self.txt_properties.properties != properties {
469 self.txt_properties = TxtProperties { properties };
470 true
471 } else {
472 false
473 }
474 }
475
476 pub(crate) fn _set_subtype(&mut self, subtype: String) {
477 self.sub_domain = Some(subtype);
478 }
479
480 pub(crate) fn _set_host_ttl(&mut self, ttl: u32) {
483 self.host_ttl = ttl;
484 }
485
486 pub(crate) fn _set_other_ttl(&mut self, ttl: u32) {
488 self.other_ttl = ttl;
489 }
490
491 pub(crate) fn set_status(&mut self, if_index: u32, status: ServiceStatus) {
492 match self.status.get_mut(&if_index) {
493 Some(service_status) => {
494 *service_status = status;
495 }
496 None => {
497 self.status.entry(if_index).or_insert(status);
498 }
499 }
500 }
501
502 pub(crate) fn get_status(&self, intf: u32) -> ServiceStatus {
503 self.status
504 .get(&intf)
505 .cloned()
506 .unwrap_or(ServiceStatus::Unknown)
507 }
508
509 pub fn as_resolved_service(self) -> ResolvedService {
511 let addresses: HashSet<ScopedIp> = self.addresses.into_iter().map(|a| a.into()).collect();
512 ResolvedService {
513 ty_domain: self.ty_domain,
514 sub_ty_domain: self.sub_domain,
515 fullname: self.fullname,
516 host: self.server,
517 port: self.port,
518 addresses,
519 txt_properties: self.txt_properties,
520 }
521 }
522
523 fn is_address_supported(&self, intf: &Interface) -> bool {
524 let interface_supported = self.supported_intfs.iter().any(|i| i.matches(intf));
525 let addr = intf.ip();
526 let passes_link_local = !self.is_link_local_only
527 || match &addr {
528 IpAddr::V4(ipv4) => ipv4.is_link_local(),
529 IpAddr::V6(ipv6) => is_unicast_link_local(ipv6),
530 };
531 debug!(
532 "matching inserted address {} on intf {}: passes_link_local={}, interface_supported={}",
533 addr, addr, passes_link_local, interface_supported
534 );
535 interface_supported && passes_link_local
536 }
537}
538
539fn normalize_hostname(mut hostname: String) -> String {
541 if hostname.ends_with(".local.local.") {
542 let new_len = hostname.len() - "local.".len();
543 hostname.truncate(new_len);
544 }
545 hostname
546}
547
548pub trait AsIpAddrs {
550 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>>;
551}
552
553impl<T: AsIpAddrs> AsIpAddrs for &T {
554 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
555 (*self).as_ip_addrs()
556 }
557}
558
559impl AsIpAddrs for &str {
564 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
565 let mut addrs = HashSet::new();
566
567 if !self.is_empty() {
568 let iter = self.split(',').map(str::trim).map(IpAddr::from_str);
569 for addr in iter {
570 let addr = addr.map_err(|err| Error::ParseIpAddr(err.to_string()))?;
571 addrs.insert(addr);
572 }
573 }
574
575 Ok(addrs)
576 }
577}
578
579impl AsIpAddrs for String {
580 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
581 self.as_str().as_ip_addrs()
582 }
583}
584
585impl<I: AsIpAddrs> AsIpAddrs for &[I] {
587 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
588 let mut addrs = HashSet::new();
589
590 for result in self.iter().map(I::as_ip_addrs) {
591 addrs.extend(result?);
592 }
593
594 Ok(addrs)
595 }
596}
597
598impl AsIpAddrs for () {
601 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
602 Ok(HashSet::new())
603 }
604}
605
606impl AsIpAddrs for std::net::IpAddr {
607 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
608 let mut ips = HashSet::new();
609 ips.insert(*self);
610
611 Ok(ips)
612 }
613}
614
615impl AsIpAddrs for Box<dyn AsIpAddrs> {
616 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
617 self.as_ref().as_ip_addrs()
618 }
619}
620
621#[derive(Debug, Clone, PartialEq, Eq)]
629#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
630#[cfg_attr(feature = "serde", serde(transparent))]
631pub struct TxtProperties {
632 properties: Vec<TxtProperty>,
634}
635
636impl Default for TxtProperties {
637 fn default() -> Self {
638 TxtProperties::new()
639 }
640}
641
642impl TxtProperties {
643 pub fn new() -> Self {
644 TxtProperties {
645 properties: Vec::new(),
646 }
647 }
648
649 pub fn iter(&self) -> impl Iterator<Item = &TxtProperty> {
651 self.properties.iter()
652 }
653
654 pub fn len(&self) -> usize {
656 self.properties.len()
657 }
658
659 pub fn is_empty(&self) -> bool {
661 self.properties.is_empty()
662 }
663
664 pub fn get(&self, key: &str) -> Option<&TxtProperty> {
667 let key = key.to_lowercase();
668 self.properties
669 .iter()
670 .find(|&prop| prop.key.to_lowercase() == key)
671 }
672
673 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
679 self.get(key).map(|x| x.val())
680 }
681
682 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
688 self.get(key).map(|x| x.val_str())
689 }
690
691 pub fn into_property_map_str(self) -> HashMap<String, String> {
696 self.properties
697 .into_iter()
698 .filter_map(|property| {
699 let val_string = property.val.map_or(Some(String::new()), |val| {
700 String::from_utf8(val)
701 .map_err(|e| {
702 debug!("Property value contains invalid UTF-8: {e}");
703 })
704 .ok()
705 })?;
706 Some((property.key, val_string))
707 })
708 .collect()
709 }
710}
711
712impl fmt::Display for TxtProperties {
713 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
714 let delimiter = ", ";
715 let props: Vec<String> = self.properties.iter().map(|p| p.to_string()).collect();
716 write!(f, "({})", props.join(delimiter))
717 }
718}
719
720impl From<&[u8]> for TxtProperties {
721 fn from(txt: &[u8]) -> Self {
722 let properties = decode_txt_unique(txt);
723 TxtProperties { properties }
724 }
725}
726
727#[derive(Clone, PartialEq, Eq)]
729#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
730pub struct TxtProperty {
731 key: String,
733
734 #[cfg_attr(feature = "serde", serde(rename = "value"))]
738 val: Option<Vec<u8>>,
739}
740
741impl TxtProperty {
742 pub fn key(&self) -> &str {
744 &self.key
745 }
746
747 pub fn val(&self) -> Option<&[u8]> {
751 self.val.as_deref()
752 }
753
754 pub fn val_str(&self) -> &str {
756 self.val
757 .as_ref()
758 .map_or("", |v| std::str::from_utf8(&v[..]).unwrap_or_default())
759 }
760}
761
762impl<K, V> From<&(K, V)> for TxtProperty
764where
765 K: ToString,
766 V: ToString,
767{
768 fn from(prop: &(K, V)) -> Self {
769 Self {
770 key: prop.0.to_string(),
771 val: Some(prop.1.to_string().into_bytes()),
772 }
773 }
774}
775
776impl<K, V> From<(K, V)> for TxtProperty
777where
778 K: ToString,
779 V: AsRef<[u8]>,
780{
781 fn from(prop: (K, V)) -> Self {
782 Self {
783 key: prop.0.to_string(),
784 val: Some(prop.1.as_ref().into()),
785 }
786 }
787}
788
789impl From<&str> for TxtProperty {
791 fn from(key: &str) -> Self {
792 Self {
793 key: key.to_string(),
794 val: None,
795 }
796 }
797}
798
799impl fmt::Display for TxtProperty {
800 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
801 write!(f, "{}={}", self.key, self.val_str())
802 }
803}
804
805impl fmt::Debug for TxtProperty {
809 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
810 let val_string = self.val.as_ref().map_or_else(
811 || "None".to_string(),
812 |v| {
813 std::str::from_utf8(&v[..]).map_or_else(
814 |_| format!("Some({})", u8_slice_to_hex(&v[..])),
815 |s| format!("Some(\"{s}\")"),
816 )
817 },
818 );
819
820 write!(
821 f,
822 "TxtProperty {{key: \"{}\", val: {}}}",
823 &self.key, &val_string,
824 )
825 }
826}
827
828const HEX_TABLE: [char; 16] = [
829 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
830];
831
832fn u8_slice_to_hex(slice: &[u8]) -> String {
836 let mut hex = String::with_capacity(slice.len() * 2 + 2);
837 hex.push_str("0x");
838 for b in slice {
839 hex.push(HEX_TABLE[(b >> 4) as usize]);
840 hex.push(HEX_TABLE[(b & 0x0F) as usize]);
841 }
842 hex
843}
844
845pub trait IntoTxtProperties {
847 fn into_txt_properties(self) -> TxtProperties;
848}
849
850impl IntoTxtProperties for HashMap<String, String> {
851 fn into_txt_properties(mut self) -> TxtProperties {
852 let properties = self
853 .drain()
854 .map(|(key, val)| TxtProperty {
855 key,
856 val: Some(val.into_bytes()),
857 })
858 .collect();
859 TxtProperties { properties }
860 }
861}
862
863impl IntoTxtProperties for Option<HashMap<String, String>> {
865 fn into_txt_properties(self) -> TxtProperties {
866 self.map_or_else(
867 || TxtProperties {
868 properties: Vec::new(),
869 },
870 |h| h.into_txt_properties(),
871 )
872 }
873}
874
875impl<'a, T: 'a> IntoTxtProperties for &'a [T]
877where
878 TxtProperty: From<&'a T>,
879{
880 fn into_txt_properties(self) -> TxtProperties {
881 let mut properties = Vec::new();
882 let mut keys = HashSet::new();
883 for t in self.iter() {
884 let prop = TxtProperty::from(t);
885 let key = prop.key.to_lowercase();
886 if keys.insert(key) {
887 properties.push(prop);
895 }
896 }
897 TxtProperties { properties }
898 }
899}
900
901impl IntoTxtProperties for Vec<TxtProperty> {
902 fn into_txt_properties(self) -> TxtProperties {
903 TxtProperties { properties: self }
904 }
905}
906
907fn encode_txt<'a>(properties: impl Iterator<Item = &'a TxtProperty>) -> Vec<u8> {
909 let mut bytes = Vec::new();
910 for prop in properties {
911 let mut s = prop.key.clone().into_bytes();
912 if let Some(v) = &prop.val {
913 s.extend(b"=");
914 s.extend(v);
915 }
916
917 debug_assert!(
918 s.len() <= u8::MAX as usize,
919 "TXT property '{}' exceeds 255 bytes; should have been validated in ServiceInfo::new()",
920 prop.key
921 );
922 s.truncate(u8::MAX as usize);
923 let sz: u8 = s.len() as u8;
924
925 bytes.push(sz);
928 bytes.extend(s);
929 }
930 if bytes.is_empty() {
931 bytes.push(0);
932 }
933 bytes
934}
935
936pub(crate) fn decode_txt(txt: &[u8]) -> Vec<TxtProperty> {
938 let mut properties = Vec::new();
939 let mut offset = 0;
940 while offset < txt.len() {
941 let length = txt[offset] as usize;
942 if length == 0 {
943 break; }
945 offset += 1; let offset_end = offset + length;
948 if offset_end > txt.len() {
949 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());
950 break; }
952 let kv_bytes = &txt[offset..offset_end];
953
954 let (k, v) = kv_bytes.iter().position(|&x| x == b'=').map_or_else(
956 || (kv_bytes.to_vec(), None),
957 |idx| (kv_bytes[..idx].to_vec(), Some(kv_bytes[idx + 1..].to_vec())),
958 );
959
960 match String::from_utf8(k) {
962 Ok(k_string) => {
963 properties.push(TxtProperty {
964 key: k_string,
965 val: v,
966 });
967 }
968 Err(e) => debug!("failed to convert to String from key: {}", e),
969 }
970
971 offset += length;
972 }
973
974 properties
975}
976
977fn decode_txt_unique(txt: &[u8]) -> Vec<TxtProperty> {
978 let mut properties = decode_txt(txt);
979
980 let mut keys = HashSet::new();
983 properties.retain(|p| {
984 let key = p.key().to_lowercase();
985 keys.insert(key) });
987 properties
988}
989
990pub(crate) fn valid_ip_on_intf(addr: &IpAddr, if_addr: &IfAddr) -> bool {
992 match (addr, if_addr) {
993 (IpAddr::V4(addr), IfAddr::V4(if_v4)) => {
994 let netmask = u32::from(if_v4.netmask);
995 let intf_net = u32::from(if_v4.ip) & netmask;
996 let addr_net = u32::from(*addr) & netmask;
997 addr_net == intf_net
998 }
999 (IpAddr::V6(addr), IfAddr::V6(if_v6)) => {
1000 let netmask = u128::from(if_v6.netmask);
1001 let intf_net = u128::from(if_v6.ip) & netmask;
1002 let addr_net = u128::from(*addr) & netmask;
1003 addr_net == intf_net
1004 }
1005 _ => false,
1006 }
1007}
1008
1009#[derive(Debug)]
1011pub(crate) struct Probe {
1012 pub(crate) records: Vec<DnsRecordBox>,
1014
1015 pub(crate) waiting_services: HashSet<String>,
1018
1019 pub(crate) start_time: u64,
1021
1022 pub(crate) next_send: u64,
1024}
1025
1026impl Probe {
1027 pub(crate) fn new(start_time: u64) -> Self {
1028 let next_send = start_time;
1035
1036 Self {
1037 records: Vec::new(),
1038 waiting_services: HashSet::new(),
1039 start_time,
1040 next_send,
1041 }
1042 }
1043
1044 pub(crate) fn insert_record(&mut self, record: DnsRecordBox) {
1046 let insert_position = self
1056 .records
1057 .binary_search_by(
1058 |existing| match existing.get_class().cmp(&record.get_class()) {
1059 std::cmp::Ordering::Equal => existing.get_type().cmp(&record.get_type()),
1060 other => other,
1061 },
1062 )
1063 .unwrap_or_else(|pos| pos);
1064
1065 self.records.insert(insert_position, record);
1066 }
1067
1068 pub(crate) fn tiebreaking(&mut self, msg: &DnsIncoming, probe_name: &str) {
1070 let now = crate::current_time_millis();
1071
1072 if self.start_time >= now {
1076 return;
1077 }
1078
1079 let incoming: Vec<_> = msg
1080 .authorities()
1081 .iter()
1082 .filter(|r| r.get_name() == probe_name)
1083 .collect();
1084 let min_len = self.records.len().min(incoming.len());
1094
1095 let mut cmp_result = cmp::Ordering::Equal;
1097 for (i, incoming_record) in incoming.iter().enumerate().take(min_len) {
1098 match self.records[i].compare(incoming_record.as_ref()) {
1099 cmp::Ordering::Equal => continue,
1100 other => {
1101 cmp_result = other;
1102 break; }
1104 }
1105 }
1106
1107 if cmp_result == cmp::Ordering::Equal {
1108 cmp_result = self.records.len().cmp(&incoming.len());
1110 }
1111
1112 match cmp_result {
1113 cmp::Ordering::Less => {
1114 debug!("tiebreaking '{probe_name}': LOST, will wait for one second",);
1115 self.start_time = now + 1000; self.next_send = now + 1000;
1117 }
1118 ordering => {
1119 debug!("tiebreaking '{probe_name}': {:?}", ordering);
1120 }
1121 }
1122 }
1123
1124 pub(crate) fn update_next_send(&mut self, now: u64) {
1125 self.next_send = now + 250;
1126 }
1127
1128 pub(crate) fn expired(&self, now: u64) -> bool {
1130 now >= self.start_time + 750
1133 }
1134}
1135
1136pub(crate) struct DnsRegistry {
1138 pub(crate) probing: HashMap<String, Probe>,
1149
1150 pub(crate) active: HashMap<String, Vec<DnsRecordBox>>,
1153
1154 pub(crate) new_timers: Vec<u64>,
1156
1157 pub(crate) name_changes: HashMap<String, String>,
1159
1160 pub(crate) last_multicast_v4: HashMap<String, u64>,
1170
1171 pub(crate) last_multicast_v6: HashMap<String, u64>,
1173}
1174
1175impl DnsRegistry {
1176 pub(crate) fn new() -> Self {
1177 Self {
1178 probing: HashMap::new(),
1179 active: HashMap::new(),
1180 new_timers: Vec::new(),
1181 name_changes: HashMap::new(),
1182 last_multicast_v4: HashMap::new(),
1183 last_multicast_v6: HashMap::new(),
1184 }
1185 }
1186
1187 pub(crate) fn apply_multicast_rate_limit(
1202 &mut self,
1203 out: &mut DnsOutgoing,
1204 now: u64,
1205 is_ipv4: bool,
1206 ) {
1207 let last_multicast = if is_ipv4 {
1208 &mut self.last_multicast_v4
1209 } else {
1210 &mut self.last_multicast_v6
1211 };
1212
1213 last_multicast.retain(|_, last| now.saturating_sub(*last) < MULTICAST_RATE_LIMIT_MILLIS);
1216
1217 out.retain_answers(|record| keep_after_rate_limit(last_multicast, record, now));
1218
1219 if out.answers_count() > 0 {
1221 out.retain_additionals(|record| keep_after_rate_limit(last_multicast, record, now));
1222 }
1223 }
1224
1225 pub(crate) fn resolve_name<'a>(&'a self, name: &'a str) -> &'a str {
1227 match self.name_changes.get(name) {
1228 Some(new_name) => new_name,
1229 None => name,
1230 }
1231 }
1232
1233 pub(crate) fn is_probing_done<T>(
1234 &mut self,
1235 answer: &T,
1236 service_name: &str,
1237 start_time: u64,
1238 ) -> bool
1239 where
1240 T: DnsRecordExt + Send + 'static,
1241 {
1242 if let Some(active_records) = self.active.get(answer.get_name()) {
1243 for record in active_records.iter() {
1244 if answer.matches(record.as_ref()) {
1245 debug!(
1246 "found active record {} {}",
1247 answer.get_type(),
1248 answer.get_name(),
1249 );
1250 return true;
1251 }
1252 }
1253 }
1254
1255 let probe = self
1256 .probing
1257 .entry(answer.get_name().to_string())
1258 .or_insert_with(|| {
1259 debug!("new probe of {}", answer.get_name());
1260 Probe::new(start_time)
1261 });
1262
1263 self.new_timers.push(probe.next_send);
1264
1265 for record in probe.records.iter() {
1266 if answer.matches(record.as_ref()) {
1267 debug!(
1268 "found existing record {} in probe of '{}'",
1269 answer.get_type(),
1270 answer.get_name(),
1271 );
1272 probe.waiting_services.insert(service_name.to_string());
1273 return false; }
1275 }
1276
1277 debug!(
1278 "insert record {} into probe of {}",
1279 answer.get_type(),
1280 answer.get_name(),
1281 );
1282 probe.insert_record(answer.clone_box());
1283 probe.waiting_services.insert(service_name.to_string());
1284
1285 false
1286 }
1287
1288 pub(crate) fn update_hostname(
1292 &mut self,
1293 original: &str,
1294 new_name: &str,
1295 probe_time: u64,
1296 ) -> bool {
1297 let mut found_records = Vec::new();
1298 let mut new_timer_added = false;
1299
1300 for (_name, probe) in self.probing.iter_mut() {
1301 probe.records.retain(|record| {
1302 if record.get_type() == RRType::SRV {
1303 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1304 if srv.host() == original {
1305 let mut new_record = srv.clone();
1306 new_record.set_host(new_name.to_string());
1307 found_records.push(new_record);
1308 return false;
1309 }
1310 }
1311 }
1312 true
1313 });
1314 }
1315
1316 for (_name, records) in self.active.iter_mut() {
1317 records.retain(|record| {
1318 if record.get_type() == RRType::SRV {
1319 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1320 if srv.host() == original {
1321 let mut new_record = srv.clone();
1322 new_record.set_host(new_name.to_string());
1323 found_records.push(new_record);
1324 return false;
1325 }
1326 }
1327 }
1328 true
1329 });
1330 }
1331
1332 for record in found_records {
1333 let probe = match self.probing.get_mut(record.get_name()) {
1334 Some(p) => {
1335 p.start_time = probe_time; p
1337 }
1338 None => {
1339 let new_probe = self
1340 .probing
1341 .entry(record.get_name().to_string())
1342 .or_insert_with(|| Probe::new(probe_time));
1343 new_timer_added = true;
1344 new_probe
1345 }
1346 };
1347
1348 debug!(
1349 "insert record {} with new hostname {new_name} into probe for: {}",
1350 record.get_type(),
1351 record.get_name()
1352 );
1353 probe.insert_record(record.boxed());
1354 }
1355
1356 new_timer_added
1357 }
1358}
1359
1360pub(crate) const MULTICAST_RATE_LIMIT_MILLIS: u64 = 1000;
1364
1365fn keep_after_rate_limit(
1368 last_multicast: &mut HashMap<String, u64>,
1369 record: &DnsRecordBox,
1370 now: u64,
1371) -> bool {
1372 let key = rate_limit_key(record);
1373 match last_multicast.get(&key) {
1374 Some(last) if now.saturating_sub(*last) < MULTICAST_RATE_LIMIT_MILLIS => false,
1375 _ => {
1376 last_multicast.insert(key, now);
1377 true
1378 }
1379 }
1380}
1381
1382fn rate_limit_key(record: &DnsRecordBox) -> String {
1387 format!(
1388 "{}-{}-{}",
1389 record.get_name().to_lowercase(),
1390 record.get_type(),
1391 record.rdata_print(),
1392 )
1393}
1394
1395pub(crate) fn split_sub_domain(domain: &str) -> (&str, Option<&str>) {
1397 if let Some((_, ty_domain)) = domain.rsplit_once("._sub.") {
1398 (ty_domain, Some(domain))
1399 } else {
1400 (domain, None)
1401 }
1402}
1403
1404pub(crate) fn is_unicast_link_local(addr: &Ipv6Addr) -> bool {
1410 (addr.segments()[0] & 0xffc0) == 0xfe80
1411}
1412
1413#[derive(Clone, Debug)]
1416#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1417#[non_exhaustive]
1418pub struct ResolvedService {
1419 pub ty_domain: String,
1421
1422 pub sub_ty_domain: Option<String>,
1428
1429 pub fullname: String,
1431
1432 pub host: String,
1434
1435 pub port: u16,
1437
1438 pub addresses: HashSet<ScopedIp>,
1440
1441 pub txt_properties: TxtProperties,
1443}
1444
1445impl ResolvedService {
1446 pub fn is_valid(&self) -> bool {
1448 let some_missing = self.ty_domain.is_empty()
1449 || self.fullname.is_empty()
1450 || self.host.is_empty()
1451 || self.addresses.is_empty();
1452 !some_missing
1453 }
1454
1455 #[inline]
1456 pub const fn get_subtype(&self) -> &Option<String> {
1457 &self.sub_ty_domain
1458 }
1459
1460 #[inline]
1461 pub fn get_fullname(&self) -> &str {
1462 &self.fullname
1463 }
1464
1465 #[inline]
1466 pub fn get_hostname(&self) -> &str {
1467 &self.host
1468 }
1469
1470 #[inline]
1471 pub fn get_port(&self) -> u16 {
1472 self.port
1473 }
1474
1475 #[inline]
1476 pub fn get_addresses(&self) -> &HashSet<ScopedIp> {
1477 &self.addresses
1478 }
1479
1480 pub fn get_addresses_v4(&self) -> HashSet<Ipv4Addr> {
1481 self.addresses
1482 .iter()
1483 .filter_map(|ip| match ip {
1484 ScopedIp::V4(ipv4) => Some(*ipv4.addr()),
1485 _ => None,
1486 })
1487 .collect()
1488 }
1489
1490 #[inline]
1491 pub fn get_properties(&self) -> &TxtProperties {
1492 &self.txt_properties
1493 }
1494
1495 #[inline]
1496 pub fn get_property(&self, key: &str) -> Option<&TxtProperty> {
1497 self.txt_properties.get(key)
1498 }
1499
1500 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
1501 self.txt_properties.get_property_val(key)
1502 }
1503
1504 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
1505 self.txt_properties.get_property_val_str(key)
1506 }
1507}
1508
1509#[cfg(test)]
1510mod tests {
1511 use super::{decode_txt, encode_txt, u8_slice_to_hex, DnsRegistry, ServiceInfo, TxtProperty};
1512 use crate::dns_parser::{DnsOutgoing, DnsPointer, RRType, CLASS_IN, FLAGS_QR_RESPONSE};
1513 use crate::{IfKind, IfPredicate};
1514 use if_addrs::{IfAddr, IfOperStatus, Ifv4Addr, Ifv6Addr, Interface};
1515 use std::net::{Ipv4Addr, Ipv6Addr};
1516
1517 #[test]
1521 fn test_multicast_rate_limit() {
1522 let mut registry = DnsRegistry::new();
1523
1524 let build_out = || {
1525 let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
1526 out.add_answer_at_time(
1527 DnsPointer::new(
1528 "_test._tcp.local.",
1529 RRType::PTR,
1530 CLASS_IN,
1531 4500,
1532 "inst._test._tcp.local.".to_string(),
1533 ),
1534 0,
1535 );
1536 out
1537 };
1538
1539 let now = 1_000_000;
1540
1541 let mut out = build_out();
1543 registry.apply_multicast_rate_limit(&mut out, now, true);
1544 assert_eq!(out.answers_count(), 1);
1545
1546 let mut out = build_out();
1548 registry.apply_multicast_rate_limit(&mut out, now + 500, true);
1549 assert_eq!(out.answers_count(), 0);
1550
1551 let mut out = build_out();
1553 registry.apply_multicast_rate_limit(&mut out, now + 1000, true);
1554 assert_eq!(out.answers_count(), 1);
1555 }
1556
1557 #[test]
1563 fn test_multicast_rate_limit_per_family() {
1564 let mut registry = DnsRegistry::new();
1565
1566 let build_out = || {
1567 let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
1568 out.add_answer_at_time(
1569 DnsPointer::new(
1570 "_test._tcp.local.",
1571 RRType::PTR,
1572 CLASS_IN,
1573 4500,
1574 "inst._test._tcp.local.".to_string(),
1575 ),
1576 0,
1577 );
1578 out
1579 };
1580
1581 let now = 1_000_000;
1582
1583 let mut out = build_out();
1585 registry.apply_multicast_rate_limit(&mut out, now, true);
1586 assert_eq!(out.answers_count(), 1);
1587
1588 let mut out = build_out();
1591 registry.apply_multicast_rate_limit(&mut out, now, false);
1592 assert_eq!(out.answers_count(), 1);
1593
1594 let mut out = build_out();
1597 registry.apply_multicast_rate_limit(&mut out, now + 500, true);
1598 assert_eq!(out.answers_count(), 0);
1599
1600 let mut out = build_out();
1602 registry.apply_multicast_rate_limit(&mut out, now + 500, false);
1603 assert_eq!(out.answers_count(), 0);
1604 }
1605
1606 #[test]
1611 fn test_multicast_rate_limit_additionals_not_stamped_without_answer() {
1612 let mut registry = DnsRegistry::new();
1613
1614 let ptr_answer = || {
1615 DnsPointer::new(
1616 "_test._tcp.local.",
1617 RRType::PTR,
1618 CLASS_IN,
1619 4500,
1620 "inst._test._tcp.local.".to_string(),
1621 )
1622 };
1623 let extra = || {
1624 DnsPointer::new(
1625 "_other._tcp.local.",
1626 RRType::PTR,
1627 CLASS_IN,
1628 4500,
1629 "inst._other._tcp.local.".to_string(),
1630 )
1631 };
1632
1633 let now = 1_000_000;
1634
1635 let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
1637 out.add_answer_at_time(ptr_answer(), 0);
1638 registry.apply_multicast_rate_limit(&mut out, now, true);
1639 assert_eq!(out.answers_count(), 1);
1640
1641 let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
1644 out.add_answer_at_time(ptr_answer(), 0);
1645 out.add_additional_answer(extra());
1646 registry.apply_multicast_rate_limit(&mut out, now + 100, true);
1647 assert_eq!(out.answers_count(), 0);
1648
1649 let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
1653 out.add_answer_at_time(extra(), 0);
1654 registry.apply_multicast_rate_limit(&mut out, now + 200, true);
1655 assert_eq!(out.answers_count(), 1);
1656 }
1657
1658 #[test]
1659 fn test_txt_encode_decode() {
1660 let properties = [
1661 TxtProperty::from(&("key1", "value1")),
1662 TxtProperty::from(&("key2", "value2")),
1663 ];
1664
1665 let property_count = properties.len();
1667 let encoded = encode_txt(properties.iter());
1668 assert_eq!(
1669 encoded.len(),
1670 "key1=value1".len() + "key2=value2".len() + property_count
1671 );
1672 assert_eq!(encoded[0] as usize, "key1=value1".len());
1673
1674 let decoded = decode_txt(&encoded);
1676 assert!(properties[..] == decoded[..]);
1677
1678 let properties = vec![TxtProperty::from(&("key3", ""))];
1680 let property_count = properties.len();
1681 let encoded = encode_txt(properties.iter());
1682 assert_eq!(encoded.len(), "key3=".len() + property_count);
1683
1684 let decoded = decode_txt(&encoded);
1685 assert_eq!(properties, decoded);
1686
1687 let binary_val: Vec<u8> = vec![123, 234, 0];
1689 let binary_len = binary_val.len();
1690 let properties = vec![TxtProperty::from(("key4", binary_val))];
1691 let property_count = properties.len();
1692 let encoded = encode_txt(properties.iter());
1693 assert_eq!(encoded.len(), "key4=".len() + binary_len + property_count);
1694
1695 let decoded = decode_txt(&encoded);
1696 assert_eq!(properties, decoded);
1697
1698 let properties = vec![TxtProperty::from(("key5", "val=5"))];
1700 let property_count = properties.len();
1701 let encoded = encode_txt(properties.iter());
1702 assert_eq!(
1703 encoded.len(),
1704 "key5=".len() + "val=5".len() + property_count
1705 );
1706
1707 let decoded = decode_txt(&encoded);
1708 assert_eq!(properties, decoded);
1709
1710 let properties = vec![TxtProperty::from("key6")];
1712 let property_count = properties.len();
1713 let encoded = encode_txt(properties.iter());
1714 assert_eq!(encoded.len(), "key6".len() + property_count);
1715 let decoded = decode_txt(&encoded);
1716 assert_eq!(properties, decoded);
1717
1718 let properties = [TxtProperty::from(
1720 String::from_utf8(vec![0x30; 255]).unwrap().as_str(),
1721 )];
1722 let property_count = properties.len();
1723 let encoded = encode_txt(properties.iter());
1724 assert_eq!(encoded.len(), 255 + property_count);
1726 let decoded = decode_txt(&encoded);
1727 assert_eq!(properties.to_vec(), decoded);
1728 }
1729
1730 #[test]
1731 fn test_txt_property_exceeds_255_bytes() {
1732 let long_key = String::from_utf8(vec![0x30; 256]).unwrap();
1733 let result = ServiceInfo::new(
1734 "_test._tcp.local.",
1735 "test",
1736 "host",
1737 "",
1738 1234,
1739 &[(long_key.as_str(), "")][..],
1740 );
1741 assert!(result.is_err());
1742 assert!(result
1743 .unwrap_err()
1744 .to_string()
1745 .contains("exceeding the 255-byte limit"));
1746
1747 let key_at_limit = String::from_utf8(vec![0x30; 250]).unwrap();
1750 let result = ServiceInfo::new(
1751 "_test._tcp.local.",
1752 "test",
1753 "host",
1754 "",
1755 1234,
1756 &[(key_at_limit.as_str(), "abcd")][..],
1757 );
1758 assert!(result.is_ok());
1759 }
1760
1761 #[test]
1762 fn test_set_properties_from_txt() {
1763 let properties = [
1765 TxtProperty::from(&("one", "1")),
1766 TxtProperty::from(&("ONE", "2")),
1767 TxtProperty::from(&("One", "3")),
1768 ];
1769 let encoded = encode_txt(properties.iter());
1770
1771 let decoded = decode_txt(&encoded);
1773 assert_eq!(decoded.len(), 3);
1774
1775 let mut service_info =
1777 ServiceInfo::new("_test._tcp", "prop_test", "localhost", "", 1234, None).unwrap();
1778 service_info._set_properties_from_txt(&encoded);
1779 assert_eq!(service_info.get_properties().len(), 1);
1780
1781 let prop = service_info.get_properties().iter().next().unwrap();
1783 assert_eq!(prop.key, "one");
1784 assert_eq!(prop.val_str(), "1");
1785 }
1786
1787 #[test]
1788 fn test_u8_slice_to_hex() {
1789 let bytes = [0x01u8, 0x02u8, 0x03u8];
1790 let hex = u8_slice_to_hex(&bytes);
1791 assert_eq!(hex.as_str(), "0x010203");
1792
1793 let slice = "abcdefghijklmnopqrstuvwxyz";
1794 let hex = u8_slice_to_hex(slice.as_bytes());
1795 assert_eq!(hex.len(), slice.len() * 2 + 2);
1796 assert_eq!(
1797 hex.as_str(),
1798 "0x6162636465666768696a6b6c6d6e6f707172737475767778797a"
1799 );
1800 }
1801
1802 #[test]
1803 fn test_txt_property_debug() {
1804 let prop_1 = TxtProperty {
1806 key: "key1".to_string(),
1807 val: Some("val1".to_string().into()),
1808 };
1809 let prop_1_debug = format!("{:?}", &prop_1);
1810 assert_eq!(
1811 prop_1_debug,
1812 "TxtProperty {key: \"key1\", val: Some(\"val1\")}"
1813 );
1814
1815 let prop_2 = TxtProperty {
1817 key: "key2".to_string(),
1818 val: Some(vec![150u8, 151u8, 152u8]),
1819 };
1820 let prop_2_debug = format!("{:?}", &prop_2);
1821 assert_eq!(
1822 prop_2_debug,
1823 "TxtProperty {key: \"key2\", val: Some(0x969798)}"
1824 );
1825 }
1826
1827 #[test]
1828 fn test_txt_decode_property_size_out_of_bounds() {
1829 let encoded: Vec<u8> = vec![
1831 0x0b, b'k', b'e', b'y', b'1', b'=', b'v', b'a', b'l', b'u', b'e',
1833 b'1', 0x10, b'k', b'e', b'y', b'2', b'=', b'v', b'a', b'l', b'u', b'e',
1836 b'2', ];
1838 let decoded = decode_txt(&encoded);
1840 assert_eq!(decoded.len(), 1);
1843 assert_eq!(decoded[0].key, "key1");
1845 }
1846
1847 #[test]
1848 fn test_is_address_supported() {
1849 let mut service_info =
1850 ServiceInfo::new("_test._tcp", "prop_test", "testhost", "", 1234, None).unwrap();
1851
1852 let intf_v6 = Interface {
1853 name: "foo".to_string(),
1854 index: Some(1),
1855 addr: IfAddr::V6(Ifv6Addr {
1856 ip: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0x1234, 0, 0, 1),
1857 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1858 broadcast: None,
1859 prefixlen: 16,
1860 }),
1861 oper_status: IfOperStatus::Up,
1862 is_p2p: false,
1863 #[cfg(windows)]
1864 adapter_name: String::new(),
1865 };
1866
1867 let intf_v4 = Interface {
1868 name: "bar".to_string(),
1869 index: Some(1),
1870 addr: IfAddr::V4(Ifv4Addr {
1871 ip: Ipv4Addr::new(192, 1, 2, 3),
1872 netmask: Ipv4Addr::new(255, 255, 0, 0),
1873 broadcast: None,
1874 prefixlen: 16,
1875 }),
1876 oper_status: IfOperStatus::Up,
1877 is_p2p: false,
1878 #[cfg(windows)]
1879 adapter_name: String::new(),
1880 };
1881
1882 let intf_baz = Interface {
1883 name: "baz".to_string(),
1884 index: Some(1),
1885 addr: IfAddr::V6(Ifv6Addr {
1886 ip: Ipv6Addr::new(0x2003, 0xdb8, 0, 0, 0x1234, 0, 0, 1),
1887 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1888 broadcast: None,
1889 prefixlen: 16,
1890 }),
1891 oper_status: IfOperStatus::Up,
1892 is_p2p: false,
1893 #[cfg(windows)]
1894 adapter_name: String::new(),
1895 };
1896
1897 let intf_loopback_v4 = Interface {
1898 name: "foo".to_string(),
1899 index: Some(1),
1900 addr: IfAddr::V4(Ifv4Addr {
1901 ip: Ipv4Addr::new(127, 0, 0, 1),
1902 netmask: Ipv4Addr::new(255, 255, 255, 255),
1903 broadcast: None,
1904 prefixlen: 16,
1905 }),
1906 oper_status: IfOperStatus::Up,
1907 is_p2p: false,
1908 #[cfg(windows)]
1909 adapter_name: String::new(),
1910 };
1911
1912 let intf_loopback_v6 = Interface {
1913 name: "foo".to_string(),
1914 index: Some(1),
1915 addr: IfAddr::V6(Ifv6Addr {
1916 ip: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1),
1917 netmask: Ipv6Addr::new(
1918 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1919 ),
1920 broadcast: None,
1921 prefixlen: 16,
1922 }),
1923 oper_status: IfOperStatus::Up,
1924 is_p2p: false,
1925 #[cfg(windows)]
1926 adapter_name: String::new(),
1927 };
1928
1929 let intf_link_local_v4 = Interface {
1930 name: "foo".to_string(),
1931 index: Some(1),
1932 addr: IfAddr::V4(Ifv4Addr {
1933 ip: Ipv4Addr::new(169, 254, 0, 1),
1934 netmask: Ipv4Addr::new(255, 255, 0, 0),
1935 broadcast: None,
1936 prefixlen: 16,
1937 }),
1938 oper_status: IfOperStatus::Up,
1939 is_p2p: false,
1940 #[cfg(windows)]
1941 adapter_name: String::new(),
1942 };
1943
1944 let intf_link_local_v6 = Interface {
1945 name: "foo".to_string(),
1946 index: Some(1),
1947 addr: IfAddr::V6(Ifv6Addr {
1948 ip: Ipv6Addr::new(0xfe80, 0, 0, 0, 0x1234, 0, 0, 1),
1949 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1950 broadcast: None,
1951 prefixlen: 16,
1952 }),
1953 oper_status: IfOperStatus::Up,
1954 is_p2p: false,
1955 #[cfg(windows)]
1956 adapter_name: String::new(),
1957 };
1958
1959 assert!(service_info.is_address_supported(&intf_v6));
1961
1962 service_info.set_interfaces(vec![
1964 IfKind::Name("foo".to_string()),
1965 IfKind::Name("bar".to_string()),
1966 ]);
1967 assert!(!service_info.is_address_supported(&intf_baz));
1968
1969 service_info.set_link_local_only(true);
1971 assert!(!service_info.is_address_supported(&intf_v4));
1972 assert!(!service_info.is_address_supported(&intf_v6));
1973 assert!(service_info.is_address_supported(&intf_link_local_v4));
1974 assert!(service_info.is_address_supported(&intf_link_local_v6));
1975 service_info.set_link_local_only(false);
1976
1977 service_info.set_interfaces(vec![IfKind::All]);
1979 assert!(service_info.is_address_supported(&intf_v6));
1980 assert!(service_info.is_address_supported(&intf_v4));
1981
1982 service_info.set_interfaces(vec![IfKind::IPv6]);
1984 assert!(service_info.is_address_supported(&intf_v6));
1985 assert!(!service_info.is_address_supported(&intf_v4));
1986
1987 service_info.set_interfaces(vec![IfKind::IPv4]);
1989 assert!(service_info.is_address_supported(&intf_v4));
1990 assert!(!service_info.is_address_supported(&intf_v6));
1991
1992 service_info.set_interfaces(vec![IfKind::Addr(intf_v6.ip())]);
1994 assert!(service_info.is_address_supported(&intf_v6));
1995 assert!(!service_info.is_address_supported(&intf_v4));
1996
1997 service_info.set_interfaces(vec![IfKind::LoopbackV4]);
1999 assert!(service_info.is_address_supported(&intf_loopback_v4));
2000 assert!(!service_info.is_address_supported(&intf_loopback_v6));
2001
2002 service_info.set_interfaces(vec![IfKind::LoopbackV6]);
2004 assert!(!service_info.is_address_supported(&intf_loopback_v4));
2005 assert!(service_info.is_address_supported(&intf_loopback_v6));
2006
2007 service_info.set_interfaces(vec![IfKind::Predicate(IfPredicate::new(|intf| {
2009 intf.ip().is_ipv4() && intf.name == "foo"
2010 }))]);
2011 assert!(service_info.is_address_supported(&intf_loopback_v4));
2012 assert!(!service_info.is_address_supported(&intf_v4));
2013 assert!(!service_info.is_address_supported(&intf_loopback_v6));
2014 }
2015
2016 #[test]
2017 fn test_scoped_ip_set_detects_interface_id_change() {
2018 use crate::{InterfaceId, ScopedIp, ScopedIpV4};
2019 use std::collections::HashSet;
2020
2021 let intf1 = InterfaceId {
2022 name: "en0".to_string(),
2023 index: 1,
2024 };
2025 let intf2 = InterfaceId {
2026 name: "en1".to_string(),
2027 index: 2,
2028 };
2029 let addr = Ipv4Addr::new(192, 168, 1, 100);
2030
2031 let scoped_v4_one_intf = ScopedIpV4::new(addr, intf1);
2032 let mut scoped_v4_two_intfs = scoped_v4_one_intf.clone();
2033 scoped_v4_two_intfs.add_interface_id(intf2);
2034
2035 assert_ne!(scoped_v4_one_intf, scoped_v4_two_intfs);
2036
2037 let set_old: HashSet<ScopedIp> = HashSet::from([ScopedIp::V4(scoped_v4_one_intf)]);
2038 let set_new: HashSet<ScopedIp> = HashSet::from([ScopedIp::V4(scoped_v4_two_intfs)]);
2039
2040 assert_ne!(set_old, set_new);
2041 }
2042
2043 #[cfg(test)]
2044 #[cfg(feature = "serde")]
2045 mod serde {
2046 use super::{Ipv4Addr, Ipv6Addr};
2047 use crate::{ResolvedService, ScopedIp, TxtProperties};
2048
2049 use std::collections::HashSet;
2050 use std::net::IpAddr;
2051
2052 #[test]
2053 fn test_deserialize_serialize() -> Result<(), Box<dyn std::error::Error>> {
2054 let addresses = HashSet::from([
2055 ScopedIp::from(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
2056 ScopedIp::from(IpAddr::V6(Ipv6Addr::new(
2057 0xfe80, 0x2001, 0x0db8, 0x85a3, 0x0000, 0x8a2e, 0x0370, 0x7334,
2058 ))),
2059 ]);
2060
2061 let service = ResolvedService {
2062 ty_domain: "_http._tcp.local.".to_owned(),
2063 sub_ty_domain: None,
2064 fullname: "example._http._tcp.local.".to_owned(),
2065 host: "example.local.".to_owned(),
2066 port: 1234,
2067 addresses,
2068 txt_properties: TxtProperties::new(),
2069 };
2070
2071 let json = serde_json::to_value(&service)?;
2072
2073 let parsed: ResolvedService = serde_json::from_value(json)?;
2074
2075 assert!(compare(&service, &parsed));
2076
2077 Ok(())
2078 }
2079
2080 fn compare(service: &ResolvedService, other: &ResolvedService) -> bool {
2081 service.ty_domain == other.ty_domain
2082 && service.sub_ty_domain == other.sub_ty_domain
2083 && service.fullname == other.fullname
2084 && service.host == other.host
2085 && service.port == other.port
2086 && service.addresses == other.addresses
2087 && service.txt_properties == other.txt_properties
2088 }
2089 }
2090}