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) -> bool {
440 if self.is_address_supported(intf) {
441 self.addresses.insert(intf.addr.ip());
442 true
443 } else {
444 trace!(
445 "skipping unsupported address {} for service {}",
446 intf.addr.ip(),
447 self.fullname
448 );
449 false
450 }
451 }
452
453 pub(crate) fn remove_ipaddr(&mut self, addr: &IpAddr) {
454 self.addresses.remove(addr);
455 }
456
457 pub(crate) fn generate_txt(&self) -> Vec<u8> {
458 encode_txt(self.get_properties().iter())
459 }
460
461 pub(crate) fn _set_port(&mut self, port: u16) {
462 self.port = port;
463 }
464
465 pub(crate) fn _set_hostname(&mut self, hostname: String) {
466 self.server = normalize_hostname(hostname);
467 }
468
469 pub(crate) fn _set_properties_from_txt(&mut self, txt: &[u8]) -> bool {
471 let properties = decode_txt_unique(txt);
472 if self.txt_properties.properties != properties {
473 self.txt_properties = TxtProperties { properties };
474 true
475 } else {
476 false
477 }
478 }
479
480 pub(crate) fn _set_subtype(&mut self, subtype: String) {
481 self.sub_domain = Some(subtype);
482 }
483
484 pub(crate) fn _set_host_ttl(&mut self, ttl: u32) {
487 self.host_ttl = ttl;
488 }
489
490 pub(crate) fn _set_other_ttl(&mut self, ttl: u32) {
492 self.other_ttl = ttl;
493 }
494
495 pub(crate) fn set_status(&mut self, if_index: u32, status: ServiceStatus) {
496 match self.status.get_mut(&if_index) {
497 Some(service_status) => {
498 *service_status = status;
499 }
500 None => {
501 self.status.entry(if_index).or_insert(status);
502 }
503 }
504 }
505
506 pub(crate) fn get_status(&self, intf: u32) -> ServiceStatus {
507 self.status
508 .get(&intf)
509 .cloned()
510 .unwrap_or(ServiceStatus::Unknown)
511 }
512
513 pub fn as_resolved_service(self) -> ResolvedService {
515 let addresses: HashSet<ScopedIp> = self.addresses.into_iter().map(|a| a.into()).collect();
516 ResolvedService {
517 ty_domain: self.ty_domain,
518 sub_ty_domain: self.sub_domain,
519 fullname: self.fullname,
520 host: self.server,
521 port: self.port,
522 addresses,
523 txt_properties: self.txt_properties,
524 }
525 }
526
527 pub(crate) fn is_address_supported(&self, intf: &Interface) -> bool {
528 let interface_supported = self.supported_intfs.iter().any(|i| i.matches(intf));
529 let addr = intf.ip();
530 let passes_link_local = !self.is_link_local_only
531 || match &addr {
532 IpAddr::V4(ipv4) => ipv4.is_link_local(),
533 IpAddr::V6(ipv6) => is_unicast_link_local(ipv6),
534 };
535 debug!(
536 "matching inserted address {} on intf {}: passes_link_local={}, interface_supported={}",
537 addr, addr, passes_link_local, interface_supported
538 );
539 interface_supported && passes_link_local
540 }
541}
542
543fn normalize_hostname(mut hostname: String) -> String {
545 if hostname.ends_with(".local.local.") {
546 let new_len = hostname.len() - "local.".len();
547 hostname.truncate(new_len);
548 }
549 hostname
550}
551
552pub trait AsIpAddrs {
554 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>>;
555}
556
557impl<T: AsIpAddrs> AsIpAddrs for &T {
558 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
559 (*self).as_ip_addrs()
560 }
561}
562
563impl AsIpAddrs for &str {
568 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
569 let mut addrs = HashSet::new();
570
571 if !self.is_empty() {
572 let iter = self.split(',').map(str::trim).map(IpAddr::from_str);
573 for addr in iter {
574 let addr = addr.map_err(|err| Error::ParseIpAddr(err.to_string()))?;
575 addrs.insert(addr);
576 }
577 }
578
579 Ok(addrs)
580 }
581}
582
583impl AsIpAddrs for String {
584 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
585 self.as_str().as_ip_addrs()
586 }
587}
588
589impl<I: AsIpAddrs> AsIpAddrs for &[I] {
591 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
592 let mut addrs = HashSet::new();
593
594 for result in self.iter().map(I::as_ip_addrs) {
595 addrs.extend(result?);
596 }
597
598 Ok(addrs)
599 }
600}
601
602impl AsIpAddrs for () {
605 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
606 Ok(HashSet::new())
607 }
608}
609
610impl AsIpAddrs for std::net::IpAddr {
611 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
612 let mut ips = HashSet::new();
613 ips.insert(*self);
614
615 Ok(ips)
616 }
617}
618
619impl AsIpAddrs for Box<dyn AsIpAddrs> {
620 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
621 self.as_ref().as_ip_addrs()
622 }
623}
624
625#[derive(Debug, Clone, PartialEq, Eq)]
633#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
634#[cfg_attr(feature = "serde", serde(transparent))]
635pub struct TxtProperties {
636 properties: Vec<TxtProperty>,
638}
639
640impl Default for TxtProperties {
641 fn default() -> Self {
642 TxtProperties::new()
643 }
644}
645
646impl TxtProperties {
647 pub fn new() -> Self {
648 TxtProperties {
649 properties: Vec::new(),
650 }
651 }
652
653 pub fn iter(&self) -> impl Iterator<Item = &TxtProperty> {
655 self.properties.iter()
656 }
657
658 pub fn len(&self) -> usize {
660 self.properties.len()
661 }
662
663 pub fn is_empty(&self) -> bool {
665 self.properties.is_empty()
666 }
667
668 pub fn get(&self, key: &str) -> Option<&TxtProperty> {
671 let key = key.to_lowercase();
672 self.properties
673 .iter()
674 .find(|&prop| prop.key.to_lowercase() == key)
675 }
676
677 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
683 self.get(key).map(|x| x.val())
684 }
685
686 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
692 self.get(key).map(|x| x.val_str())
693 }
694
695 pub fn into_property_map_str(self) -> HashMap<String, String> {
700 self.properties
701 .into_iter()
702 .filter_map(|property| {
703 let val_string = property.val.map_or(Some(String::new()), |val| {
704 String::from_utf8(val)
705 .map_err(|e| {
706 debug!("Property value contains invalid UTF-8: {e}");
707 })
708 .ok()
709 })?;
710 Some((property.key, val_string))
711 })
712 .collect()
713 }
714}
715
716impl fmt::Display for TxtProperties {
717 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
718 let delimiter = ", ";
719 let props: Vec<String> = self.properties.iter().map(|p| p.to_string()).collect();
720 write!(f, "({})", props.join(delimiter))
721 }
722}
723
724impl From<&[u8]> for TxtProperties {
725 fn from(txt: &[u8]) -> Self {
726 let properties = decode_txt_unique(txt);
727 TxtProperties { properties }
728 }
729}
730
731#[derive(Clone, PartialEq, Eq)]
733#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
734pub struct TxtProperty {
735 key: String,
737
738 #[cfg_attr(feature = "serde", serde(rename = "value"))]
742 val: Option<Vec<u8>>,
743}
744
745impl TxtProperty {
746 pub fn key(&self) -> &str {
748 &self.key
749 }
750
751 pub fn val(&self) -> Option<&[u8]> {
755 self.val.as_deref()
756 }
757
758 pub fn val_str(&self) -> &str {
760 self.val
761 .as_ref()
762 .map_or("", |v| std::str::from_utf8(&v[..]).unwrap_or_default())
763 }
764}
765
766impl<K, V> From<&(K, V)> for TxtProperty
768where
769 K: ToString,
770 V: ToString,
771{
772 fn from(prop: &(K, V)) -> Self {
773 Self {
774 key: prop.0.to_string(),
775 val: Some(prop.1.to_string().into_bytes()),
776 }
777 }
778}
779
780impl<K, V> From<(K, V)> for TxtProperty
781where
782 K: ToString,
783 V: AsRef<[u8]>,
784{
785 fn from(prop: (K, V)) -> Self {
786 Self {
787 key: prop.0.to_string(),
788 val: Some(prop.1.as_ref().into()),
789 }
790 }
791}
792
793impl From<&str> for TxtProperty {
795 fn from(key: &str) -> Self {
796 Self {
797 key: key.to_string(),
798 val: None,
799 }
800 }
801}
802
803impl fmt::Display for TxtProperty {
804 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
805 write!(f, "{}={}", self.key, self.val_str())
806 }
807}
808
809impl fmt::Debug for TxtProperty {
813 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
814 let val_string = self.val.as_ref().map_or_else(
815 || "None".to_string(),
816 |v| {
817 std::str::from_utf8(&v[..]).map_or_else(
818 |_| format!("Some({})", u8_slice_to_hex(&v[..])),
819 |s| format!("Some(\"{s}\")"),
820 )
821 },
822 );
823
824 write!(
825 f,
826 "TxtProperty {{key: \"{}\", val: {}}}",
827 &self.key, &val_string,
828 )
829 }
830}
831
832const HEX_TABLE: [char; 16] = [
833 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
834];
835
836fn u8_slice_to_hex(slice: &[u8]) -> String {
840 let mut hex = String::with_capacity(slice.len() * 2 + 2);
841 hex.push_str("0x");
842 for b in slice {
843 hex.push(HEX_TABLE[(b >> 4) as usize]);
844 hex.push(HEX_TABLE[(b & 0x0F) as usize]);
845 }
846 hex
847}
848
849pub trait IntoTxtProperties {
851 fn into_txt_properties(self) -> TxtProperties;
852}
853
854impl IntoTxtProperties for HashMap<String, String> {
855 fn into_txt_properties(mut self) -> TxtProperties {
856 let properties = self
857 .drain()
858 .map(|(key, val)| TxtProperty {
859 key,
860 val: Some(val.into_bytes()),
861 })
862 .collect();
863 TxtProperties { properties }
864 }
865}
866
867impl IntoTxtProperties for Option<HashMap<String, String>> {
869 fn into_txt_properties(self) -> TxtProperties {
870 self.map_or_else(
871 || TxtProperties {
872 properties: Vec::new(),
873 },
874 |h| h.into_txt_properties(),
875 )
876 }
877}
878
879impl<'a, T: 'a> IntoTxtProperties for &'a [T]
881where
882 TxtProperty: From<&'a T>,
883{
884 fn into_txt_properties(self) -> TxtProperties {
885 let mut properties = Vec::new();
886 let mut keys = HashSet::new();
887 for t in self.iter() {
888 let prop = TxtProperty::from(t);
889 let key = prop.key.to_lowercase();
890 if keys.insert(key) {
891 properties.push(prop);
899 }
900 }
901 TxtProperties { properties }
902 }
903}
904
905impl IntoTxtProperties for Vec<TxtProperty> {
906 fn into_txt_properties(self) -> TxtProperties {
907 TxtProperties { properties: self }
908 }
909}
910
911fn encode_txt<'a>(properties: impl Iterator<Item = &'a TxtProperty>) -> Vec<u8> {
913 let mut bytes = Vec::new();
914 for prop in properties {
915 let mut s = prop.key.clone().into_bytes();
916 if let Some(v) = &prop.val {
917 s.extend(b"=");
918 s.extend(v);
919 }
920
921 debug_assert!(
922 s.len() <= u8::MAX as usize,
923 "TXT property '{}' exceeds 255 bytes; should have been validated in ServiceInfo::new()",
924 prop.key
925 );
926 s.truncate(u8::MAX as usize);
927 let sz: u8 = s.len() as u8;
928
929 bytes.push(sz);
932 bytes.extend(s);
933 }
934 if bytes.is_empty() {
935 bytes.push(0);
936 }
937 bytes
938}
939
940pub(crate) fn decode_txt(txt: &[u8]) -> Vec<TxtProperty> {
942 let mut properties = Vec::new();
943 let mut offset = 0;
944 while offset < txt.len() {
945 let length = txt[offset] as usize;
946 if length == 0 {
947 break; }
949 offset += 1; let offset_end = offset + length;
952 if offset_end > txt.len() {
953 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());
954 break; }
956 let kv_bytes = &txt[offset..offset_end];
957
958 let (k, v) = kv_bytes.iter().position(|&x| x == b'=').map_or_else(
960 || (kv_bytes.to_vec(), None),
961 |idx| (kv_bytes[..idx].to_vec(), Some(kv_bytes[idx + 1..].to_vec())),
962 );
963
964 match String::from_utf8(k) {
966 Ok(k_string) => {
967 properties.push(TxtProperty {
968 key: k_string,
969 val: v,
970 });
971 }
972 Err(e) => debug!("failed to convert to String from key: {}", e),
973 }
974
975 offset += length;
976 }
977
978 properties
979}
980
981fn decode_txt_unique(txt: &[u8]) -> Vec<TxtProperty> {
982 let mut properties = decode_txt(txt);
983
984 let mut keys = HashSet::new();
987 properties.retain(|p| {
988 let key = p.key().to_lowercase();
989 keys.insert(key) });
991 properties
992}
993
994pub(crate) fn valid_ip_on_intf(addr: &IpAddr, if_addr: &IfAddr) -> bool {
996 match (addr, if_addr) {
997 (IpAddr::V4(addr), IfAddr::V4(if_v4)) => {
998 let netmask = u32::from(if_v4.netmask);
999 let intf_net = u32::from(if_v4.ip) & netmask;
1000 let addr_net = u32::from(*addr) & netmask;
1001 addr_net == intf_net
1002 }
1003 (IpAddr::V6(addr), IfAddr::V6(if_v6)) => {
1004 let netmask = u128::from(if_v6.netmask);
1005 let intf_net = u128::from(if_v6.ip) & netmask;
1006 let addr_net = u128::from(*addr) & netmask;
1007 addr_net == intf_net
1008 }
1009 _ => false,
1010 }
1011}
1012
1013#[derive(Debug)]
1015pub(crate) struct Probe {
1016 pub(crate) records: Vec<DnsRecordBox>,
1018
1019 pub(crate) waiting_services: HashSet<String>,
1022
1023 pub(crate) start_time: u64,
1025
1026 pub(crate) next_send: u64,
1028}
1029
1030impl Probe {
1031 pub(crate) fn new(start_time: u64) -> Self {
1032 let next_send = start_time;
1039
1040 Self {
1041 records: Vec::new(),
1042 waiting_services: HashSet::new(),
1043 start_time,
1044 next_send,
1045 }
1046 }
1047
1048 pub(crate) fn insert_record(&mut self, record: DnsRecordBox) {
1050 let insert_position = self
1060 .records
1061 .binary_search_by(
1062 |existing| match existing.get_class().cmp(&record.get_class()) {
1063 std::cmp::Ordering::Equal => existing.get_type().cmp(&record.get_type()),
1064 other => other,
1065 },
1066 )
1067 .unwrap_or_else(|pos| pos);
1068
1069 self.records.insert(insert_position, record);
1070 }
1071
1072 pub(crate) fn tiebreaking(&mut self, msg: &DnsIncoming, probe_name: &str) {
1074 let now = crate::current_time_millis();
1075
1076 if self.start_time >= now {
1080 return;
1081 }
1082
1083 let incoming: Vec<_> = msg
1084 .authorities()
1085 .iter()
1086 .filter(|r| r.get_name() == probe_name)
1087 .collect();
1088 let min_len = self.records.len().min(incoming.len());
1098
1099 let mut cmp_result = cmp::Ordering::Equal;
1101 for (i, incoming_record) in incoming.iter().enumerate().take(min_len) {
1102 match self.records[i].compare(incoming_record.as_ref()) {
1103 cmp::Ordering::Equal => continue,
1104 other => {
1105 cmp_result = other;
1106 break; }
1108 }
1109 }
1110
1111 if cmp_result == cmp::Ordering::Equal {
1112 cmp_result = self.records.len().cmp(&incoming.len());
1114 }
1115
1116 match cmp_result {
1117 cmp::Ordering::Less => {
1118 debug!("tiebreaking '{probe_name}': LOST, will wait for one second",);
1119 self.start_time = now + 1000; self.next_send = now + 1000;
1121 }
1122 ordering => {
1123 debug!("tiebreaking '{probe_name}': {:?}", ordering);
1124 }
1125 }
1126 }
1127
1128 pub(crate) fn update_next_send(&mut self, now: u64) {
1129 self.next_send = now + 250;
1130 }
1131
1132 pub(crate) fn expired(&self, now: u64) -> bool {
1134 now >= self.start_time + 750
1137 }
1138}
1139
1140pub(crate) struct DnsRegistry {
1142 pub(crate) probing: HashMap<String, Probe>,
1153
1154 pub(crate) active: HashMap<String, Vec<DnsRecordBox>>,
1157
1158 pub(crate) new_timers: Vec<u64>,
1160
1161 pub(crate) name_changes: HashMap<String, String>,
1163
1164 pub(crate) last_multicast_v4: HashMap<String, u64>,
1174
1175 pub(crate) last_multicast_v6: HashMap<String, u64>,
1177}
1178
1179impl DnsRegistry {
1180 pub(crate) fn new() -> Self {
1181 Self {
1182 probing: HashMap::new(),
1183 active: HashMap::new(),
1184 new_timers: Vec::new(),
1185 name_changes: HashMap::new(),
1186 last_multicast_v4: HashMap::new(),
1187 last_multicast_v6: HashMap::new(),
1188 }
1189 }
1190
1191 pub(crate) fn apply_multicast_rate_limit(
1206 &mut self,
1207 out: &mut DnsOutgoing,
1208 now: u64,
1209 is_ipv4: bool,
1210 ) {
1211 let last_multicast = if is_ipv4 {
1212 &mut self.last_multicast_v4
1213 } else {
1214 &mut self.last_multicast_v6
1215 };
1216
1217 last_multicast.retain(|_, last| now.saturating_sub(*last) < MULTICAST_RATE_LIMIT_MILLIS);
1220
1221 out.retain_answers(|record| keep_after_rate_limit(last_multicast, record, now));
1222
1223 if out.answers_count() > 0 {
1225 out.retain_additionals(|record| keep_after_rate_limit(last_multicast, record, now));
1226 }
1227 }
1228
1229 pub(crate) fn resolve_name<'a>(&'a self, name: &'a str) -> &'a str {
1231 match self.name_changes.get(name) {
1232 Some(new_name) => new_name,
1233 None => name,
1234 }
1235 }
1236
1237 pub(crate) fn is_probing_done<T>(
1238 &mut self,
1239 answer: &T,
1240 service_name: &str,
1241 start_time: u64,
1242 ) -> bool
1243 where
1244 T: DnsRecordExt + Send + 'static,
1245 {
1246 if let Some(active_records) = self.active.get(answer.get_name()) {
1247 for record in active_records.iter() {
1248 if answer.matches(record.as_ref()) {
1249 debug!(
1250 "found active record {} {}",
1251 answer.get_type(),
1252 answer.get_name(),
1253 );
1254 return true;
1255 }
1256 }
1257 }
1258
1259 let probe = self
1260 .probing
1261 .entry(answer.get_name().to_string())
1262 .or_insert_with(|| {
1263 debug!("new probe of {}", answer.get_name());
1264 Probe::new(start_time)
1265 });
1266
1267 self.new_timers.push(probe.next_send);
1268
1269 for record in probe.records.iter() {
1270 if answer.matches(record.as_ref()) {
1271 debug!(
1272 "found existing record {} in probe of '{}'",
1273 answer.get_type(),
1274 answer.get_name(),
1275 );
1276 probe.waiting_services.insert(service_name.to_string());
1277 return false; }
1279 }
1280
1281 debug!(
1282 "insert record {} into probe of {}",
1283 answer.get_type(),
1284 answer.get_name(),
1285 );
1286 probe.insert_record(answer.clone_box());
1287 probe.waiting_services.insert(service_name.to_string());
1288
1289 false
1290 }
1291
1292 pub(crate) fn update_hostname(
1296 &mut self,
1297 original: &str,
1298 new_name: &str,
1299 probe_time: u64,
1300 ) -> bool {
1301 let mut found_records = Vec::new();
1302 let mut new_timer_added = false;
1303
1304 for (_name, probe) in self.probing.iter_mut() {
1305 probe.records.retain(|record| {
1306 if record.get_type() == RRType::SRV {
1307 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1308 if srv.host() == original {
1309 let mut new_record = srv.clone();
1310 new_record.set_host(new_name.to_string());
1311 found_records.push(new_record);
1312 return false;
1313 }
1314 }
1315 }
1316 true
1317 });
1318 }
1319
1320 for (_name, records) in self.active.iter_mut() {
1321 records.retain(|record| {
1322 if record.get_type() == RRType::SRV {
1323 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1324 if srv.host() == original {
1325 let mut new_record = srv.clone();
1326 new_record.set_host(new_name.to_string());
1327 found_records.push(new_record);
1328 return false;
1329 }
1330 }
1331 }
1332 true
1333 });
1334 }
1335
1336 for record in found_records {
1337 let probe = match self.probing.get_mut(record.get_name()) {
1338 Some(p) => {
1339 p.start_time = probe_time; p
1341 }
1342 None => {
1343 let new_probe = self
1344 .probing
1345 .entry(record.get_name().to_string())
1346 .or_insert_with(|| Probe::new(probe_time));
1347 new_timer_added = true;
1348 new_probe
1349 }
1350 };
1351
1352 debug!(
1353 "insert record {} with new hostname {new_name} into probe for: {}",
1354 record.get_type(),
1355 record.get_name()
1356 );
1357 probe.insert_record(record.boxed());
1358 }
1359
1360 new_timer_added
1361 }
1362}
1363
1364pub(crate) const MULTICAST_RATE_LIMIT_MILLIS: u64 = 1000;
1368
1369fn keep_after_rate_limit(
1372 last_multicast: &mut HashMap<String, u64>,
1373 record: &DnsRecordBox,
1374 now: u64,
1375) -> bool {
1376 let key = rate_limit_key(record);
1377 match last_multicast.get(&key) {
1378 Some(last) if now.saturating_sub(*last) < MULTICAST_RATE_LIMIT_MILLIS => false,
1379 _ => {
1380 last_multicast.insert(key, now);
1381 true
1382 }
1383 }
1384}
1385
1386fn rate_limit_key(record: &DnsRecordBox) -> String {
1391 format!(
1392 "{}-{}-{}",
1393 record.get_name().to_lowercase(),
1394 record.get_type(),
1395 record.rdata_print(),
1396 )
1397}
1398
1399pub(crate) fn split_sub_domain(domain: &str) -> (&str, Option<&str>) {
1401 if let Some((_, ty_domain)) = domain.rsplit_once("._sub.") {
1402 (ty_domain, Some(domain))
1403 } else {
1404 (domain, None)
1405 }
1406}
1407
1408pub(crate) fn is_unicast_link_local(addr: &Ipv6Addr) -> bool {
1414 (addr.segments()[0] & 0xffc0) == 0xfe80
1415}
1416
1417#[derive(Clone, Debug)]
1420#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1421#[non_exhaustive]
1422pub struct ResolvedService {
1423 pub ty_domain: String,
1425
1426 pub sub_ty_domain: Option<String>,
1432
1433 pub fullname: String,
1435
1436 pub host: String,
1438
1439 pub port: u16,
1441
1442 pub addresses: HashSet<ScopedIp>,
1444
1445 pub txt_properties: TxtProperties,
1447}
1448
1449impl ResolvedService {
1450 pub fn is_valid(&self) -> bool {
1452 let some_missing = self.ty_domain.is_empty()
1453 || self.fullname.is_empty()
1454 || self.host.is_empty()
1455 || self.addresses.is_empty();
1456 !some_missing
1457 }
1458
1459 #[inline]
1460 pub const fn get_subtype(&self) -> &Option<String> {
1461 &self.sub_ty_domain
1462 }
1463
1464 #[inline]
1465 pub fn get_fullname(&self) -> &str {
1466 &self.fullname
1467 }
1468
1469 #[inline]
1470 pub fn get_hostname(&self) -> &str {
1471 &self.host
1472 }
1473
1474 #[inline]
1475 pub fn get_port(&self) -> u16 {
1476 self.port
1477 }
1478
1479 #[inline]
1480 pub fn get_addresses(&self) -> &HashSet<ScopedIp> {
1481 &self.addresses
1482 }
1483
1484 pub fn get_addresses_v4(&self) -> HashSet<Ipv4Addr> {
1485 self.addresses
1486 .iter()
1487 .filter_map(|ip| match ip {
1488 ScopedIp::V4(ipv4) => Some(*ipv4.addr()),
1489 _ => None,
1490 })
1491 .collect()
1492 }
1493
1494 #[inline]
1495 pub fn get_properties(&self) -> &TxtProperties {
1496 &self.txt_properties
1497 }
1498
1499 #[inline]
1500 pub fn get_property(&self, key: &str) -> Option<&TxtProperty> {
1501 self.txt_properties.get(key)
1502 }
1503
1504 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
1505 self.txt_properties.get_property_val(key)
1506 }
1507
1508 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
1509 self.txt_properties.get_property_val_str(key)
1510 }
1511}
1512
1513#[cfg(test)]
1514mod tests {
1515 use super::{decode_txt, encode_txt, u8_slice_to_hex, DnsRegistry, ServiceInfo, TxtProperty};
1516 use crate::dns_parser::{DnsOutgoing, DnsPointer, RRType, CLASS_IN, FLAGS_QR_RESPONSE};
1517 use crate::{IfKind, IfPredicate};
1518 use if_addrs::{IfAddr, IfOperStatus, Ifv4Addr, Ifv6Addr, Interface};
1519 use std::net::{Ipv4Addr, Ipv6Addr};
1520
1521 #[test]
1525 fn test_multicast_rate_limit() {
1526 let mut registry = DnsRegistry::new();
1527
1528 let build_out = || {
1529 let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
1530 out.add_answer_at_time(
1531 DnsPointer::new(
1532 "_test._tcp.local.",
1533 RRType::PTR,
1534 CLASS_IN,
1535 4500,
1536 "inst._test._tcp.local.".to_string(),
1537 ),
1538 0,
1539 );
1540 out
1541 };
1542
1543 let now = 1_000_000;
1544
1545 let mut out = build_out();
1547 registry.apply_multicast_rate_limit(&mut out, now, true);
1548 assert_eq!(out.answers_count(), 1);
1549
1550 let mut out = build_out();
1552 registry.apply_multicast_rate_limit(&mut out, now + 500, true);
1553 assert_eq!(out.answers_count(), 0);
1554
1555 let mut out = build_out();
1557 registry.apply_multicast_rate_limit(&mut out, now + 1000, true);
1558 assert_eq!(out.answers_count(), 1);
1559 }
1560
1561 #[test]
1567 fn test_multicast_rate_limit_per_family() {
1568 let mut registry = DnsRegistry::new();
1569
1570 let build_out = || {
1571 let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
1572 out.add_answer_at_time(
1573 DnsPointer::new(
1574 "_test._tcp.local.",
1575 RRType::PTR,
1576 CLASS_IN,
1577 4500,
1578 "inst._test._tcp.local.".to_string(),
1579 ),
1580 0,
1581 );
1582 out
1583 };
1584
1585 let now = 1_000_000;
1586
1587 let mut out = build_out();
1589 registry.apply_multicast_rate_limit(&mut out, now, true);
1590 assert_eq!(out.answers_count(), 1);
1591
1592 let mut out = build_out();
1595 registry.apply_multicast_rate_limit(&mut out, now, false);
1596 assert_eq!(out.answers_count(), 1);
1597
1598 let mut out = build_out();
1601 registry.apply_multicast_rate_limit(&mut out, now + 500, true);
1602 assert_eq!(out.answers_count(), 0);
1603
1604 let mut out = build_out();
1606 registry.apply_multicast_rate_limit(&mut out, now + 500, false);
1607 assert_eq!(out.answers_count(), 0);
1608 }
1609
1610 #[test]
1615 fn test_multicast_rate_limit_additionals_not_stamped_without_answer() {
1616 let mut registry = DnsRegistry::new();
1617
1618 let ptr_answer = || {
1619 DnsPointer::new(
1620 "_test._tcp.local.",
1621 RRType::PTR,
1622 CLASS_IN,
1623 4500,
1624 "inst._test._tcp.local.".to_string(),
1625 )
1626 };
1627 let extra = || {
1628 DnsPointer::new(
1629 "_other._tcp.local.",
1630 RRType::PTR,
1631 CLASS_IN,
1632 4500,
1633 "inst._other._tcp.local.".to_string(),
1634 )
1635 };
1636
1637 let now = 1_000_000;
1638
1639 let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
1641 out.add_answer_at_time(ptr_answer(), 0);
1642 registry.apply_multicast_rate_limit(&mut out, now, true);
1643 assert_eq!(out.answers_count(), 1);
1644
1645 let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
1648 out.add_answer_at_time(ptr_answer(), 0);
1649 out.add_additional_answer(extra());
1650 registry.apply_multicast_rate_limit(&mut out, now + 100, true);
1651 assert_eq!(out.answers_count(), 0);
1652
1653 let mut out = DnsOutgoing::new(FLAGS_QR_RESPONSE);
1657 out.add_answer_at_time(extra(), 0);
1658 registry.apply_multicast_rate_limit(&mut out, now + 200, true);
1659 assert_eq!(out.answers_count(), 1);
1660 }
1661
1662 #[test]
1663 fn test_txt_encode_decode() {
1664 let properties = [
1665 TxtProperty::from(&("key1", "value1")),
1666 TxtProperty::from(&("key2", "value2")),
1667 ];
1668
1669 let property_count = properties.len();
1671 let encoded = encode_txt(properties.iter());
1672 assert_eq!(
1673 encoded.len(),
1674 "key1=value1".len() + "key2=value2".len() + property_count
1675 );
1676 assert_eq!(encoded[0] as usize, "key1=value1".len());
1677
1678 let decoded = decode_txt(&encoded);
1680 assert_eq!(properties, decoded[..]);
1681
1682 let properties = vec![TxtProperty::from(&("key3", ""))];
1684 let property_count = properties.len();
1685 let encoded = encode_txt(properties.iter());
1686 assert_eq!(encoded.len(), "key3=".len() + property_count);
1687
1688 let decoded = decode_txt(&encoded);
1689 assert_eq!(properties, decoded);
1690
1691 let binary_val: Vec<u8> = vec![123, 234, 0];
1693 let binary_len = binary_val.len();
1694 let properties = vec![TxtProperty::from(("key4", binary_val))];
1695 let property_count = properties.len();
1696 let encoded = encode_txt(properties.iter());
1697 assert_eq!(encoded.len(), "key4=".len() + binary_len + property_count);
1698
1699 let decoded = decode_txt(&encoded);
1700 assert_eq!(properties, decoded);
1701
1702 let properties = vec![TxtProperty::from(("key5", "val=5"))];
1704 let property_count = properties.len();
1705 let encoded = encode_txt(properties.iter());
1706 assert_eq!(
1707 encoded.len(),
1708 "key5=".len() + "val=5".len() + property_count
1709 );
1710
1711 let decoded = decode_txt(&encoded);
1712 assert_eq!(properties, decoded);
1713
1714 let properties = vec![TxtProperty::from("key6")];
1716 let property_count = properties.len();
1717 let encoded = encode_txt(properties.iter());
1718 assert_eq!(encoded.len(), "key6".len() + property_count);
1719 let decoded = decode_txt(&encoded);
1720 assert_eq!(properties, decoded);
1721
1722 let properties = [TxtProperty::from(
1724 String::from_utf8(vec![0x30; 255]).unwrap().as_str(),
1725 )];
1726 let property_count = properties.len();
1727 let encoded = encode_txt(properties.iter());
1728 assert_eq!(encoded.len(), 255 + property_count);
1730 let decoded = decode_txt(&encoded);
1731 assert_eq!(properties.to_vec(), decoded);
1732 }
1733
1734 #[test]
1735 fn test_txt_property_exceeds_255_bytes() {
1736 let long_key = String::from_utf8(vec![0x30; 256]).unwrap();
1737 let result = ServiceInfo::new(
1738 "_test._tcp.local.",
1739 "test",
1740 "host",
1741 "",
1742 1234,
1743 &[(long_key.as_str(), "")][..],
1744 );
1745 assert!(result.is_err());
1746 assert!(result
1747 .unwrap_err()
1748 .to_string()
1749 .contains("exceeding the 255-byte limit"));
1750
1751 let key_at_limit = String::from_utf8(vec![0x30; 250]).unwrap();
1754 let result = ServiceInfo::new(
1755 "_test._tcp.local.",
1756 "test",
1757 "host",
1758 "",
1759 1234,
1760 &[(key_at_limit.as_str(), "abcd")][..],
1761 );
1762 assert!(result.is_ok());
1763 }
1764
1765 #[test]
1766 fn test_set_properties_from_txt() {
1767 let properties = [
1769 TxtProperty::from(&("one", "1")),
1770 TxtProperty::from(&("ONE", "2")),
1771 TxtProperty::from(&("One", "3")),
1772 ];
1773 let encoded = encode_txt(properties.iter());
1774
1775 let decoded = decode_txt(&encoded);
1777 assert_eq!(decoded.len(), 3);
1778
1779 let mut service_info =
1781 ServiceInfo::new("_test._tcp", "prop_test", "localhost", "", 1234, None).unwrap();
1782 service_info._set_properties_from_txt(&encoded);
1783 assert_eq!(service_info.get_properties().len(), 1);
1784
1785 let prop = service_info.get_properties().iter().next().unwrap();
1787 assert_eq!(prop.key, "one");
1788 assert_eq!(prop.val_str(), "1");
1789 }
1790
1791 #[test]
1792 fn test_u8_slice_to_hex() {
1793 let bytes = [0x01u8, 0x02u8, 0x03u8];
1794 let hex = u8_slice_to_hex(&bytes);
1795 assert_eq!(hex.as_str(), "0x010203");
1796
1797 let slice = "abcdefghijklmnopqrstuvwxyz";
1798 let hex = u8_slice_to_hex(slice.as_bytes());
1799 assert_eq!(hex.len(), slice.len() * 2 + 2);
1800 assert_eq!(
1801 hex.as_str(),
1802 "0x6162636465666768696a6b6c6d6e6f707172737475767778797a"
1803 );
1804 }
1805
1806 #[test]
1807 fn test_txt_property_debug() {
1808 let prop_1 = TxtProperty {
1810 key: "key1".to_string(),
1811 val: Some("val1".to_string().into()),
1812 };
1813 let prop_1_debug = format!("{:?}", &prop_1);
1814 assert_eq!(
1815 prop_1_debug,
1816 "TxtProperty {key: \"key1\", val: Some(\"val1\")}"
1817 );
1818
1819 let prop_2 = TxtProperty {
1821 key: "key2".to_string(),
1822 val: Some(vec![150u8, 151u8, 152u8]),
1823 };
1824 let prop_2_debug = format!("{:?}", &prop_2);
1825 assert_eq!(
1826 prop_2_debug,
1827 "TxtProperty {key: \"key2\", val: Some(0x969798)}"
1828 );
1829 }
1830
1831 #[test]
1832 fn test_txt_decode_property_size_out_of_bounds() {
1833 let encoded: Vec<u8> = vec![
1835 0x0b, b'k', b'e', b'y', b'1', b'=', b'v', b'a', b'l', b'u', b'e',
1837 b'1', 0x10, b'k', b'e', b'y', b'2', b'=', b'v', b'a', b'l', b'u', b'e',
1840 b'2', ];
1842 let decoded = decode_txt(&encoded);
1844 assert_eq!(decoded.len(), 1);
1847 assert_eq!(decoded[0].key, "key1");
1849 }
1850
1851 #[test]
1852 fn test_is_address_supported() {
1853 let mut service_info =
1854 ServiceInfo::new("_test._tcp", "prop_test", "testhost", "", 1234, None).unwrap();
1855
1856 let intf_v6 = Interface {
1857 name: "foo".to_string(),
1858 index: Some(1),
1859 addr: IfAddr::V6(Ifv6Addr {
1860 ip: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0x1234, 0, 0, 1),
1861 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1862 broadcast: None,
1863 prefixlen: 16,
1864 }),
1865 oper_status: IfOperStatus::Up,
1866 is_p2p: false,
1867 #[cfg(windows)]
1868 adapter_name: String::new(),
1869 };
1870
1871 let intf_v4 = Interface {
1872 name: "bar".to_string(),
1873 index: Some(1),
1874 addr: IfAddr::V4(Ifv4Addr {
1875 ip: Ipv4Addr::new(192, 1, 2, 3),
1876 netmask: Ipv4Addr::new(255, 255, 0, 0),
1877 broadcast: None,
1878 prefixlen: 16,
1879 }),
1880 oper_status: IfOperStatus::Up,
1881 is_p2p: false,
1882 #[cfg(windows)]
1883 adapter_name: String::new(),
1884 };
1885
1886 let intf_baz = Interface {
1887 name: "baz".to_string(),
1888 index: Some(1),
1889 addr: IfAddr::V6(Ifv6Addr {
1890 ip: Ipv6Addr::new(0x2003, 0xdb8, 0, 0, 0x1234, 0, 0, 1),
1891 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1892 broadcast: None,
1893 prefixlen: 16,
1894 }),
1895 oper_status: IfOperStatus::Up,
1896 is_p2p: false,
1897 #[cfg(windows)]
1898 adapter_name: String::new(),
1899 };
1900
1901 let intf_loopback_v4 = Interface {
1902 name: "foo".to_string(),
1903 index: Some(1),
1904 addr: IfAddr::V4(Ifv4Addr {
1905 ip: Ipv4Addr::new(127, 0, 0, 1),
1906 netmask: Ipv4Addr::new(255, 255, 255, 255),
1907 broadcast: None,
1908 prefixlen: 16,
1909 }),
1910 oper_status: IfOperStatus::Up,
1911 is_p2p: false,
1912 #[cfg(windows)]
1913 adapter_name: String::new(),
1914 };
1915
1916 let intf_loopback_v6 = Interface {
1917 name: "foo".to_string(),
1918 index: Some(1),
1919 addr: IfAddr::V6(Ifv6Addr {
1920 ip: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1),
1921 netmask: Ipv6Addr::new(
1922 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1923 ),
1924 broadcast: None,
1925 prefixlen: 16,
1926 }),
1927 oper_status: IfOperStatus::Up,
1928 is_p2p: false,
1929 #[cfg(windows)]
1930 adapter_name: String::new(),
1931 };
1932
1933 let intf_link_local_v4 = Interface {
1934 name: "foo".to_string(),
1935 index: Some(1),
1936 addr: IfAddr::V4(Ifv4Addr {
1937 ip: Ipv4Addr::new(169, 254, 0, 1),
1938 netmask: Ipv4Addr::new(255, 255, 0, 0),
1939 broadcast: None,
1940 prefixlen: 16,
1941 }),
1942 oper_status: IfOperStatus::Up,
1943 is_p2p: false,
1944 #[cfg(windows)]
1945 adapter_name: String::new(),
1946 };
1947
1948 let intf_link_local_v6 = Interface {
1949 name: "foo".to_string(),
1950 index: Some(1),
1951 addr: IfAddr::V6(Ifv6Addr {
1952 ip: Ipv6Addr::new(0xfe80, 0, 0, 0, 0x1234, 0, 0, 1),
1953 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1954 broadcast: None,
1955 prefixlen: 16,
1956 }),
1957 oper_status: IfOperStatus::Up,
1958 is_p2p: false,
1959 #[cfg(windows)]
1960 adapter_name: String::new(),
1961 };
1962
1963 assert!(service_info.is_address_supported(&intf_v6));
1965
1966 service_info.set_interfaces(vec![
1968 IfKind::Name("foo".to_string()),
1969 IfKind::Name("bar".to_string()),
1970 ]);
1971 assert!(!service_info.is_address_supported(&intf_baz));
1972
1973 service_info.set_link_local_only(true);
1975 assert!(!service_info.is_address_supported(&intf_v4));
1976 assert!(!service_info.is_address_supported(&intf_v6));
1977 assert!(service_info.is_address_supported(&intf_link_local_v4));
1978 assert!(service_info.is_address_supported(&intf_link_local_v6));
1979 service_info.set_link_local_only(false);
1980
1981 service_info.set_interfaces(vec![IfKind::All]);
1983 assert!(service_info.is_address_supported(&intf_v6));
1984 assert!(service_info.is_address_supported(&intf_v4));
1985
1986 service_info.set_interfaces(vec![IfKind::IPv6]);
1988 assert!(service_info.is_address_supported(&intf_v6));
1989 assert!(!service_info.is_address_supported(&intf_v4));
1990
1991 service_info.set_interfaces(vec![IfKind::IPv4]);
1993 assert!(service_info.is_address_supported(&intf_v4));
1994 assert!(!service_info.is_address_supported(&intf_v6));
1995
1996 service_info.set_interfaces(vec![IfKind::Addr(intf_v6.ip())]);
1998 assert!(service_info.is_address_supported(&intf_v6));
1999 assert!(!service_info.is_address_supported(&intf_v4));
2000
2001 service_info.set_interfaces(vec![IfKind::LoopbackV4]);
2003 assert!(service_info.is_address_supported(&intf_loopback_v4));
2004 assert!(!service_info.is_address_supported(&intf_loopback_v6));
2005
2006 service_info.set_interfaces(vec![IfKind::LoopbackV6]);
2008 assert!(!service_info.is_address_supported(&intf_loopback_v4));
2009 assert!(service_info.is_address_supported(&intf_loopback_v6));
2010
2011 service_info.set_interfaces(vec![IfKind::Predicate(IfPredicate::new(|intf| {
2013 intf.ip().is_ipv4() && intf.name == "foo"
2014 }))]);
2015 assert!(service_info.is_address_supported(&intf_loopback_v4));
2016 assert!(!service_info.is_address_supported(&intf_v4));
2017 assert!(!service_info.is_address_supported(&intf_loopback_v6));
2018 }
2019
2020 #[test]
2021 fn test_scoped_ip_set_detects_interface_id_change() {
2022 use crate::{InterfaceId, ScopedIp, ScopedIpV4};
2023 use std::collections::HashSet;
2024
2025 let intf1 = InterfaceId {
2026 name: "en0".to_string(),
2027 index: 1,
2028 };
2029 let intf2 = InterfaceId {
2030 name: "en1".to_string(),
2031 index: 2,
2032 };
2033 let addr = Ipv4Addr::new(192, 168, 1, 100);
2034
2035 let scoped_v4_one_intf = ScopedIpV4::new(addr, intf1);
2036 let mut scoped_v4_two_intfs = scoped_v4_one_intf.clone();
2037 scoped_v4_two_intfs.add_interface_id(intf2);
2038
2039 assert_ne!(scoped_v4_one_intf, scoped_v4_two_intfs);
2040
2041 let set_old: HashSet<ScopedIp> = HashSet::from([ScopedIp::V4(scoped_v4_one_intf)]);
2042 let set_new: HashSet<ScopedIp> = HashSet::from([ScopedIp::V4(scoped_v4_two_intfs)]);
2043
2044 assert_ne!(set_old, set_new);
2045 }
2046
2047 #[cfg(test)]
2048 #[cfg(feature = "serde")]
2049 mod serde {
2050 use super::{Ipv4Addr, Ipv6Addr};
2051 use crate::{ResolvedService, ScopedIp, TxtProperties};
2052
2053 use std::collections::HashSet;
2054 use std::net::IpAddr;
2055
2056 #[test]
2057 fn test_deserialize_serialize() -> Result<(), Box<dyn std::error::Error>> {
2058 let addresses = HashSet::from([
2059 ScopedIp::from(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
2060 ScopedIp::from(IpAddr::V6(Ipv6Addr::new(
2061 0xfe80, 0x2001, 0x0db8, 0x85a3, 0x0000, 0x8a2e, 0x0370, 0x7334,
2062 ))),
2063 ]);
2064
2065 let service = ResolvedService {
2066 ty_domain: "_http._tcp.local.".to_owned(),
2067 sub_ty_domain: None,
2068 fullname: "example._http._tcp.local.".to_owned(),
2069 host: "example.local.".to_owned(),
2070 port: 1234,
2071 addresses,
2072 txt_properties: TxtProperties::new(),
2073 };
2074
2075 let json = serde_json::to_value(&service)?;
2076
2077 let parsed: ResolvedService = serde_json::from_value(json)?;
2078
2079 assert!(compare(&service, &parsed));
2080
2081 Ok(())
2082 }
2083
2084 fn compare(service: &ResolvedService, other: &ResolvedService) -> bool {
2085 service.ty_domain == other.ty_domain
2086 && service.sub_ty_domain == other.sub_ty_domain
2087 && service.fullname == other.fullname
2088 && service.host == other.host
2089 && service.port == other.port
2090 && service.addresses == other.addresses
2091 && service.txt_properties == other.txt_properties
2092 }
2093 }
2094}