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
56fn escape_instance_name(name: &str) -> String {
65 let mut result = String::with_capacity(name.len() + 10); for ch in name.chars() {
68 match ch {
69 '.' => {
70 result.push('\\');
71 result.push('.');
72 }
73 '\\' => {
74 result.push('\\');
75 result.push('\\');
76 }
77 _ => result.push(ch),
78 }
79 }
80
81 result
82}
83
84#[derive(Debug, Clone)]
89pub struct ServiceInfo {
90 ty_domain: String,
94
95 sub_domain: Option<String>, fullname: String, server: String, addresses: HashSet<IpAddr>,
102 port: u16,
103 host_ttl: u32, other_ttl: u32, priority: u16,
106 weight: u16,
107 txt_properties: TxtProperties,
108 addr_auto: bool, status: HashMap<u32, ServiceStatus>, requires_probe: bool,
114
115 supported_intfs: Vec<IfKind>,
117
118 is_link_local_only: bool,
120}
121
122#[derive(Debug, Clone, PartialEq, Eq)]
123pub(crate) enum ServiceStatus {
124 Probing,
125 Announced,
126 Unknown,
127}
128
129impl ServiceInfo {
130 pub fn new<Ip: AsIpAddrs, P: IntoTxtProperties>(
166 ty_domain: &str,
167 my_name: &str,
168 host_name: &str,
169 ip: Ip,
170 port: u16,
171 properties: P,
172 ) -> Result<Self> {
173 let (ty_domain, sub_domain) = split_sub_domain(ty_domain);
174
175 let escaped_name = escape_instance_name(my_name);
176 let fullname = format!("{escaped_name}.{ty_domain}");
177 let ty_domain = ty_domain.to_string();
178 let sub_domain = sub_domain.map(str::to_string);
179 let server = normalize_hostname(host_name.to_string());
180 let addresses = ip.as_ip_addrs()?;
181 let txt_properties = properties.into_txt_properties();
182
183 for prop in txt_properties.iter() {
187 let key = prop.key();
188 if !key.is_ascii() {
189 return Err(Error::Msg(format!(
190 "TXT property key {} is not ASCII",
191 prop.key()
192 )));
193 }
194 if key.contains('=') {
195 return Err(Error::Msg(format!(
196 "TXT property key {} contains '='",
197 prop.key()
198 )));
199 }
200 }
201
202 let this = Self {
203 ty_domain,
204 sub_domain,
205 fullname,
206 server,
207 addresses,
208 port,
209 host_ttl: DNS_HOST_TTL,
210 other_ttl: DNS_OTHER_TTL,
211 priority: 0,
212 weight: 0,
213 txt_properties,
214 addr_auto: false,
215 status: HashMap::new(),
216 requires_probe: true,
217 is_link_local_only: false,
218 supported_intfs: vec![IfKind::All],
219 };
220
221 Ok(this)
222 }
223
224 pub const fn enable_addr_auto(mut self) -> Self {
228 self.addr_auto = true;
229 self
230 }
231
232 pub const fn is_addr_auto(&self) -> bool {
235 self.addr_auto
236 }
237
238 pub fn set_requires_probe(&mut self, enable: bool) {
243 self.requires_probe = enable;
244 }
245
246 pub fn set_link_local_only(&mut self, is_link_local_only: bool) {
250 self.is_link_local_only = is_link_local_only;
251 }
252
253 pub fn set_interfaces(&mut self, intfs: Vec<IfKind>) {
258 self.supported_intfs = intfs;
259 }
260
261 pub const fn requires_probe(&self) -> bool {
265 self.requires_probe
266 }
267
268 #[inline]
272 pub fn get_type(&self) -> &str {
273 &self.ty_domain
274 }
275
276 #[inline]
281 pub const fn get_subtype(&self) -> &Option<String> {
282 &self.sub_domain
283 }
284
285 #[inline]
289 pub fn get_fullname(&self) -> &str {
290 &self.fullname
291 }
292
293 #[inline]
295 pub const fn get_properties(&self) -> &TxtProperties {
296 &self.txt_properties
297 }
298
299 pub fn get_property(&self, key: &str) -> Option<&TxtProperty> {
304 self.txt_properties.get(key)
305 }
306
307 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
312 self.txt_properties.get_property_val(key)
313 }
314
315 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
320 self.txt_properties.get_property_val_str(key)
321 }
322
323 #[inline]
325 pub fn get_hostname(&self) -> &str {
326 &self.server
327 }
328
329 #[inline]
331 pub const fn get_port(&self) -> u16 {
332 self.port
333 }
334
335 #[inline]
337 pub const fn get_addresses(&self) -> &HashSet<IpAddr> {
338 &self.addresses
339 }
340
341 pub fn get_addresses_v4(&self) -> HashSet<&Ipv4Addr> {
343 let mut ipv4_addresses = HashSet::new();
344
345 for ip in &self.addresses {
346 if let IpAddr::V4(ipv4) = ip {
347 ipv4_addresses.insert(ipv4);
348 }
349 }
350
351 ipv4_addresses
352 }
353
354 #[inline]
356 pub const fn get_host_ttl(&self) -> u32 {
357 self.host_ttl
358 }
359
360 #[inline]
362 pub const fn get_other_ttl(&self) -> u32 {
363 self.other_ttl
364 }
365
366 #[inline]
368 pub const fn get_priority(&self) -> u16 {
369 self.priority
370 }
371
372 #[inline]
374 pub const fn get_weight(&self) -> u16 {
375 self.weight
376 }
377
378 pub(crate) fn get_addrs_on_my_intf_v4(&self, my_intf: &MyIntf) -> Vec<IpAddr> {
380 self.addresses
381 .iter()
382 .filter(|a| a.is_ipv4() && my_intf.addrs.iter().any(|x| valid_ip_on_intf(a, x)))
383 .copied()
384 .collect()
385 }
386
387 pub(crate) fn get_addrs_on_my_intf_v6(&self, my_intf: &MyIntf) -> Vec<IpAddr> {
388 self.addresses
389 .iter()
390 .filter(|a| a.is_ipv6() && my_intf.addrs.iter().any(|x| valid_ip_on_intf(a, x)))
391 .copied()
392 .collect()
393 }
394
395 pub(crate) fn _is_ready(&self) -> bool {
397 let some_missing = self.ty_domain.is_empty()
398 || self.fullname.is_empty()
399 || self.server.is_empty()
400 || self.addresses.is_empty();
401 !some_missing
402 }
403
404 pub(crate) fn insert_ipaddr(&mut self, intf: &Interface) {
406 if self.is_address_supported(intf) {
407 self.addresses.insert(intf.addr.ip());
408 } else {
409 trace!(
410 "skipping unsupported address {} for service {}",
411 intf.addr.ip(),
412 self.fullname
413 );
414 }
415 }
416
417 pub(crate) fn remove_ipaddr(&mut self, addr: &IpAddr) {
418 self.addresses.remove(addr);
419 }
420
421 pub(crate) fn generate_txt(&self) -> Vec<u8> {
422 encode_txt(self.get_properties().iter())
423 }
424
425 pub(crate) fn _set_port(&mut self, port: u16) {
426 self.port = port;
427 }
428
429 pub(crate) fn _set_hostname(&mut self, hostname: String) {
430 self.server = normalize_hostname(hostname);
431 }
432
433 pub(crate) fn _set_properties_from_txt(&mut self, txt: &[u8]) -> bool {
435 let properties = decode_txt_unique(txt);
436 if self.txt_properties.properties != properties {
437 self.txt_properties = TxtProperties { properties };
438 true
439 } else {
440 false
441 }
442 }
443
444 pub(crate) fn _set_subtype(&mut self, subtype: String) {
445 self.sub_domain = Some(subtype);
446 }
447
448 pub(crate) fn _set_host_ttl(&mut self, ttl: u32) {
451 self.host_ttl = ttl;
452 }
453
454 pub(crate) fn _set_other_ttl(&mut self, ttl: u32) {
456 self.other_ttl = ttl;
457 }
458
459 pub(crate) fn set_status(&mut self, if_index: u32, status: ServiceStatus) {
460 match self.status.get_mut(&if_index) {
461 Some(service_status) => {
462 *service_status = status;
463 }
464 None => {
465 self.status.entry(if_index).or_insert(status);
466 }
467 }
468 }
469
470 pub(crate) fn get_status(&self, intf: u32) -> ServiceStatus {
471 self.status
472 .get(&intf)
473 .cloned()
474 .unwrap_or(ServiceStatus::Unknown)
475 }
476
477 pub fn as_resolved_service(self) -> ResolvedService {
479 let addresses: HashSet<ScopedIp> = self.addresses.into_iter().map(|a| a.into()).collect();
480 ResolvedService {
481 ty_domain: self.ty_domain,
482 sub_ty_domain: self.sub_domain,
483 fullname: self.fullname,
484 host: self.server,
485 port: self.port,
486 addresses,
487 txt_properties: self.txt_properties,
488 }
489 }
490
491 fn is_address_supported(&self, intf: &Interface) -> bool {
492 let addr = intf.ip();
493 let interface_supported = self.supported_intfs.iter().any(|i| match i {
494 IfKind::Name(name) => *name == intf.name,
495 IfKind::IPv4 => addr.is_ipv4(),
496 IfKind::IPv6 => addr.is_ipv6(),
497 IfKind::Addr(a) => *a == addr,
498 IfKind::LoopbackV4 => matches!(addr, IpAddr::V4(ipv4) if ipv4.is_loopback()),
499 IfKind::LoopbackV6 => matches!(addr, IpAddr::V6(ipv6) if ipv6.is_loopback()),
500 IfKind::IndexV4(idx) => intf.index == Some(*idx) && addr.is_ipv4(),
501 IfKind::IndexV6(idx) => intf.index == Some(*idx) && addr.is_ipv6(),
502 IfKind::All => true,
503 });
504
505 let passes_link_local = !self.is_link_local_only
506 || match &addr {
507 IpAddr::V4(ipv4) => ipv4.is_link_local(),
508 IpAddr::V6(ipv6) => is_unicast_link_local(ipv6),
509 };
510 debug!(
511 "matching inserted address {} on intf {}: passes_link_local={}, interface_supported={}",
512 addr, addr, passes_link_local, interface_supported
513 );
514 interface_supported && passes_link_local
515 }
516}
517
518fn normalize_hostname(mut hostname: String) -> String {
520 if hostname.ends_with(".local.local.") {
521 let new_len = hostname.len() - "local.".len();
522 hostname.truncate(new_len);
523 }
524 hostname
525}
526
527pub trait AsIpAddrs {
529 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>>;
530}
531
532impl<T: AsIpAddrs> AsIpAddrs for &T {
533 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
534 (*self).as_ip_addrs()
535 }
536}
537
538impl AsIpAddrs for &str {
543 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
544 let mut addrs = HashSet::new();
545
546 if !self.is_empty() {
547 let iter = self.split(',').map(str::trim).map(IpAddr::from_str);
548 for addr in iter {
549 let addr = addr.map_err(|err| Error::ParseIpAddr(err.to_string()))?;
550 addrs.insert(addr);
551 }
552 }
553
554 Ok(addrs)
555 }
556}
557
558impl AsIpAddrs for String {
559 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
560 self.as_str().as_ip_addrs()
561 }
562}
563
564impl<I: AsIpAddrs> AsIpAddrs for &[I] {
566 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
567 let mut addrs = HashSet::new();
568
569 for result in self.iter().map(I::as_ip_addrs) {
570 addrs.extend(result?);
571 }
572
573 Ok(addrs)
574 }
575}
576
577impl AsIpAddrs for () {
580 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
581 Ok(HashSet::new())
582 }
583}
584
585impl AsIpAddrs for std::net::IpAddr {
586 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
587 let mut ips = HashSet::new();
588 ips.insert(*self);
589
590 Ok(ips)
591 }
592}
593
594impl AsIpAddrs for Box<dyn AsIpAddrs> {
595 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
596 self.as_ref().as_ip_addrs()
597 }
598}
599
600#[derive(Debug, Clone, PartialEq, Eq)]
608pub struct TxtProperties {
609 properties: Vec<TxtProperty>,
611}
612
613impl Default for TxtProperties {
614 fn default() -> Self {
615 TxtProperties::new()
616 }
617}
618
619impl TxtProperties {
620 pub fn new() -> Self {
621 TxtProperties {
622 properties: Vec::new(),
623 }
624 }
625
626 pub fn iter(&self) -> impl Iterator<Item = &TxtProperty> {
628 self.properties.iter()
629 }
630
631 pub fn len(&self) -> usize {
633 self.properties.len()
634 }
635
636 pub fn is_empty(&self) -> bool {
638 self.properties.is_empty()
639 }
640
641 pub fn get(&self, key: &str) -> Option<&TxtProperty> {
644 let key = key.to_lowercase();
645 self.properties
646 .iter()
647 .find(|&prop| prop.key.to_lowercase() == key)
648 }
649
650 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
656 self.get(key).map(|x| x.val())
657 }
658
659 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
665 self.get(key).map(|x| x.val_str())
666 }
667
668 pub fn into_property_map_str(self) -> HashMap<String, String> {
673 self.properties
674 .into_iter()
675 .filter_map(|property| {
676 let val_string = property.val.map_or(Some(String::new()), |val| {
677 String::from_utf8(val)
678 .map_err(|e| {
679 debug!("Property value contains invalid UTF-8: {e}");
680 })
681 .ok()
682 })?;
683 Some((property.key, val_string))
684 })
685 .collect()
686 }
687}
688
689impl fmt::Display for TxtProperties {
690 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
691 let delimiter = ", ";
692 let props: Vec<String> = self.properties.iter().map(|p| p.to_string()).collect();
693 write!(f, "({})", props.join(delimiter))
694 }
695}
696
697impl From<&[u8]> for TxtProperties {
698 fn from(txt: &[u8]) -> Self {
699 let properties = decode_txt_unique(txt);
700 TxtProperties { properties }
701 }
702}
703
704#[derive(Clone, PartialEq, Eq)]
706pub struct TxtProperty {
707 key: String,
709
710 val: Option<Vec<u8>>,
714}
715
716impl TxtProperty {
717 pub fn key(&self) -> &str {
719 &self.key
720 }
721
722 pub fn val(&self) -> Option<&[u8]> {
726 self.val.as_deref()
727 }
728
729 pub fn val_str(&self) -> &str {
731 self.val
732 .as_ref()
733 .map_or("", |v| std::str::from_utf8(&v[..]).unwrap_or_default())
734 }
735}
736
737impl<K, V> From<&(K, V)> for TxtProperty
739where
740 K: ToString,
741 V: ToString,
742{
743 fn from(prop: &(K, V)) -> Self {
744 Self {
745 key: prop.0.to_string(),
746 val: Some(prop.1.to_string().into_bytes()),
747 }
748 }
749}
750
751impl<K, V> From<(K, V)> for TxtProperty
752where
753 K: ToString,
754 V: AsRef<[u8]>,
755{
756 fn from(prop: (K, V)) -> Self {
757 Self {
758 key: prop.0.to_string(),
759 val: Some(prop.1.as_ref().into()),
760 }
761 }
762}
763
764impl From<&str> for TxtProperty {
766 fn from(key: &str) -> Self {
767 Self {
768 key: key.to_string(),
769 val: None,
770 }
771 }
772}
773
774impl fmt::Display for TxtProperty {
775 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
776 write!(f, "{}={}", self.key, self.val_str())
777 }
778}
779
780impl fmt::Debug for TxtProperty {
784 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
785 let val_string = self.val.as_ref().map_or_else(
786 || "None".to_string(),
787 |v| {
788 std::str::from_utf8(&v[..]).map_or_else(
789 |_| format!("Some({})", u8_slice_to_hex(&v[..])),
790 |s| format!("Some(\"{s}\")"),
791 )
792 },
793 );
794
795 write!(
796 f,
797 "TxtProperty {{key: \"{}\", val: {}}}",
798 &self.key, &val_string,
799 )
800 }
801}
802
803const HEX_TABLE: [char; 16] = [
804 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
805];
806
807fn u8_slice_to_hex(slice: &[u8]) -> String {
811 let mut hex = String::with_capacity(slice.len() * 2 + 2);
812 hex.push_str("0x");
813 for b in slice {
814 hex.push(HEX_TABLE[(b >> 4) as usize]);
815 hex.push(HEX_TABLE[(b & 0x0F) as usize]);
816 }
817 hex
818}
819
820pub trait IntoTxtProperties {
822 fn into_txt_properties(self) -> TxtProperties;
823}
824
825impl IntoTxtProperties for HashMap<String, String> {
826 fn into_txt_properties(mut self) -> TxtProperties {
827 let properties = self
828 .drain()
829 .map(|(key, val)| TxtProperty {
830 key,
831 val: Some(val.into_bytes()),
832 })
833 .collect();
834 TxtProperties { properties }
835 }
836}
837
838impl IntoTxtProperties for Option<HashMap<String, String>> {
840 fn into_txt_properties(self) -> TxtProperties {
841 self.map_or_else(
842 || TxtProperties {
843 properties: Vec::new(),
844 },
845 |h| h.into_txt_properties(),
846 )
847 }
848}
849
850impl<'a, T: 'a> IntoTxtProperties for &'a [T]
852where
853 TxtProperty: From<&'a T>,
854{
855 fn into_txt_properties(self) -> TxtProperties {
856 let mut properties = Vec::new();
857 let mut keys = HashSet::new();
858 for t in self.iter() {
859 let prop = TxtProperty::from(t);
860 let key = prop.key.to_lowercase();
861 if keys.insert(key) {
862 properties.push(prop);
870 }
871 }
872 TxtProperties { properties }
873 }
874}
875
876impl IntoTxtProperties for Vec<TxtProperty> {
877 fn into_txt_properties(self) -> TxtProperties {
878 TxtProperties { properties: self }
879 }
880}
881
882fn encode_txt<'a>(properties: impl Iterator<Item = &'a TxtProperty>) -> Vec<u8> {
884 let mut bytes = Vec::new();
885 for prop in properties {
886 let mut s = prop.key.clone().into_bytes();
887 if let Some(v) = &prop.val {
888 s.extend(b"=");
889 s.extend(v);
890 }
891
892 let sz: u8 = s.len().try_into().unwrap_or_else(|_| {
894 debug!("Property {} is too long, truncating to 255 bytes", prop.key);
895 s.resize(u8::MAX as usize, 0);
896 u8::MAX
897 });
898
899 bytes.push(sz);
902 bytes.extend(s);
903 }
904 if bytes.is_empty() {
905 bytes.push(0);
906 }
907 bytes
908}
909
910pub(crate) fn decode_txt(txt: &[u8]) -> Vec<TxtProperty> {
912 let mut properties = Vec::new();
913 let mut offset = 0;
914 while offset < txt.len() {
915 let length = txt[offset] as usize;
916 if length == 0 {
917 break; }
919 offset += 1; let offset_end = offset + length;
922 if offset_end > txt.len() {
923 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());
924 break; }
926 let kv_bytes = &txt[offset..offset_end];
927
928 let (k, v) = kv_bytes.iter().position(|&x| x == b'=').map_or_else(
930 || (kv_bytes.to_vec(), None),
931 |idx| (kv_bytes[..idx].to_vec(), Some(kv_bytes[idx + 1..].to_vec())),
932 );
933
934 match String::from_utf8(k) {
936 Ok(k_string) => {
937 properties.push(TxtProperty {
938 key: k_string,
939 val: v,
940 });
941 }
942 Err(e) => debug!("failed to convert to String from key: {}", e),
943 }
944
945 offset += length;
946 }
947
948 properties
949}
950
951fn decode_txt_unique(txt: &[u8]) -> Vec<TxtProperty> {
952 let mut properties = decode_txt(txt);
953
954 let mut keys = HashSet::new();
957 properties.retain(|p| {
958 let key = p.key().to_lowercase();
959 keys.insert(key) });
961 properties
962}
963
964pub(crate) fn valid_ip_on_intf(addr: &IpAddr, if_addr: &IfAddr) -> bool {
966 match (addr, if_addr) {
967 (IpAddr::V4(addr), IfAddr::V4(if_v4)) => {
968 let netmask = u32::from(if_v4.netmask);
969 let intf_net = u32::from(if_v4.ip) & netmask;
970 let addr_net = u32::from(*addr) & netmask;
971 addr_net == intf_net
972 }
973 (IpAddr::V6(addr), IfAddr::V6(if_v6)) => {
974 let netmask = u128::from(if_v6.netmask);
975 let intf_net = u128::from(if_v6.ip) & netmask;
976 let addr_net = u128::from(*addr) & netmask;
977 addr_net == intf_net
978 }
979 _ => false,
980 }
981}
982
983#[derive(Debug)]
985pub(crate) struct Probe {
986 pub(crate) records: Vec<DnsRecordBox>,
988
989 pub(crate) waiting_services: HashSet<String>,
992
993 pub(crate) start_time: u64,
995
996 pub(crate) next_send: u64,
998}
999
1000impl Probe {
1001 pub(crate) fn new(start_time: u64) -> Self {
1002 let next_send = start_time;
1009
1010 Self {
1011 records: Vec::new(),
1012 waiting_services: HashSet::new(),
1013 start_time,
1014 next_send,
1015 }
1016 }
1017
1018 pub(crate) fn insert_record(&mut self, record: DnsRecordBox) {
1020 let insert_position = self
1030 .records
1031 .binary_search_by(
1032 |existing| match existing.get_class().cmp(&record.get_class()) {
1033 std::cmp::Ordering::Equal => existing.get_type().cmp(&record.get_type()),
1034 other => other,
1035 },
1036 )
1037 .unwrap_or_else(|pos| pos);
1038
1039 self.records.insert(insert_position, record);
1040 }
1041
1042 pub(crate) fn tiebreaking(&mut self, incoming: &[&DnsRecordBox], now: u64, probe_name: &str) {
1044 let min_len = self.records.len().min(incoming.len());
1054
1055 let mut cmp_result = cmp::Ordering::Equal;
1057 for (i, incoming_record) in incoming.iter().enumerate().take(min_len) {
1058 match self.records[i].compare(incoming_record.as_ref()) {
1059 cmp::Ordering::Equal => continue,
1060 other => {
1061 cmp_result = other;
1062 break; }
1064 }
1065 }
1066
1067 if cmp_result == cmp::Ordering::Equal {
1068 cmp_result = self.records.len().cmp(&incoming.len());
1070 }
1071
1072 match cmp_result {
1073 cmp::Ordering::Less => {
1074 debug!("tiebreaking '{probe_name}': LOST, will wait for one second",);
1075 self.start_time = now + 1000; self.next_send = now + 1000;
1077 }
1078 ordering => {
1079 debug!("tiebreaking '{probe_name}': {:?}", ordering);
1080 }
1081 }
1082 }
1083
1084 pub(crate) fn update_next_send(&mut self, now: u64) {
1085 self.next_send = now + 250;
1086 }
1087
1088 pub(crate) fn expired(&self, now: u64) -> bool {
1090 now >= self.start_time + 750
1093 }
1094}
1095
1096pub(crate) struct DnsRegistry {
1098 pub(crate) probing: HashMap<String, Probe>,
1109
1110 pub(crate) active: HashMap<String, Vec<DnsRecordBox>>,
1113
1114 pub(crate) new_timers: Vec<u64>,
1116
1117 pub(crate) name_changes: HashMap<String, String>,
1119}
1120
1121impl DnsRegistry {
1122 pub(crate) fn new() -> Self {
1123 Self {
1124 probing: HashMap::new(),
1125 active: HashMap::new(),
1126 new_timers: Vec::new(),
1127 name_changes: HashMap::new(),
1128 }
1129 }
1130
1131 pub(crate) fn is_probing_done<T>(
1132 &mut self,
1133 answer: &T,
1134 service_name: &str,
1135 start_time: u64,
1136 ) -> bool
1137 where
1138 T: DnsRecordExt + Send + 'static,
1139 {
1140 if let Some(active_records) = self.active.get(answer.get_name()) {
1141 for record in active_records.iter() {
1142 if answer.matches(record.as_ref()) {
1143 debug!(
1144 "found active record {} {}",
1145 answer.get_type(),
1146 answer.get_name(),
1147 );
1148 return true;
1149 }
1150 }
1151 }
1152
1153 let probe = self
1154 .probing
1155 .entry(answer.get_name().to_string())
1156 .or_insert_with(|| {
1157 debug!("new probe of {}", answer.get_name());
1158 Probe::new(start_time)
1159 });
1160
1161 self.new_timers.push(probe.next_send);
1162
1163 for record in probe.records.iter() {
1164 if answer.matches(record.as_ref()) {
1165 debug!(
1166 "found existing record {} in probe of '{}'",
1167 answer.get_type(),
1168 answer.get_name(),
1169 );
1170 probe.waiting_services.insert(service_name.to_string());
1171 return false; }
1173 }
1174
1175 debug!(
1176 "insert record {} into probe of {}",
1177 answer.get_type(),
1178 answer.get_name(),
1179 );
1180 probe.insert_record(answer.clone_box());
1181 probe.waiting_services.insert(service_name.to_string());
1182
1183 false
1184 }
1185
1186 pub(crate) fn update_hostname(
1190 &mut self,
1191 original: &str,
1192 new_name: &str,
1193 probe_time: u64,
1194 ) -> bool {
1195 let mut found_records = Vec::new();
1196 let mut new_timer_added = false;
1197
1198 for (_name, probe) in self.probing.iter_mut() {
1199 probe.records.retain(|record| {
1200 if record.get_type() == RRType::SRV {
1201 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1202 if srv.host() == original {
1203 let mut new_record = srv.clone();
1204 new_record.set_host(new_name.to_string());
1205 found_records.push(new_record);
1206 return false;
1207 }
1208 }
1209 }
1210 true
1211 });
1212 }
1213
1214 for (_name, records) in self.active.iter_mut() {
1215 records.retain(|record| {
1216 if record.get_type() == RRType::SRV {
1217 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1218 if srv.host() == original {
1219 let mut new_record = srv.clone();
1220 new_record.set_host(new_name.to_string());
1221 found_records.push(new_record);
1222 return false;
1223 }
1224 }
1225 }
1226 true
1227 });
1228 }
1229
1230 for record in found_records {
1231 let probe = match self.probing.get_mut(record.get_name()) {
1232 Some(p) => {
1233 p.start_time = probe_time; p
1235 }
1236 None => {
1237 let new_probe = self
1238 .probing
1239 .entry(record.get_name().to_string())
1240 .or_insert_with(|| Probe::new(probe_time));
1241 new_timer_added = true;
1242 new_probe
1243 }
1244 };
1245
1246 debug!(
1247 "insert record {} with new hostname {new_name} into probe for: {}",
1248 record.get_type(),
1249 record.get_name()
1250 );
1251 probe.insert_record(record.boxed());
1252 }
1253
1254 new_timer_added
1255 }
1256}
1257
1258pub(crate) fn split_sub_domain(domain: &str) -> (&str, Option<&str>) {
1260 if let Some((_, ty_domain)) = domain.rsplit_once("._sub.") {
1261 (ty_domain, Some(domain))
1262 } else {
1263 (domain, None)
1264 }
1265}
1266
1267pub(crate) fn is_unicast_link_local(addr: &Ipv6Addr) -> bool {
1273 (addr.segments()[0] & 0xffc0) == 0xfe80
1274}
1275
1276#[derive(Clone, Debug)]
1279#[non_exhaustive]
1280pub struct ResolvedService {
1281 pub ty_domain: String,
1283
1284 pub sub_ty_domain: Option<String>,
1290
1291 pub fullname: String,
1293
1294 pub host: String,
1296
1297 pub port: u16,
1299
1300 pub addresses: HashSet<ScopedIp>,
1302
1303 pub txt_properties: TxtProperties,
1305}
1306
1307impl ResolvedService {
1308 pub fn is_valid(&self) -> bool {
1310 let some_missing = self.ty_domain.is_empty()
1311 || self.fullname.is_empty()
1312 || self.host.is_empty()
1313 || self.addresses.is_empty();
1314 !some_missing
1315 }
1316
1317 #[inline]
1318 pub const fn get_subtype(&self) -> &Option<String> {
1319 &self.sub_ty_domain
1320 }
1321
1322 #[inline]
1323 pub fn get_fullname(&self) -> &str {
1324 &self.fullname
1325 }
1326
1327 #[inline]
1328 pub fn get_hostname(&self) -> &str {
1329 &self.host
1330 }
1331
1332 #[inline]
1333 pub fn get_port(&self) -> u16 {
1334 self.port
1335 }
1336
1337 #[inline]
1338 pub fn get_addresses(&self) -> &HashSet<ScopedIp> {
1339 &self.addresses
1340 }
1341
1342 pub fn get_addresses_v4(&self) -> HashSet<Ipv4Addr> {
1343 self.addresses
1344 .iter()
1345 .filter_map(|ip| match ip {
1346 ScopedIp::V4(ipv4) => Some(*ipv4.addr()),
1347 _ => None,
1348 })
1349 .collect()
1350 }
1351
1352 #[inline]
1353 pub fn get_properties(&self) -> &TxtProperties {
1354 &self.txt_properties
1355 }
1356
1357 #[inline]
1358 pub fn get_property(&self, key: &str) -> Option<&TxtProperty> {
1359 self.txt_properties.get(key)
1360 }
1361
1362 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
1363 self.txt_properties.get_property_val(key)
1364 }
1365
1366 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
1367 self.txt_properties.get_property_val_str(key)
1368 }
1369}
1370
1371#[cfg(test)]
1372mod tests {
1373 use super::{decode_txt, encode_txt, u8_slice_to_hex, ServiceInfo, TxtProperty};
1374 use crate::IfKind;
1375 use if_addrs::{IfAddr, IfOperStatus, Ifv4Addr, Ifv6Addr, Interface};
1376 use std::net::{Ipv4Addr, Ipv6Addr};
1377
1378 #[test]
1379 fn test_txt_encode_decode() {
1380 let properties = [
1381 TxtProperty::from(&("key1", "value1")),
1382 TxtProperty::from(&("key2", "value2")),
1383 ];
1384
1385 let property_count = properties.len();
1387 let encoded = encode_txt(properties.iter());
1388 assert_eq!(
1389 encoded.len(),
1390 "key1=value1".len() + "key2=value2".len() + property_count
1391 );
1392 assert_eq!(encoded[0] as usize, "key1=value1".len());
1393
1394 let decoded = decode_txt(&encoded);
1396 assert!(properties[..] == decoded[..]);
1397
1398 let properties = vec![TxtProperty::from(&("key3", ""))];
1400 let property_count = properties.len();
1401 let encoded = encode_txt(properties.iter());
1402 assert_eq!(encoded.len(), "key3=".len() + property_count);
1403
1404 let decoded = decode_txt(&encoded);
1405 assert_eq!(properties, decoded);
1406
1407 let binary_val: Vec<u8> = vec![123, 234, 0];
1409 let binary_len = binary_val.len();
1410 let properties = vec![TxtProperty::from(("key4", binary_val))];
1411 let property_count = properties.len();
1412 let encoded = encode_txt(properties.iter());
1413 assert_eq!(encoded.len(), "key4=".len() + binary_len + property_count);
1414
1415 let decoded = decode_txt(&encoded);
1416 assert_eq!(properties, decoded);
1417
1418 let properties = vec![TxtProperty::from(("key5", "val=5"))];
1420 let property_count = properties.len();
1421 let encoded = encode_txt(properties.iter());
1422 assert_eq!(
1423 encoded.len(),
1424 "key5=".len() + "val=5".len() + property_count
1425 );
1426
1427 let decoded = decode_txt(&encoded);
1428 assert_eq!(properties, decoded);
1429
1430 let properties = vec![TxtProperty::from("key6")];
1432 let property_count = properties.len();
1433 let encoded = encode_txt(properties.iter());
1434 assert_eq!(encoded.len(), "key6".len() + property_count);
1435 let decoded = decode_txt(&encoded);
1436 assert_eq!(properties, decoded);
1437
1438 let properties = [TxtProperty::from(
1440 String::from_utf8(vec![0x30; 1024]).unwrap().as_str(), )];
1442 let property_count = properties.len();
1443 let encoded = encode_txt(properties.iter());
1444 assert_eq!(encoded.len(), 255 + property_count);
1445 let decoded = decode_txt(&encoded);
1446 assert_eq!(
1447 vec![TxtProperty::from(
1448 String::from_utf8(vec![0x30; 255]).unwrap().as_str()
1449 )],
1450 decoded
1451 );
1452 }
1453
1454 #[test]
1455 fn test_set_properties_from_txt() {
1456 let properties = [
1458 TxtProperty::from(&("one", "1")),
1459 TxtProperty::from(&("ONE", "2")),
1460 TxtProperty::from(&("One", "3")),
1461 ];
1462 let encoded = encode_txt(properties.iter());
1463
1464 let decoded = decode_txt(&encoded);
1466 assert_eq!(decoded.len(), 3);
1467
1468 let mut service_info =
1470 ServiceInfo::new("_test._tcp", "prop_test", "localhost", "", 1234, None).unwrap();
1471 service_info._set_properties_from_txt(&encoded);
1472 assert_eq!(service_info.get_properties().len(), 1);
1473
1474 let prop = service_info.get_properties().iter().next().unwrap();
1476 assert_eq!(prop.key, "one");
1477 assert_eq!(prop.val_str(), "1");
1478 }
1479
1480 #[test]
1481 fn test_u8_slice_to_hex() {
1482 let bytes = [0x01u8, 0x02u8, 0x03u8];
1483 let hex = u8_slice_to_hex(&bytes);
1484 assert_eq!(hex.as_str(), "0x010203");
1485
1486 let slice = "abcdefghijklmnopqrstuvwxyz";
1487 let hex = u8_slice_to_hex(slice.as_bytes());
1488 assert_eq!(hex.len(), slice.len() * 2 + 2);
1489 assert_eq!(
1490 hex.as_str(),
1491 "0x6162636465666768696a6b6c6d6e6f707172737475767778797a"
1492 );
1493 }
1494
1495 #[test]
1496 fn test_txt_property_debug() {
1497 let prop_1 = TxtProperty {
1499 key: "key1".to_string(),
1500 val: Some("val1".to_string().into()),
1501 };
1502 let prop_1_debug = format!("{:?}", &prop_1);
1503 assert_eq!(
1504 prop_1_debug,
1505 "TxtProperty {key: \"key1\", val: Some(\"val1\")}"
1506 );
1507
1508 let prop_2 = TxtProperty {
1510 key: "key2".to_string(),
1511 val: Some(vec![150u8, 151u8, 152u8]),
1512 };
1513 let prop_2_debug = format!("{:?}", &prop_2);
1514 assert_eq!(
1515 prop_2_debug,
1516 "TxtProperty {key: \"key2\", val: Some(0x969798)}"
1517 );
1518 }
1519
1520 #[test]
1521 fn test_txt_decode_property_size_out_of_bounds() {
1522 let encoded: Vec<u8> = vec![
1524 0x0b, b'k', b'e', b'y', b'1', b'=', b'v', b'a', b'l', b'u', b'e',
1526 b'1', 0x10, b'k', b'e', b'y', b'2', b'=', b'v', b'a', b'l', b'u', b'e',
1529 b'2', ];
1531 let decoded = decode_txt(&encoded);
1533 assert_eq!(decoded.len(), 1);
1536 assert_eq!(decoded[0].key, "key1");
1538 }
1539
1540 #[test]
1541 fn test_is_address_supported() {
1542 let mut service_info =
1543 ServiceInfo::new("_test._tcp", "prop_test", "testhost", "", 1234, None).unwrap();
1544
1545 let intf_v6 = Interface {
1546 name: "foo".to_string(),
1547 index: Some(1),
1548 addr: IfAddr::V6(Ifv6Addr {
1549 ip: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0x1234, 0, 0, 1),
1550 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1551 broadcast: None,
1552 prefixlen: 16,
1553 }),
1554 oper_status: IfOperStatus::Up,
1555 is_p2p: false,
1556 #[cfg(windows)]
1557 adapter_name: String::new(),
1558 };
1559
1560 let intf_v4 = Interface {
1561 name: "bar".to_string(),
1562 index: Some(1),
1563 addr: IfAddr::V4(Ifv4Addr {
1564 ip: Ipv4Addr::new(192, 1, 2, 3),
1565 netmask: Ipv4Addr::new(255, 255, 0, 0),
1566 broadcast: None,
1567 prefixlen: 16,
1568 }),
1569 oper_status: IfOperStatus::Up,
1570 is_p2p: false,
1571 #[cfg(windows)]
1572 adapter_name: String::new(),
1573 };
1574
1575 let intf_baz = Interface {
1576 name: "baz".to_string(),
1577 index: Some(1),
1578 addr: IfAddr::V6(Ifv6Addr {
1579 ip: Ipv6Addr::new(0x2003, 0xdb8, 0, 0, 0x1234, 0, 0, 1),
1580 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1581 broadcast: None,
1582 prefixlen: 16,
1583 }),
1584 oper_status: IfOperStatus::Up,
1585 is_p2p: false,
1586 #[cfg(windows)]
1587 adapter_name: String::new(),
1588 };
1589
1590 let intf_loopback_v4 = Interface {
1591 name: "foo".to_string(),
1592 index: Some(1),
1593 addr: IfAddr::V4(Ifv4Addr {
1594 ip: Ipv4Addr::new(127, 0, 0, 1),
1595 netmask: Ipv4Addr::new(255, 255, 255, 255),
1596 broadcast: None,
1597 prefixlen: 16,
1598 }),
1599 oper_status: IfOperStatus::Up,
1600 is_p2p: false,
1601 #[cfg(windows)]
1602 adapter_name: String::new(),
1603 };
1604
1605 let intf_loopback_v6 = Interface {
1606 name: "foo".to_string(),
1607 index: Some(1),
1608 addr: IfAddr::V6(Ifv6Addr {
1609 ip: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1),
1610 netmask: Ipv6Addr::new(
1611 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1612 ),
1613 broadcast: None,
1614 prefixlen: 16,
1615 }),
1616 oper_status: IfOperStatus::Up,
1617 is_p2p: false,
1618 #[cfg(windows)]
1619 adapter_name: String::new(),
1620 };
1621
1622 let intf_link_local_v4 = Interface {
1623 name: "foo".to_string(),
1624 index: Some(1),
1625 addr: IfAddr::V4(Ifv4Addr {
1626 ip: Ipv4Addr::new(169, 254, 0, 1),
1627 netmask: Ipv4Addr::new(255, 255, 0, 0),
1628 broadcast: None,
1629 prefixlen: 16,
1630 }),
1631 oper_status: IfOperStatus::Up,
1632 is_p2p: false,
1633 #[cfg(windows)]
1634 adapter_name: String::new(),
1635 };
1636
1637 let intf_link_local_v6 = Interface {
1638 name: "foo".to_string(),
1639 index: Some(1),
1640 addr: IfAddr::V6(Ifv6Addr {
1641 ip: Ipv6Addr::new(0xfe80, 0, 0, 0, 0x1234, 0, 0, 1),
1642 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1643 broadcast: None,
1644 prefixlen: 16,
1645 }),
1646 oper_status: IfOperStatus::Up,
1647 is_p2p: false,
1648 #[cfg(windows)]
1649 adapter_name: String::new(),
1650 };
1651
1652 assert!(service_info.is_address_supported(&intf_v6));
1654
1655 service_info.set_interfaces(vec![
1657 IfKind::Name("foo".to_string()),
1658 IfKind::Name("bar".to_string()),
1659 ]);
1660 assert!(!service_info.is_address_supported(&intf_baz));
1661
1662 service_info.set_link_local_only(true);
1664 assert!(!service_info.is_address_supported(&intf_v4));
1665 assert!(!service_info.is_address_supported(&intf_v6));
1666 assert!(service_info.is_address_supported(&intf_link_local_v4));
1667 assert!(service_info.is_address_supported(&intf_link_local_v6));
1668 service_info.set_link_local_only(false);
1669
1670 service_info.set_interfaces(vec![IfKind::All]);
1672 assert!(service_info.is_address_supported(&intf_v6));
1673 assert!(service_info.is_address_supported(&intf_v4));
1674
1675 service_info.set_interfaces(vec![IfKind::IPv6]);
1677 assert!(service_info.is_address_supported(&intf_v6));
1678 assert!(!service_info.is_address_supported(&intf_v4));
1679
1680 service_info.set_interfaces(vec![IfKind::IPv4]);
1682 assert!(service_info.is_address_supported(&intf_v4));
1683 assert!(!service_info.is_address_supported(&intf_v6));
1684
1685 service_info.set_interfaces(vec![IfKind::Addr(intf_v6.ip())]);
1687 assert!(service_info.is_address_supported(&intf_v6));
1688 assert!(!service_info.is_address_supported(&intf_v4));
1689
1690 service_info.set_interfaces(vec![IfKind::LoopbackV4]);
1692 assert!(service_info.is_address_supported(&intf_loopback_v4));
1693 assert!(!service_info.is_address_supported(&intf_loopback_v6));
1694
1695 service_info.set_interfaces(vec![IfKind::LoopbackV6]);
1697 assert!(!service_info.is_address_supported(&intf_loopback_v4));
1698 assert!(service_info.is_address_supported(&intf_loopback_v6));
1699 }
1700}