1#[cfg(feature = "logging")]
4use crate::log::debug;
5use crate::{
6 dns_parser::{DnsRecordBox, DnsRecordExt, DnsSrv, RRType},
7 Error, Result,
8};
9use if_addrs::{IfAddr, Interface};
10use std::{
11 cmp,
12 collections::{HashMap, HashSet},
13 convert::TryInto,
14 fmt,
15 net::{IpAddr, Ipv4Addr},
16 str::FromStr,
17};
18
19const DNS_HOST_TTL: u32 = 120; const DNS_OTHER_TTL: u32 = 4500; #[derive(Debug, Clone)]
28pub struct ServiceInfo {
29 ty_domain: String, sub_domain: Option<String>, fullname: String, server: String, addresses: HashSet<IpAddr>,
38 port: u16,
39 host_ttl: u32, other_ttl: u32, priority: u16,
42 weight: u16,
43 txt_properties: TxtProperties,
44 addr_auto: bool, status: HashMap<Interface, ServiceStatus>,
47
48 requires_probe: bool,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub(crate) enum ServiceStatus {
54 Probing,
55 Announced,
56 Unknown,
57}
58
59impl ServiceInfo {
60 pub fn new<Ip: AsIpAddrs, P: IntoTxtProperties>(
95 ty_domain: &str,
96 my_name: &str,
97 host_name: &str,
98 ip: Ip,
99 port: u16,
100 properties: P,
101 ) -> Result<Self> {
102 let (ty_domain, sub_domain) = split_sub_domain(ty_domain);
103
104 let fullname = format!("{}.{}", my_name, ty_domain);
105 let ty_domain = ty_domain.to_string();
106 let sub_domain = sub_domain.map(str::to_string);
107 let server = normalize_hostname(host_name.to_string());
108 let addresses = ip.as_ip_addrs()?;
109 let txt_properties = properties.into_txt_properties();
110
111 for prop in txt_properties.iter() {
115 let key = prop.key();
116 if !key.is_ascii() {
117 return Err(Error::Msg(format!(
118 "TXT property key {} is not ASCII",
119 prop.key()
120 )));
121 }
122 if key.contains('=') {
123 return Err(Error::Msg(format!(
124 "TXT property key {} contains '='",
125 prop.key()
126 )));
127 }
128 }
129
130 let this = Self {
131 ty_domain,
132 sub_domain,
133 fullname,
134 server,
135 addresses,
136 port,
137 host_ttl: DNS_HOST_TTL,
138 other_ttl: DNS_OTHER_TTL,
139 priority: 0,
140 weight: 0,
141 txt_properties,
142 addr_auto: false,
143 status: HashMap::new(),
144 requires_probe: true,
145 };
146
147 Ok(this)
148 }
149
150 pub const fn enable_addr_auto(mut self) -> Self {
154 self.addr_auto = true;
155 self
156 }
157
158 pub const fn is_addr_auto(&self) -> bool {
161 self.addr_auto
162 }
163
164 pub fn set_requires_probe(&mut self, enable: bool) {
169 self.requires_probe = enable;
170 }
171
172 pub const fn requires_probe(&self) -> bool {
176 self.requires_probe
177 }
178
179 #[inline]
183 pub fn get_type(&self) -> &str {
184 &self.ty_domain
185 }
186
187 #[inline]
192 pub const fn get_subtype(&self) -> &Option<String> {
193 &self.sub_domain
194 }
195
196 #[inline]
200 pub fn get_fullname(&self) -> &str {
201 &self.fullname
202 }
203
204 #[inline]
206 pub const fn get_properties(&self) -> &TxtProperties {
207 &self.txt_properties
208 }
209
210 pub fn get_property(&self, key: &str) -> Option<&TxtProperty> {
215 self.txt_properties.get(key)
216 }
217
218 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
223 self.txt_properties.get_property_val(key)
224 }
225
226 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
231 self.txt_properties.get_property_val_str(key)
232 }
233
234 #[inline]
236 pub fn get_hostname(&self) -> &str {
237 &self.server
238 }
239
240 #[inline]
242 pub const fn get_port(&self) -> u16 {
243 self.port
244 }
245
246 #[inline]
248 pub const fn get_addresses(&self) -> &HashSet<IpAddr> {
249 &self.addresses
250 }
251
252 pub fn get_addresses_v4(&self) -> HashSet<&Ipv4Addr> {
254 let mut ipv4_addresses = HashSet::new();
255
256 for ip in &self.addresses {
257 if let IpAddr::V4(ipv4) = ip {
258 ipv4_addresses.insert(ipv4);
259 }
260 }
261
262 ipv4_addresses
263 }
264
265 #[inline]
267 pub const fn get_host_ttl(&self) -> u32 {
268 self.host_ttl
269 }
270
271 #[inline]
273 pub const fn get_other_ttl(&self) -> u32 {
274 self.other_ttl
275 }
276
277 #[inline]
279 pub const fn get_priority(&self) -> u16 {
280 self.priority
281 }
282
283 #[inline]
285 pub const fn get_weight(&self) -> u16 {
286 self.weight
287 }
288
289 pub(crate) fn get_addrs_on_intf(&self, intf: &Interface) -> Vec<IpAddr> {
292 self.addresses
293 .iter()
294 .filter(|a| valid_ip_on_intf(a, intf))
295 .copied()
296 .collect()
297 }
298
299 pub(crate) fn is_ready(&self) -> bool {
301 let some_missing = self.ty_domain.is_empty()
302 || self.fullname.is_empty()
303 || self.server.is_empty()
304 || self.addresses.is_empty();
305 !some_missing
306 }
307
308 pub(crate) fn insert_ipaddr(&mut self, addr: IpAddr) {
310 self.addresses.insert(addr);
311 }
312
313 pub(crate) fn remove_ipaddr(&mut self, addr: &IpAddr) {
314 self.addresses.remove(addr);
315 }
316
317 pub(crate) fn generate_txt(&self) -> Vec<u8> {
318 encode_txt(self.get_properties().iter())
319 }
320
321 pub(crate) fn set_port(&mut self, port: u16) {
322 self.port = port;
323 }
324
325 pub(crate) fn set_hostname(&mut self, hostname: String) {
326 self.server = normalize_hostname(hostname);
327 }
328
329 pub(crate) fn set_properties_from_txt(&mut self, txt: &[u8]) -> bool {
331 let properties = decode_txt_unique(txt);
332 if self.txt_properties.properties != properties {
333 self.txt_properties = TxtProperties { properties };
334 true
335 } else {
336 false
337 }
338 }
339
340 pub(crate) fn set_subtype(&mut self, subtype: String) {
341 self.sub_domain = Some(subtype);
342 }
343
344 pub(crate) fn _set_host_ttl(&mut self, ttl: u32) {
347 self.host_ttl = ttl;
348 }
349
350 pub(crate) fn _set_other_ttl(&mut self, ttl: u32) {
352 self.other_ttl = ttl;
353 }
354
355 pub(crate) fn set_status(&mut self, intf: &Interface, status: ServiceStatus) {
356 match self.status.get_mut(intf) {
357 Some(service_status) => {
358 *service_status = status;
359 }
360 None => {
361 self.status.entry(intf.clone()).or_insert(status);
362 }
363 }
364 }
365
366 pub(crate) fn get_status(&self, intf: &Interface) -> ServiceStatus {
367 self.status
368 .get(intf)
369 .cloned()
370 .unwrap_or(ServiceStatus::Unknown)
371 }
372
373 pub fn as_resolved_service(self) -> ResolvedService {
375 ResolvedService {
376 ty_domain: self.ty_domain,
377 sub_ty_domain: self.sub_domain,
378 fullname: self.fullname,
379 host: self.server,
380 port: self.port,
381 addresses: self.addresses,
382 txt_properties: self.txt_properties,
383 }
384 }
385}
386
387fn normalize_hostname(mut hostname: String) -> String {
389 if hostname.ends_with(".local.local.") {
390 let new_len = hostname.len() - "local.".len();
391 hostname.truncate(new_len);
392 }
393 hostname
394}
395
396pub trait AsIpAddrs {
398 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>>;
399}
400
401impl<T: AsIpAddrs> AsIpAddrs for &T {
402 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
403 (*self).as_ip_addrs()
404 }
405}
406
407impl AsIpAddrs for &str {
412 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
413 let mut addrs = HashSet::new();
414
415 if !self.is_empty() {
416 let iter = self.split(',').map(str::trim).map(IpAddr::from_str);
417 for addr in iter {
418 let addr = addr.map_err(|err| Error::ParseIpAddr(err.to_string()))?;
419 addrs.insert(addr);
420 }
421 }
422
423 Ok(addrs)
424 }
425}
426
427impl AsIpAddrs for String {
428 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
429 self.as_str().as_ip_addrs()
430 }
431}
432
433impl<I: AsIpAddrs> AsIpAddrs for &[I] {
435 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
436 let mut addrs = HashSet::new();
437
438 for result in self.iter().map(I::as_ip_addrs) {
439 addrs.extend(result?);
440 }
441
442 Ok(addrs)
443 }
444}
445
446impl AsIpAddrs for () {
449 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
450 Ok(HashSet::new())
451 }
452}
453
454impl AsIpAddrs for std::net::IpAddr {
455 fn as_ip_addrs(&self) -> Result<HashSet<IpAddr>> {
456 let mut ips = HashSet::new();
457 ips.insert(*self);
458
459 Ok(ips)
460 }
461}
462
463#[derive(Debug, Clone, PartialEq, Eq)]
471pub struct TxtProperties {
472 properties: Vec<TxtProperty>,
474}
475
476impl TxtProperties {
477 pub fn iter(&self) -> impl Iterator<Item = &TxtProperty> {
479 self.properties.iter()
480 }
481
482 pub fn len(&self) -> usize {
484 self.properties.len()
485 }
486
487 pub fn is_empty(&self) -> bool {
489 self.properties.is_empty()
490 }
491
492 pub fn get(&self, key: &str) -> Option<&TxtProperty> {
495 let key = key.to_lowercase();
496 self.properties
497 .iter()
498 .find(|&prop| prop.key.to_lowercase() == key)
499 }
500
501 pub fn get_property_val(&self, key: &str) -> Option<Option<&[u8]>> {
507 self.get(key).map(|x| x.val())
508 }
509
510 pub fn get_property_val_str(&self, key: &str) -> Option<&str> {
516 self.get(key).map(|x| x.val_str())
517 }
518
519 pub fn into_property_map_str(self) -> HashMap<String, String> {
524 self.properties
525 .into_iter()
526 .filter_map(|property| {
527 let val_string = property.val.map_or(Some(String::new()), |val| {
528 String::from_utf8(val)
529 .map_err(|e| {
530 debug!("Property value contains invalid UTF-8: {e}");
531 })
532 .ok()
533 })?;
534 Some((property.key, val_string))
535 })
536 .collect()
537 }
538}
539
540impl fmt::Display for TxtProperties {
541 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542 let delimiter = ", ";
543 let props: Vec<String> = self.properties.iter().map(|p| p.to_string()).collect();
544 write!(f, "({})", props.join(delimiter))
545 }
546}
547
548#[derive(Clone, PartialEq, Eq)]
550pub struct TxtProperty {
551 key: String,
553
554 val: Option<Vec<u8>>,
558}
559
560impl TxtProperty {
561 pub fn key(&self) -> &str {
563 &self.key
564 }
565
566 pub fn val(&self) -> Option<&[u8]> {
570 self.val.as_deref()
571 }
572
573 pub fn val_str(&self) -> &str {
575 self.val
576 .as_ref()
577 .map_or("", |v| std::str::from_utf8(&v[..]).unwrap_or_default())
578 }
579}
580
581impl<K, V> From<&(K, V)> for TxtProperty
583where
584 K: ToString,
585 V: ToString,
586{
587 fn from(prop: &(K, V)) -> Self {
588 Self {
589 key: prop.0.to_string(),
590 val: Some(prop.1.to_string().into_bytes()),
591 }
592 }
593}
594
595impl<K, V> From<(K, V)> for TxtProperty
596where
597 K: ToString,
598 V: AsRef<[u8]>,
599{
600 fn from(prop: (K, V)) -> Self {
601 Self {
602 key: prop.0.to_string(),
603 val: Some(prop.1.as_ref().into()),
604 }
605 }
606}
607
608impl From<&str> for TxtProperty {
610 fn from(key: &str) -> Self {
611 Self {
612 key: key.to_string(),
613 val: None,
614 }
615 }
616}
617
618impl fmt::Display for TxtProperty {
619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620 write!(f, "{}={}", self.key, self.val_str())
621 }
622}
623
624impl fmt::Debug for TxtProperty {
628 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
629 let val_string = self.val.as_ref().map_or_else(
630 || "None".to_string(),
631 |v| {
632 std::str::from_utf8(&v[..]).map_or_else(
633 |_| format!("Some({})", u8_slice_to_hex(&v[..])),
634 |s| format!("Some(\"{}\")", s),
635 )
636 },
637 );
638
639 write!(
640 f,
641 "TxtProperty {{key: \"{}\", val: {}}}",
642 &self.key, &val_string,
643 )
644 }
645}
646
647const HEX_TABLE: [char; 16] = [
648 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
649];
650
651fn u8_slice_to_hex(slice: &[u8]) -> String {
655 let mut hex = String::with_capacity(slice.len() * 2 + 2);
656 hex.push_str("0x");
657 for b in slice {
658 hex.push(HEX_TABLE[(b >> 4) as usize]);
659 hex.push(HEX_TABLE[(b & 0x0F) as usize]);
660 }
661 hex
662}
663
664pub trait IntoTxtProperties {
666 fn into_txt_properties(self) -> TxtProperties;
667}
668
669impl IntoTxtProperties for HashMap<String, String> {
670 fn into_txt_properties(mut self) -> TxtProperties {
671 let properties = self
672 .drain()
673 .map(|(key, val)| TxtProperty {
674 key,
675 val: Some(val.into_bytes()),
676 })
677 .collect();
678 TxtProperties { properties }
679 }
680}
681
682impl IntoTxtProperties for Option<HashMap<String, String>> {
684 fn into_txt_properties(self) -> TxtProperties {
685 self.map_or_else(
686 || TxtProperties {
687 properties: Vec::new(),
688 },
689 |h| h.into_txt_properties(),
690 )
691 }
692}
693
694impl<'a, T: 'a> IntoTxtProperties for &'a [T]
696where
697 TxtProperty: From<&'a T>,
698{
699 fn into_txt_properties(self) -> TxtProperties {
700 let mut properties = Vec::new();
701 let mut keys = HashSet::new();
702 for t in self.iter() {
703 let prop = TxtProperty::from(t);
704 let key = prop.key.to_lowercase();
705 if keys.insert(key) {
706 properties.push(prop);
714 }
715 }
716 TxtProperties { properties }
717 }
718}
719
720impl IntoTxtProperties for Vec<TxtProperty> {
721 fn into_txt_properties(self) -> TxtProperties {
722 TxtProperties { properties: self }
723 }
724}
725
726fn encode_txt<'a>(properties: impl Iterator<Item = &'a TxtProperty>) -> Vec<u8> {
728 let mut bytes = Vec::new();
729 for prop in properties {
730 let mut s = prop.key.clone().into_bytes();
731 if let Some(v) = &prop.val {
732 s.extend(b"=");
733 s.extend(v);
734 }
735
736 let sz: u8 = s.len().try_into().unwrap_or_else(|_| {
738 debug!("Property {} is too long, truncating to 255 bytes", prop.key);
739 s.resize(u8::MAX as usize, 0);
740 u8::MAX
741 });
742
743 bytes.push(sz);
746 bytes.extend(s);
747 }
748 if bytes.is_empty() {
749 bytes.push(0);
750 }
751 bytes
752}
753
754pub(crate) fn decode_txt(txt: &[u8]) -> Vec<TxtProperty> {
756 let mut properties = Vec::new();
757 let mut offset = 0;
758 while offset < txt.len() {
759 let length = txt[offset] as usize;
760 if length == 0 {
761 break; }
763 offset += 1; let offset_end = offset + length;
766 if offset_end > txt.len() {
767 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());
768 break; }
770 let kv_bytes = &txt[offset..offset_end];
771
772 let (k, v) = kv_bytes.iter().position(|&x| x == b'=').map_or_else(
774 || (kv_bytes.to_vec(), None),
775 |idx| (kv_bytes[..idx].to_vec(), Some(kv_bytes[idx + 1..].to_vec())),
776 );
777
778 match String::from_utf8(k) {
780 Ok(k_string) => {
781 properties.push(TxtProperty {
782 key: k_string,
783 val: v,
784 });
785 }
786 Err(e) => debug!("failed to convert to String from key: {}", e),
787 }
788
789 offset += length;
790 }
791
792 properties
793}
794
795fn decode_txt_unique(txt: &[u8]) -> Vec<TxtProperty> {
796 let mut properties = decode_txt(txt);
797
798 let mut keys = HashSet::new();
801 properties.retain(|p| {
802 let key = p.key().to_lowercase();
803 keys.insert(key) });
805 properties
806}
807
808pub(crate) fn valid_ip_on_intf(addr: &IpAddr, intf: &Interface) -> bool {
810 match (addr, &intf.addr) {
811 (IpAddr::V4(addr), IfAddr::V4(intf)) => {
812 let netmask = u32::from(intf.netmask);
813 let intf_net = u32::from(intf.ip) & netmask;
814 let addr_net = u32::from(*addr) & netmask;
815 addr_net == intf_net
816 }
817 (IpAddr::V6(addr), IfAddr::V6(intf)) => {
818 let netmask = u128::from(intf.netmask);
819 let intf_net = u128::from(intf.ip) & netmask;
820 let addr_net = u128::from(*addr) & netmask;
821 addr_net == intf_net
822 }
823 _ => false,
824 }
825}
826
827pub fn valid_two_addrs_on_intf(addr_a: &IpAddr, addr_b: &IpAddr, intf: &Interface) -> bool {
829 match (addr_a, addr_b, &intf.addr) {
830 (IpAddr::V4(ipv4_a), IpAddr::V4(ipv4_b), IfAddr::V4(intf)) => {
831 let netmask = u32::from(intf.netmask);
832 let intf_net = u32::from(intf.ip) & netmask;
833 let net_a = u32::from(*ipv4_a) & netmask;
834 let net_b = u32::from(*ipv4_b) & netmask;
835 net_a == intf_net && net_b == intf_net
836 }
837 (IpAddr::V6(ipv6_a), IpAddr::V6(ipv6_b), IfAddr::V6(intf)) => {
838 let netmask = u128::from(intf.netmask);
839 let intf_net = u128::from(intf.ip) & netmask;
840 let net_a = u128::from(*ipv6_a) & netmask;
841 let net_b = u128::from(*ipv6_b) & netmask;
842 net_a == intf_net && net_b == intf_net
843 }
844 _ => false,
845 }
846}
847
848#[derive(Debug)]
850pub(crate) struct Probe {
851 pub(crate) records: Vec<DnsRecordBox>,
853
854 pub(crate) waiting_services: HashSet<String>,
857
858 pub(crate) start_time: u64,
860
861 pub(crate) next_send: u64,
863}
864
865impl Probe {
866 pub(crate) fn new(start_time: u64) -> Self {
867 let next_send = start_time;
874
875 Self {
876 records: Vec::new(),
877 waiting_services: HashSet::new(),
878 start_time,
879 next_send,
880 }
881 }
882
883 pub(crate) fn insert_record(&mut self, record: DnsRecordBox) {
885 let insert_position = self
895 .records
896 .binary_search_by(
897 |existing| match existing.get_class().cmp(&record.get_class()) {
898 std::cmp::Ordering::Equal => existing.get_type().cmp(&record.get_type()),
899 other => other,
900 },
901 )
902 .unwrap_or_else(|pos| pos);
903
904 self.records.insert(insert_position, record);
905 }
906
907 pub(crate) fn tiebreaking(&mut self, incoming: &[&DnsRecordBox], now: u64, probe_name: &str) {
909 let min_len = self.records.len().min(incoming.len());
919
920 let mut cmp_result = cmp::Ordering::Equal;
922 for (i, incoming_record) in incoming.iter().enumerate().take(min_len) {
923 match self.records[i].compare(incoming_record.as_ref()) {
924 cmp::Ordering::Equal => continue,
925 other => {
926 cmp_result = other;
927 break; }
929 }
930 }
931
932 if cmp_result == cmp::Ordering::Equal {
933 cmp_result = self.records.len().cmp(&incoming.len());
935 }
936
937 match cmp_result {
938 cmp::Ordering::Less => {
939 debug!("tiebreaking '{probe_name}': LOST, will wait for one second",);
940 self.start_time = now + 1000; self.next_send = now + 1000;
942 }
943 ordering => {
944 debug!("tiebreaking '{probe_name}': {:?}", ordering);
945 }
946 }
947 }
948
949 pub(crate) fn update_next_send(&mut self, now: u64) {
950 self.next_send = now + 250;
951 }
952
953 pub(crate) fn expired(&self, now: u64) -> bool {
955 now >= self.start_time + 750
958 }
959}
960
961pub(crate) struct DnsRegistry {
963 pub(crate) probing: HashMap<String, Probe>,
974
975 pub(crate) active: HashMap<String, Vec<DnsRecordBox>>,
977
978 pub(crate) new_timers: Vec<u64>,
980
981 pub(crate) name_changes: HashMap<String, String>,
983}
984
985impl DnsRegistry {
986 pub(crate) fn new() -> Self {
987 Self {
988 probing: HashMap::new(),
989 active: HashMap::new(),
990 new_timers: Vec::new(),
991 name_changes: HashMap::new(),
992 }
993 }
994
995 pub(crate) fn is_probing_done<T>(
996 &mut self,
997 answer: &T,
998 service_name: &str,
999 start_time: u64,
1000 ) -> bool
1001 where
1002 T: DnsRecordExt + Send + 'static,
1003 {
1004 if let Some(active_records) = self.active.get(answer.get_name()) {
1005 for record in active_records.iter() {
1006 if answer.matches(record.as_ref()) {
1007 debug!(
1008 "found active record {} {}",
1009 answer.get_type(),
1010 answer.get_name(),
1011 );
1012 return true;
1013 }
1014 }
1015 }
1016
1017 let probe = self
1018 .probing
1019 .entry(answer.get_name().to_string())
1020 .or_insert_with(|| {
1021 debug!("new probe of {}", answer.get_name());
1022 Probe::new(start_time)
1023 });
1024
1025 self.new_timers.push(probe.next_send);
1026
1027 for record in probe.records.iter() {
1028 if answer.matches(record.as_ref()) {
1029 debug!(
1030 "found existing record {} in probe of '{}'",
1031 answer.get_type(),
1032 answer.get_name(),
1033 );
1034 probe.waiting_services.insert(service_name.to_string());
1035 return false; }
1037 }
1038
1039 debug!(
1040 "insert record {} into probe of {}",
1041 answer.get_type(),
1042 answer.get_name(),
1043 );
1044 probe.insert_record(answer.clone_box());
1045 probe.waiting_services.insert(service_name.to_string());
1046
1047 false
1048 }
1049
1050 pub(crate) fn update_hostname(
1054 &mut self,
1055 original: &str,
1056 new_name: &str,
1057 probe_time: u64,
1058 ) -> bool {
1059 let mut found_records = Vec::new();
1060 let mut new_timer_added = false;
1061
1062 for (_name, probe) in self.probing.iter_mut() {
1063 probe.records.retain(|record| {
1064 if record.get_type() == RRType::SRV {
1065 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1066 if srv.host() == original {
1067 let mut new_record = srv.clone();
1068 new_record.set_host(new_name.to_string());
1069 found_records.push(new_record);
1070 return false;
1071 }
1072 }
1073 }
1074 true
1075 });
1076 }
1077
1078 for (_name, records) in self.active.iter_mut() {
1079 records.retain(|record| {
1080 if record.get_type() == RRType::SRV {
1081 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1082 if srv.host() == original {
1083 let mut new_record = srv.clone();
1084 new_record.set_host(new_name.to_string());
1085 found_records.push(new_record);
1086 return false;
1087 }
1088 }
1089 }
1090 true
1091 });
1092 }
1093
1094 for record in found_records {
1095 let probe = match self.probing.get_mut(record.get_name()) {
1096 Some(p) => {
1097 p.start_time = probe_time; p
1099 }
1100 None => {
1101 let new_probe = self
1102 .probing
1103 .entry(record.get_name().to_string())
1104 .or_insert_with(|| Probe::new(probe_time));
1105 new_timer_added = true;
1106 new_probe
1107 }
1108 };
1109
1110 debug!(
1111 "insert record {} with new hostname {new_name} into probe for: {}",
1112 record.get_type(),
1113 record.get_name()
1114 );
1115 probe.insert_record(record.boxed());
1116 }
1117
1118 new_timer_added
1119 }
1120}
1121
1122pub(crate) fn split_sub_domain(domain: &str) -> (&str, Option<&str>) {
1124 if let Some((_, ty_domain)) = domain.rsplit_once("._sub.") {
1125 (ty_domain, Some(domain))
1126 } else {
1127 (domain, None)
1128 }
1129}
1130
1131#[non_exhaustive]
1134pub struct ResolvedService {
1135 pub ty_domain: String,
1137
1138 pub sub_ty_domain: Option<String>,
1144
1145 pub fullname: String,
1147
1148 pub host: String,
1150
1151 pub port: u16,
1153
1154 pub addresses: HashSet<IpAddr>,
1156
1157 pub txt_properties: TxtProperties,
1159}
1160
1161impl ResolvedService {
1162 pub fn is_valid(&self) -> bool {
1164 let some_missing = self.ty_domain.is_empty()
1165 || self.fullname.is_empty()
1166 || self.host.is_empty()
1167 || self.addresses.is_empty();
1168 !some_missing
1169 }
1170}
1171
1172#[cfg(test)]
1173mod tests {
1174 use super::{
1175 decode_txt, encode_txt, u8_slice_to_hex, valid_two_addrs_on_intf, ServiceInfo, TxtProperty,
1176 };
1177 use if_addrs::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
1178 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
1179
1180 #[test]
1181 fn test_txt_encode_decode() {
1182 let properties = vec![
1183 TxtProperty::from(&("key1", "value1")),
1184 TxtProperty::from(&("key2", "value2")),
1185 ];
1186
1187 let property_count = properties.len();
1189 let encoded = encode_txt(properties.iter());
1190 assert_eq!(
1191 encoded.len(),
1192 "key1=value1".len() + "key2=value2".len() + property_count
1193 );
1194 assert_eq!(encoded[0] as usize, "key1=value1".len());
1195
1196 let decoded = decode_txt(&encoded);
1198 assert!(properties[..] == decoded[..]);
1199
1200 let properties = vec![TxtProperty::from(&("key3", ""))];
1202 let property_count = properties.len();
1203 let encoded = encode_txt(properties.iter());
1204 assert_eq!(encoded.len(), "key3=".len() + property_count);
1205
1206 let decoded = decode_txt(&encoded);
1207 assert_eq!(properties, decoded);
1208
1209 let binary_val: Vec<u8> = vec![123, 234, 0];
1211 let binary_len = binary_val.len();
1212 let properties = vec![TxtProperty::from(("key4", binary_val))];
1213 let property_count = properties.len();
1214 let encoded = encode_txt(properties.iter());
1215 assert_eq!(encoded.len(), "key4=".len() + binary_len + property_count);
1216
1217 let decoded = decode_txt(&encoded);
1218 assert_eq!(properties, decoded);
1219
1220 let properties = vec![TxtProperty::from(("key5", "val=5"))];
1222 let property_count = properties.len();
1223 let encoded = encode_txt(properties.iter());
1224 assert_eq!(
1225 encoded.len(),
1226 "key5=".len() + "val=5".len() + property_count
1227 );
1228
1229 let decoded = decode_txt(&encoded);
1230 assert_eq!(properties, decoded);
1231
1232 let properties = vec![TxtProperty::from("key6")];
1234 let property_count = properties.len();
1235 let encoded = encode_txt(properties.iter());
1236 assert_eq!(encoded.len(), "key6".len() + property_count);
1237 let decoded = decode_txt(&encoded);
1238 assert_eq!(properties, decoded);
1239
1240 let properties = vec![TxtProperty::from(
1242 String::from_utf8(vec![0x30; 1024]).unwrap().as_str(), )];
1244 let property_count = properties.len();
1245 let encoded = encode_txt(properties.iter());
1246 assert_eq!(encoded.len(), 255 + property_count);
1247 let decoded = decode_txt(&encoded);
1248 assert_eq!(
1249 vec![TxtProperty::from(
1250 String::from_utf8(vec![0x30; 255]).unwrap().as_str()
1251 )],
1252 decoded
1253 );
1254 }
1255
1256 #[test]
1257 fn test_set_properties_from_txt() {
1258 let properties = vec![
1260 TxtProperty::from(&("one", "1")),
1261 TxtProperty::from(&("ONE", "2")),
1262 TxtProperty::from(&("One", "3")),
1263 ];
1264 let encoded = encode_txt(properties.iter());
1265
1266 let decoded = decode_txt(&encoded);
1268 assert_eq!(decoded.len(), 3);
1269
1270 let mut service_info =
1272 ServiceInfo::new("_test._tcp", "prop_test", "localhost", "", 1234, None).unwrap();
1273 service_info.set_properties_from_txt(&encoded);
1274 assert_eq!(service_info.get_properties().len(), 1);
1275
1276 let prop = service_info.get_properties().iter().next().unwrap();
1278 assert_eq!(prop.key, "one");
1279 assert_eq!(prop.val_str(), "1");
1280 }
1281
1282 #[test]
1283 fn test_u8_slice_to_hex() {
1284 let bytes = [0x01u8, 0x02u8, 0x03u8];
1285 let hex = u8_slice_to_hex(&bytes);
1286 assert_eq!(hex.as_str(), "0x010203");
1287
1288 let slice = "abcdefghijklmnopqrstuvwxyz";
1289 let hex = u8_slice_to_hex(slice.as_bytes());
1290 assert_eq!(hex.len(), slice.len() * 2 + 2);
1291 assert_eq!(
1292 hex.as_str(),
1293 "0x6162636465666768696a6b6c6d6e6f707172737475767778797a"
1294 );
1295 }
1296
1297 #[test]
1298 fn test_txt_property_debug() {
1299 let prop_1 = TxtProperty {
1301 key: "key1".to_string(),
1302 val: Some("val1".to_string().into()),
1303 };
1304 let prop_1_debug = format!("{:?}", &prop_1);
1305 assert_eq!(
1306 prop_1_debug,
1307 "TxtProperty {key: \"key1\", val: Some(\"val1\")}"
1308 );
1309
1310 let prop_2 = TxtProperty {
1312 key: "key2".to_string(),
1313 val: Some(vec![150u8, 151u8, 152u8]),
1314 };
1315 let prop_2_debug = format!("{:?}", &prop_2);
1316 assert_eq!(
1317 prop_2_debug,
1318 "TxtProperty {key: \"key2\", val: Some(0x969798)}"
1319 );
1320 }
1321
1322 #[test]
1323 fn test_txt_decode_property_size_out_of_bounds() {
1324 let encoded: Vec<u8> = vec![
1326 0x0b, b'k', b'e', b'y', b'1', b'=', b'v', b'a', b'l', b'u', b'e',
1328 b'1', 0x10, b'k', b'e', b'y', b'2', b'=', b'v', b'a', b'l', b'u', b'e',
1331 b'2', ];
1333 let decoded = decode_txt(&encoded);
1335 assert_eq!(decoded.len(), 1);
1338 assert_eq!(decoded[0].key, "key1");
1340 }
1341
1342 #[test]
1343 fn test_valid_two_addrs_on_intf() {
1344 let ipv4_netmask = Ipv4Addr::new(192, 168, 1, 0);
1347 let ipv4_intf_addr = IfAddr::V4(Ifv4Addr {
1348 ip: Ipv4Addr::new(192, 168, 1, 10),
1349 netmask: ipv4_netmask,
1350 prefixlen: 24,
1351 broadcast: None,
1352 });
1353 let ipv4_intf = Interface {
1354 name: "e0".to_string(),
1355 addr: ipv4_intf_addr,
1356 index: Some(1),
1357 #[cfg(windows)]
1358 adapter_name: "ethernet".to_string(),
1359 };
1360 let ipv4_a = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10));
1361 let ipv4_b = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 11));
1362
1363 let result = valid_two_addrs_on_intf(&ipv4_a, &ipv4_b, &ipv4_intf);
1364 assert!(result);
1365
1366 let ipv4_c = IpAddr::V4(Ipv4Addr::new(172, 17, 0, 1));
1367 let result = valid_two_addrs_on_intf(&ipv4_a, &ipv4_c, &ipv4_intf);
1368 assert!(!result);
1369
1370 let ipv6_netmask = Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0); let ipv6_intf_addr = IfAddr::V6(Ifv6Addr {
1374 ip: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
1375 netmask: ipv6_netmask,
1376 prefixlen: 32,
1377 broadcast: None,
1378 });
1379 let ipv6_intf = Interface {
1380 name: "eth0".to_string(),
1381 addr: ipv6_intf_addr,
1382 index: Some(2),
1383 #[cfg(windows)]
1384 adapter_name: "ethernet".to_string(),
1385 };
1386 let ipv6_a = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
1387 let ipv6_b = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2));
1388
1389 let result = valid_two_addrs_on_intf(&ipv6_a, &ipv6_b, &ipv6_intf);
1390 assert!(result); let ipv6_c = IpAddr::V6(Ipv6Addr::new(0x2002, 0xdb8, 0, 0, 0, 0, 0, 1));
1393 let result = valid_two_addrs_on_intf(&ipv6_a, &ipv6_c, &ipv6_intf);
1394 assert!(!result); }
1396}