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::All => true,
501 });
502
503 let passes_link_local = !self.is_link_local_only
504 || match &addr {
505 IpAddr::V4(ipv4) => ipv4.is_link_local(),
506 IpAddr::V6(ipv6) => is_unicast_link_local(ipv6),
507 };
508 debug!(
509 "matching inserted address {} on intf {}: passes_link_local={}, interface_supported={}",
510 addr, addr, passes_link_local, interface_supported
511 );
512 interface_supported && passes_link_local
513 }
514}
515
516fn normalize_hostname(mut hostname: String) -> String {
518 if hostname.ends_with(".local.local.") {
519 let new_len = hostname.len() - "local.".len();
520 hostname.truncate(new_len);
521 }
522 hostname
523}
524
525pub trait AsIpAddrs {
527 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>>;
528}
529
530impl<T: AsIpAddrs> AsIpAddrs for &T {
531 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
532 (*self).as_ip_addrs()
533 }
534}
535
536impl AsIpAddrs for &str {
541 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
542 let mut addrs = HashSet::new();
543
544 if !self.is_empty() {
545 let iter = self.split(',').map(str::trim).map(IpAddr::from_str);
546 for addr in iter {
547 let addr = addr.map_err(|err| Error::ParseIpAddr(err.to_string()))?;
548 addrs.insert(addr);
549 }
550 }
551
552 Ok(addrs)
553 }
554}
555
556impl AsIpAddrs for String {
557 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
558 self.as_str().as_ip_addrs()
559 }
560}
561
562impl<I: AsIpAddrs> AsIpAddrs for &[I] {
564 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
565 let mut addrs = HashSet::new();
566
567 for result in self.iter().map(I::as_ip_addrs) {
568 addrs.extend(result?);
569 }
570
571 Ok(addrs)
572 }
573}
574
575impl AsIpAddrs for () {
578 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
579 Ok(HashSet::new())
580 }
581}
582
583impl AsIpAddrs for std::net::IpAddr {
584 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
585 let mut ips = HashSet::new();
586 ips.insert(*self);
587
588 Ok(ips)
589 }
590}
591
592impl AsIpAddrs for Box<dyn AsIpAddrs> {
593 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
594 self.as_ref().as_ip_addrs()
595 }
596}
597
598#[derive(Debug, Clone, PartialEq, Eq)]
606pub struct TxtProperties {
607 properties: Vec<TxtProperty>,
609}
610
611impl Default for TxtProperties {
612 fn default() -> Self {
613 TxtProperties::new()
614 }
615}
616
617impl TxtProperties {
618 pub fn new() -> Self {
619 TxtProperties {
620 properties: Vec::new(),
621 }
622 }
623
624 pub fn iter(&self) -> impl Iterator<Item = &TxtProperty> {
626 self.properties.iter()
627 }
628
629 pub fn len(&self) -> usize {
631 self.properties.len()
632 }
633
634 pub fn is_empty(&self) -> bool {
636 self.properties.is_empty()
637 }
638
639 pub fn get(&self, key: &str) -> Option<&TxtProperty> {
642 let key = key.to_lowercase();
643 self.properties
644 .iter()
645 .find(|&prop| prop.key.to_lowercase() == key)
646 }
647
648 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
654 self.get(key).map(|x| x.val())
655 }
656
657 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
663 self.get(key).map(|x| x.val_str())
664 }
665
666 pub fn into_property_map_str(self) -> HashMap<String, String> {
671 self.properties
672 .into_iter()
673 .filter_map(|property| {
674 let val_string = property.val.map_or(Some(String::new()), |val| {
675 String::from_utf8(val)
676 .map_err(|e| {
677 debug!("Property value contains invalid UTF-8: {e}");
678 })
679 .ok()
680 })?;
681 Some((property.key, val_string))
682 })
683 .collect()
684 }
685}
686
687impl fmt::Display for TxtProperties {
688 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
689 let delimiter = ", ";
690 let props: Vec<String> = self.properties.iter().map(|p| p.to_string()).collect();
691 write!(f, "({})", props.join(delimiter))
692 }
693}
694
695impl From<&[u8]> for TxtProperties {
696 fn from(txt: &[u8]) -> Self {
697 let properties = decode_txt_unique(txt);
698 TxtProperties { properties }
699 }
700}
701
702#[derive(Clone, PartialEq, Eq)]
704pub struct TxtProperty {
705 key: String,
707
708 val: Option<Vec<u8>>,
712}
713
714impl TxtProperty {
715 pub fn key(&self) -> &str {
717 &self.key
718 }
719
720 pub fn val(&self) -> Option<&[u8]> {
724 self.val.as_deref()
725 }
726
727 pub fn val_str(&self) -> &str {
729 self.val
730 .as_ref()
731 .map_or("", |v| std::str::from_utf8(&v[..]).unwrap_or_default())
732 }
733}
734
735impl<K, V> From<&(K, V)> for TxtProperty
737where
738 K: ToString,
739 V: ToString,
740{
741 fn from(prop: &(K, V)) -> Self {
742 Self {
743 key: prop.0.to_string(),
744 val: Some(prop.1.to_string().into_bytes()),
745 }
746 }
747}
748
749impl<K, V> From<(K, V)> for TxtProperty
750where
751 K: ToString,
752 V: AsRef<[u8]>,
753{
754 fn from(prop: (K, V)) -> Self {
755 Self {
756 key: prop.0.to_string(),
757 val: Some(prop.1.as_ref().into()),
758 }
759 }
760}
761
762impl From<&str> for TxtProperty {
764 fn from(key: &str) -> Self {
765 Self {
766 key: key.to_string(),
767 val: None,
768 }
769 }
770}
771
772impl fmt::Display for TxtProperty {
773 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
774 write!(f, "{}={}", self.key, self.val_str())
775 }
776}
777
778impl fmt::Debug for TxtProperty {
782 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
783 let val_string = self.val.as_ref().map_or_else(
784 || "None".to_string(),
785 |v| {
786 std::str::from_utf8(&v[..]).map_or_else(
787 |_| format!("Some({})", u8_slice_to_hex(&v[..])),
788 |s| format!("Some(\"{s}\")"),
789 )
790 },
791 );
792
793 write!(
794 f,
795 "TxtProperty {{key: \"{}\", val: {}}}",
796 &self.key, &val_string,
797 )
798 }
799}
800
801const HEX_TABLE: [char; 16] = [
802 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
803];
804
805fn u8_slice_to_hex(slice: &[u8]) -> String {
809 let mut hex = String::with_capacity(slice.len() * 2 + 2);
810 hex.push_str("0x");
811 for b in slice {
812 hex.push(HEX_TABLE[(b >> 4) as usize]);
813 hex.push(HEX_TABLE[(b & 0x0F) as usize]);
814 }
815 hex
816}
817
818pub trait IntoTxtProperties {
820 fn into_txt_properties(self) -> TxtProperties;
821}
822
823impl IntoTxtProperties for HashMap<String, String> {
824 fn into_txt_properties(mut self) -> TxtProperties {
825 let properties = self
826 .drain()
827 .map(|(key, val)| TxtProperty {
828 key,
829 val: Some(val.into_bytes()),
830 })
831 .collect();
832 TxtProperties { properties }
833 }
834}
835
836impl IntoTxtProperties for Option<HashMap<String, String>> {
838 fn into_txt_properties(self) -> TxtProperties {
839 self.map_or_else(
840 || TxtProperties {
841 properties: Vec::new(),
842 },
843 |h| h.into_txt_properties(),
844 )
845 }
846}
847
848impl<'a, T: 'a> IntoTxtProperties for &'a [T]
850where
851 TxtProperty: From<&'a T>,
852{
853 fn into_txt_properties(self) -> TxtProperties {
854 let mut properties = Vec::new();
855 let mut keys = HashSet::new();
856 for t in self.iter() {
857 let prop = TxtProperty::from(t);
858 let key = prop.key.to_lowercase();
859 if keys.insert(key) {
860 properties.push(prop);
868 }
869 }
870 TxtProperties { properties }
871 }
872}
873
874impl IntoTxtProperties for Vec<TxtProperty> {
875 fn into_txt_properties(self) -> TxtProperties {
876 TxtProperties { properties: self }
877 }
878}
879
880fn encode_txt<'a>(properties: impl Iterator<Item = &'a TxtProperty>) -> Vec<u8> {
882 let mut bytes = Vec::new();
883 for prop in properties {
884 let mut s = prop.key.clone().into_bytes();
885 if let Some(v) = &prop.val {
886 s.extend(b"=");
887 s.extend(v);
888 }
889
890 let sz: u8 = s.len().try_into().unwrap_or_else(|_| {
892 debug!("Property {} is too long, truncating to 255 bytes", prop.key);
893 s.resize(u8::MAX as usize, 0);
894 u8::MAX
895 });
896
897 bytes.push(sz);
900 bytes.extend(s);
901 }
902 if bytes.is_empty() {
903 bytes.push(0);
904 }
905 bytes
906}
907
908pub(crate) fn decode_txt(txt: &[u8]) -> Vec<TxtProperty> {
910 let mut properties = Vec::new();
911 let mut offset = 0;
912 while offset < txt.len() {
913 let length = txt[offset] as usize;
914 if length == 0 {
915 break; }
917 offset += 1; let offset_end = offset + length;
920 if offset_end > txt.len() {
921 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());
922 break; }
924 let kv_bytes = &txt[offset..offset_end];
925
926 let (k, v) = kv_bytes.iter().position(|&x| x == b'=').map_or_else(
928 || (kv_bytes.to_vec(), None),
929 |idx| (kv_bytes[..idx].to_vec(), Some(kv_bytes[idx + 1..].to_vec())),
930 );
931
932 match String::from_utf8(k) {
934 Ok(k_string) => {
935 properties.push(TxtProperty {
936 key: k_string,
937 val: v,
938 });
939 }
940 Err(e) => debug!("failed to convert to String from key: {}", e),
941 }
942
943 offset += length;
944 }
945
946 properties
947}
948
949fn decode_txt_unique(txt: &[u8]) -> Vec<TxtProperty> {
950 let mut properties = decode_txt(txt);
951
952 let mut keys = HashSet::new();
955 properties.retain(|p| {
956 let key = p.key().to_lowercase();
957 keys.insert(key) });
959 properties
960}
961
962pub(crate) fn valid_ip_on_intf(addr: &IpAddr, if_addr: &IfAddr) -> bool {
964 match (addr, if_addr) {
965 (IpAddr::V4(addr), IfAddr::V4(if_v4)) => {
966 let netmask = u32::from(if_v4.netmask);
967 let intf_net = u32::from(if_v4.ip) & netmask;
968 let addr_net = u32::from(*addr) & netmask;
969 addr_net == intf_net
970 }
971 (IpAddr::V6(addr), IfAddr::V6(if_v6)) => {
972 let netmask = u128::from(if_v6.netmask);
973 let intf_net = u128::from(if_v6.ip) & netmask;
974 let addr_net = u128::from(*addr) & netmask;
975 addr_net == intf_net
976 }
977 _ => false,
978 }
979}
980
981#[derive(Debug)]
983pub(crate) struct Probe {
984 pub(crate) records: Vec<DnsRecordBox>,
986
987 pub(crate) waiting_services: HashSet<String>,
990
991 pub(crate) start_time: u64,
993
994 pub(crate) next_send: u64,
996}
997
998impl Probe {
999 pub(crate) fn new(start_time: u64) -> Self {
1000 let next_send = start_time;
1007
1008 Self {
1009 records: Vec::new(),
1010 waiting_services: HashSet::new(),
1011 start_time,
1012 next_send,
1013 }
1014 }
1015
1016 pub(crate) fn insert_record(&mut self, record: DnsRecordBox) {
1018 let insert_position = self
1028 .records
1029 .binary_search_by(
1030 |existing| match existing.get_class().cmp(&record.get_class()) {
1031 std::cmp::Ordering::Equal => existing.get_type().cmp(&record.get_type()),
1032 other => other,
1033 },
1034 )
1035 .unwrap_or_else(|pos| pos);
1036
1037 self.records.insert(insert_position, record);
1038 }
1039
1040 pub(crate) fn tiebreaking(&mut self, incoming: &[&DnsRecordBox], now: u64, probe_name: &str) {
1042 let min_len = self.records.len().min(incoming.len());
1052
1053 let mut cmp_result = cmp::Ordering::Equal;
1055 for (i, incoming_record) in incoming.iter().enumerate().take(min_len) {
1056 match self.records[i].compare(incoming_record.as_ref()) {
1057 cmp::Ordering::Equal => continue,
1058 other => {
1059 cmp_result = other;
1060 break; }
1062 }
1063 }
1064
1065 if cmp_result == cmp::Ordering::Equal {
1066 cmp_result = self.records.len().cmp(&incoming.len());
1068 }
1069
1070 match cmp_result {
1071 cmp::Ordering::Less => {
1072 debug!("tiebreaking '{probe_name}': LOST, will wait for one second",);
1073 self.start_time = now + 1000; self.next_send = now + 1000;
1075 }
1076 ordering => {
1077 debug!("tiebreaking '{probe_name}': {:?}", ordering);
1078 }
1079 }
1080 }
1081
1082 pub(crate) fn update_next_send(&mut self, now: u64) {
1083 self.next_send = now + 250;
1084 }
1085
1086 pub(crate) fn expired(&self, now: u64) -> bool {
1088 now >= self.start_time + 750
1091 }
1092}
1093
1094pub(crate) struct DnsRegistry {
1096 pub(crate) probing: HashMap<String, Probe>,
1107
1108 pub(crate) active: HashMap<String, Vec<DnsRecordBox>>,
1111
1112 pub(crate) new_timers: Vec<u64>,
1114
1115 pub(crate) name_changes: HashMap<String, String>,
1117}
1118
1119impl DnsRegistry {
1120 pub(crate) fn new() -> Self {
1121 Self {
1122 probing: HashMap::new(),
1123 active: HashMap::new(),
1124 new_timers: Vec::new(),
1125 name_changes: HashMap::new(),
1126 }
1127 }
1128
1129 pub(crate) fn is_probing_done<T>(
1130 &mut self,
1131 answer: &T,
1132 service_name: &str,
1133 start_time: u64,
1134 ) -> bool
1135 where
1136 T: DnsRecordExt + Send + 'static,
1137 {
1138 if let Some(active_records) = self.active.get(answer.get_name()) {
1139 for record in active_records.iter() {
1140 if answer.matches(record.as_ref()) {
1141 debug!(
1142 "found active record {} {}",
1143 answer.get_type(),
1144 answer.get_name(),
1145 );
1146 return true;
1147 }
1148 }
1149 }
1150
1151 let probe = self
1152 .probing
1153 .entry(answer.get_name().to_string())
1154 .or_insert_with(|| {
1155 debug!("new probe of {}", answer.get_name());
1156 Probe::new(start_time)
1157 });
1158
1159 self.new_timers.push(probe.next_send);
1160
1161 for record in probe.records.iter() {
1162 if answer.matches(record.as_ref()) {
1163 debug!(
1164 "found existing record {} in probe of '{}'",
1165 answer.get_type(),
1166 answer.get_name(),
1167 );
1168 probe.waiting_services.insert(service_name.to_string());
1169 return false; }
1171 }
1172
1173 debug!(
1174 "insert record {} into probe of {}",
1175 answer.get_type(),
1176 answer.get_name(),
1177 );
1178 probe.insert_record(answer.clone_box());
1179 probe.waiting_services.insert(service_name.to_string());
1180
1181 false
1182 }
1183
1184 pub(crate) fn update_hostname(
1188 &mut self,
1189 original: &str,
1190 new_name: &str,
1191 probe_time: u64,
1192 ) -> bool {
1193 let mut found_records = Vec::new();
1194 let mut new_timer_added = false;
1195
1196 for (_name, probe) in self.probing.iter_mut() {
1197 probe.records.retain(|record| {
1198 if record.get_type() == RRType::SRV {
1199 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1200 if srv.host() == original {
1201 let mut new_record = srv.clone();
1202 new_record.set_host(new_name.to_string());
1203 found_records.push(new_record);
1204 return false;
1205 }
1206 }
1207 }
1208 true
1209 });
1210 }
1211
1212 for (_name, records) in self.active.iter_mut() {
1213 records.retain(|record| {
1214 if record.get_type() == RRType::SRV {
1215 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1216 if srv.host() == original {
1217 let mut new_record = srv.clone();
1218 new_record.set_host(new_name.to_string());
1219 found_records.push(new_record);
1220 return false;
1221 }
1222 }
1223 }
1224 true
1225 });
1226 }
1227
1228 for record in found_records {
1229 let probe = match self.probing.get_mut(record.get_name()) {
1230 Some(p) => {
1231 p.start_time = probe_time; p
1233 }
1234 None => {
1235 let new_probe = self
1236 .probing
1237 .entry(record.get_name().to_string())
1238 .or_insert_with(|| Probe::new(probe_time));
1239 new_timer_added = true;
1240 new_probe
1241 }
1242 };
1243
1244 debug!(
1245 "insert record {} with new hostname {new_name} into probe for: {}",
1246 record.get_type(),
1247 record.get_name()
1248 );
1249 probe.insert_record(record.boxed());
1250 }
1251
1252 new_timer_added
1253 }
1254}
1255
1256pub(crate) fn split_sub_domain(domain: &str) -> (&str, Option<&str>) {
1258 if let Some((_, ty_domain)) = domain.rsplit_once("._sub.") {
1259 (ty_domain, Some(domain))
1260 } else {
1261 (domain, None)
1262 }
1263}
1264
1265pub(crate) fn is_unicast_link_local(addr: &Ipv6Addr) -> bool {
1271 (addr.segments()[0] & 0xffc0) == 0xfe80
1272}
1273
1274#[derive(Clone, Debug)]
1277#[non_exhaustive]
1278pub struct ResolvedService {
1279 pub ty_domain: String,
1281
1282 pub sub_ty_domain: Option<String>,
1288
1289 pub fullname: String,
1291
1292 pub host: String,
1294
1295 pub port: u16,
1297
1298 pub addresses: HashSet<ScopedIp>,
1300
1301 pub txt_properties: TxtProperties,
1303}
1304
1305impl ResolvedService {
1306 pub fn is_valid(&self) -> bool {
1308 let some_missing = self.ty_domain.is_empty()
1309 || self.fullname.is_empty()
1310 || self.host.is_empty()
1311 || self.addresses.is_empty();
1312 !some_missing
1313 }
1314
1315 #[inline]
1316 pub const fn get_subtype(&self) -> &Option<String> {
1317 &self.sub_ty_domain
1318 }
1319
1320 #[inline]
1321 pub fn get_fullname(&self) -> &str {
1322 &self.fullname
1323 }
1324
1325 #[inline]
1326 pub fn get_hostname(&self) -> &str {
1327 &self.host
1328 }
1329
1330 #[inline]
1331 pub fn get_port(&self) -> u16 {
1332 self.port
1333 }
1334
1335 #[inline]
1336 pub fn get_addresses(&self) -> &HashSet<ScopedIp> {
1337 &self.addresses
1338 }
1339
1340 pub fn get_addresses_v4(&self) -> HashSet<Ipv4Addr> {
1341 self.addresses
1342 .iter()
1343 .filter_map(|ip| match ip {
1344 ScopedIp::V4(ipv4) => Some(*ipv4.addr()),
1345 _ => None,
1346 })
1347 .collect()
1348 }
1349
1350 #[inline]
1351 pub fn get_properties(&self) -> &TxtProperties {
1352 &self.txt_properties
1353 }
1354
1355 #[inline]
1356 pub fn get_property(&self, key: &str) -> Option<&TxtProperty> {
1357 self.txt_properties.get(key)
1358 }
1359
1360 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
1361 self.txt_properties.get_property_val(key)
1362 }
1363
1364 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
1365 self.txt_properties.get_property_val_str(key)
1366 }
1367}
1368
1369#[cfg(test)]
1370mod tests {
1371 use super::{decode_txt, encode_txt, u8_slice_to_hex, ServiceInfo, TxtProperty};
1372 use crate::IfKind;
1373 use if_addrs::{IfAddr, IfOperStatus, Ifv4Addr, Ifv6Addr, Interface};
1374 use std::net::{Ipv4Addr, Ipv6Addr};
1375
1376 #[test]
1377 fn test_txt_encode_decode() {
1378 let properties = [
1379 TxtProperty::from(&("key1", "value1")),
1380 TxtProperty::from(&("key2", "value2")),
1381 ];
1382
1383 let property_count = properties.len();
1385 let encoded = encode_txt(properties.iter());
1386 assert_eq!(
1387 encoded.len(),
1388 "key1=value1".len() + "key2=value2".len() + property_count
1389 );
1390 assert_eq!(encoded[0] as usize, "key1=value1".len());
1391
1392 let decoded = decode_txt(&encoded);
1394 assert!(properties[..] == decoded[..]);
1395
1396 let properties = vec![TxtProperty::from(&("key3", ""))];
1398 let property_count = properties.len();
1399 let encoded = encode_txt(properties.iter());
1400 assert_eq!(encoded.len(), "key3=".len() + property_count);
1401
1402 let decoded = decode_txt(&encoded);
1403 assert_eq!(properties, decoded);
1404
1405 let binary_val: Vec<u8> = vec![123, 234, 0];
1407 let binary_len = binary_val.len();
1408 let properties = vec![TxtProperty::from(("key4", binary_val))];
1409 let property_count = properties.len();
1410 let encoded = encode_txt(properties.iter());
1411 assert_eq!(encoded.len(), "key4=".len() + binary_len + property_count);
1412
1413 let decoded = decode_txt(&encoded);
1414 assert_eq!(properties, decoded);
1415
1416 let properties = vec![TxtProperty::from(("key5", "val=5"))];
1418 let property_count = properties.len();
1419 let encoded = encode_txt(properties.iter());
1420 assert_eq!(
1421 encoded.len(),
1422 "key5=".len() + "val=5".len() + property_count
1423 );
1424
1425 let decoded = decode_txt(&encoded);
1426 assert_eq!(properties, decoded);
1427
1428 let properties = vec![TxtProperty::from("key6")];
1430 let property_count = properties.len();
1431 let encoded = encode_txt(properties.iter());
1432 assert_eq!(encoded.len(), "key6".len() + property_count);
1433 let decoded = decode_txt(&encoded);
1434 assert_eq!(properties, decoded);
1435
1436 let properties = [TxtProperty::from(
1438 String::from_utf8(vec![0x30; 1024]).unwrap().as_str(), )];
1440 let property_count = properties.len();
1441 let encoded = encode_txt(properties.iter());
1442 assert_eq!(encoded.len(), 255 + property_count);
1443 let decoded = decode_txt(&encoded);
1444 assert_eq!(
1445 vec![TxtProperty::from(
1446 String::from_utf8(vec![0x30; 255]).unwrap().as_str()
1447 )],
1448 decoded
1449 );
1450 }
1451
1452 #[test]
1453 fn test_set_properties_from_txt() {
1454 let properties = [
1456 TxtProperty::from(&("one", "1")),
1457 TxtProperty::from(&("ONE", "2")),
1458 TxtProperty::from(&("One", "3")),
1459 ];
1460 let encoded = encode_txt(properties.iter());
1461
1462 let decoded = decode_txt(&encoded);
1464 assert_eq!(decoded.len(), 3);
1465
1466 let mut service_info =
1468 ServiceInfo::new("_test._tcp", "prop_test", "localhost", "", 1234, None).unwrap();
1469 service_info._set_properties_from_txt(&encoded);
1470 assert_eq!(service_info.get_properties().len(), 1);
1471
1472 let prop = service_info.get_properties().iter().next().unwrap();
1474 assert_eq!(prop.key, "one");
1475 assert_eq!(prop.val_str(), "1");
1476 }
1477
1478 #[test]
1479 fn test_u8_slice_to_hex() {
1480 let bytes = [0x01u8, 0x02u8, 0x03u8];
1481 let hex = u8_slice_to_hex(&bytes);
1482 assert_eq!(hex.as_str(), "0x010203");
1483
1484 let slice = "abcdefghijklmnopqrstuvwxyz";
1485 let hex = u8_slice_to_hex(slice.as_bytes());
1486 assert_eq!(hex.len(), slice.len() * 2 + 2);
1487 assert_eq!(
1488 hex.as_str(),
1489 "0x6162636465666768696a6b6c6d6e6f707172737475767778797a"
1490 );
1491 }
1492
1493 #[test]
1494 fn test_txt_property_debug() {
1495 let prop_1 = TxtProperty {
1497 key: "key1".to_string(),
1498 val: Some("val1".to_string().into()),
1499 };
1500 let prop_1_debug = format!("{:?}", &prop_1);
1501 assert_eq!(
1502 prop_1_debug,
1503 "TxtProperty {key: \"key1\", val: Some(\"val1\")}"
1504 );
1505
1506 let prop_2 = TxtProperty {
1508 key: "key2".to_string(),
1509 val: Some(vec![150u8, 151u8, 152u8]),
1510 };
1511 let prop_2_debug = format!("{:?}", &prop_2);
1512 assert_eq!(
1513 prop_2_debug,
1514 "TxtProperty {key: \"key2\", val: Some(0x969798)}"
1515 );
1516 }
1517
1518 #[test]
1519 fn test_txt_decode_property_size_out_of_bounds() {
1520 let encoded: Vec<u8> = vec![
1522 0x0b, b'k', b'e', b'y', b'1', b'=', b'v', b'a', b'l', b'u', b'e',
1524 b'1', 0x10, b'k', b'e', b'y', b'2', b'=', b'v', b'a', b'l', b'u', b'e',
1527 b'2', ];
1529 let decoded = decode_txt(&encoded);
1531 assert_eq!(decoded.len(), 1);
1534 assert_eq!(decoded[0].key, "key1");
1536 }
1537
1538 #[test]
1539 fn test_is_address_supported() {
1540 let mut service_info =
1541 ServiceInfo::new("_test._tcp", "prop_test", "testhost", "", 1234, None).unwrap();
1542
1543 let intf_v6 = Interface {
1544 name: "foo".to_string(),
1545 index: Some(1),
1546 addr: IfAddr::V6(Ifv6Addr {
1547 ip: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0x1234, 0, 0, 1),
1548 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1549 broadcast: None,
1550 prefixlen: 16,
1551 }),
1552 oper_status: IfOperStatus::Up,
1553 is_p2p: false,
1554 #[cfg(windows)]
1555 adapter_name: String::new(),
1556 };
1557
1558 let intf_v4 = Interface {
1559 name: "bar".to_string(),
1560 index: Some(1),
1561 addr: IfAddr::V4(Ifv4Addr {
1562 ip: Ipv4Addr::new(192, 1, 2, 3),
1563 netmask: Ipv4Addr::new(255, 255, 0, 0),
1564 broadcast: None,
1565 prefixlen: 16,
1566 }),
1567 oper_status: IfOperStatus::Up,
1568 is_p2p: false,
1569 #[cfg(windows)]
1570 adapter_name: String::new(),
1571 };
1572
1573 let intf_baz = Interface {
1574 name: "baz".to_string(),
1575 index: Some(1),
1576 addr: IfAddr::V6(Ifv6Addr {
1577 ip: Ipv6Addr::new(0x2003, 0xdb8, 0, 0, 0x1234, 0, 0, 1),
1578 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1579 broadcast: None,
1580 prefixlen: 16,
1581 }),
1582 oper_status: IfOperStatus::Up,
1583 is_p2p: false,
1584 #[cfg(windows)]
1585 adapter_name: String::new(),
1586 };
1587
1588 let intf_loopback_v4 = Interface {
1589 name: "foo".to_string(),
1590 index: Some(1),
1591 addr: IfAddr::V4(Ifv4Addr {
1592 ip: Ipv4Addr::new(127, 0, 0, 1),
1593 netmask: Ipv4Addr::new(255, 255, 255, 255),
1594 broadcast: None,
1595 prefixlen: 16,
1596 }),
1597 oper_status: IfOperStatus::Up,
1598 is_p2p: false,
1599 #[cfg(windows)]
1600 adapter_name: String::new(),
1601 };
1602
1603 let intf_loopback_v6 = Interface {
1604 name: "foo".to_string(),
1605 index: Some(1),
1606 addr: IfAddr::V6(Ifv6Addr {
1607 ip: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1),
1608 netmask: Ipv6Addr::new(
1609 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1610 ),
1611 broadcast: None,
1612 prefixlen: 16,
1613 }),
1614 oper_status: IfOperStatus::Up,
1615 is_p2p: false,
1616 #[cfg(windows)]
1617 adapter_name: String::new(),
1618 };
1619
1620 let intf_link_local_v4 = Interface {
1621 name: "foo".to_string(),
1622 index: Some(1),
1623 addr: IfAddr::V4(Ifv4Addr {
1624 ip: Ipv4Addr::new(169, 254, 0, 1),
1625 netmask: Ipv4Addr::new(255, 255, 0, 0),
1626 broadcast: None,
1627 prefixlen: 16,
1628 }),
1629 oper_status: IfOperStatus::Up,
1630 is_p2p: false,
1631 #[cfg(windows)]
1632 adapter_name: String::new(),
1633 };
1634
1635 let intf_link_local_v6 = Interface {
1636 name: "foo".to_string(),
1637 index: Some(1),
1638 addr: IfAddr::V6(Ifv6Addr {
1639 ip: Ipv6Addr::new(0xfe80, 0, 0, 0, 0x1234, 0, 0, 1),
1640 netmask: Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0),
1641 broadcast: None,
1642 prefixlen: 16,
1643 }),
1644 oper_status: IfOperStatus::Up,
1645 is_p2p: false,
1646 #[cfg(windows)]
1647 adapter_name: String::new(),
1648 };
1649
1650 assert!(service_info.is_address_supported(&intf_v6));
1652
1653 service_info.set_interfaces(vec![
1655 IfKind::Name("foo".to_string()),
1656 IfKind::Name("bar".to_string()),
1657 ]);
1658 assert!(!service_info.is_address_supported(&intf_baz));
1659
1660 service_info.set_link_local_only(true);
1662 assert!(!service_info.is_address_supported(&intf_v4));
1663 assert!(!service_info.is_address_supported(&intf_v6));
1664 assert!(service_info.is_address_supported(&intf_link_local_v4));
1665 assert!(service_info.is_address_supported(&intf_link_local_v6));
1666 service_info.set_link_local_only(false);
1667
1668 service_info.set_interfaces(vec![IfKind::All]);
1670 assert!(service_info.is_address_supported(&intf_v6));
1671 assert!(service_info.is_address_supported(&intf_v4));
1672
1673 service_info.set_interfaces(vec![IfKind::IPv6]);
1675 assert!(service_info.is_address_supported(&intf_v6));
1676 assert!(!service_info.is_address_supported(&intf_v4));
1677
1678 service_info.set_interfaces(vec![IfKind::IPv4]);
1680 assert!(service_info.is_address_supported(&intf_v4));
1681 assert!(!service_info.is_address_supported(&intf_v6));
1682
1683 service_info.set_interfaces(vec![IfKind::Addr(intf_v6.ip())]);
1685 assert!(service_info.is_address_supported(&intf_v6));
1686 assert!(!service_info.is_address_supported(&intf_v4));
1687
1688 service_info.set_interfaces(vec![IfKind::LoopbackV4]);
1690 assert!(service_info.is_address_supported(&intf_loopback_v4));
1691 assert!(!service_info.is_address_supported(&intf_loopback_v6));
1692
1693 service_info.set_interfaces(vec![IfKind::LoopbackV6]);
1695 assert!(!service_info.is_address_supported(&intf_loopback_v4));
1696 assert!(service_info.is_address_supported(&intf_loopback_v6));
1697 }
1698}