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(&self, incoming: &[&DnsRecordBox]) -> cmp::Ordering {
909 let min_len = self.records.len().min(incoming.len());
918
919 for (i, incoming_record) in incoming.iter().enumerate().take(min_len) {
921 match self.records[i].compare(incoming_record.as_ref()) {
922 cmp::Ordering::Equal => continue,
923 other => return other,
924 }
925 }
926
927 self.records.len().cmp(&incoming.len())
928 }
929
930 pub(crate) fn update_next_send(&mut self, now: u64) {
931 self.next_send = now + 250;
932 }
933
934 pub(crate) fn expired(&self, now: u64) -> bool {
936 now >= self.start_time + 750
939 }
940}
941
942pub(crate) struct DnsRegistry {
944 pub(crate) probing: HashMap<String, Probe>,
955
956 pub(crate) active: HashMap<String, Vec<DnsRecordBox>>,
958
959 pub(crate) new_timers: Vec<u64>,
961
962 pub(crate) name_changes: HashMap<String, String>,
964}
965
966impl DnsRegistry {
967 pub(crate) fn new() -> Self {
968 Self {
969 probing: HashMap::new(),
970 active: HashMap::new(),
971 new_timers: Vec::new(),
972 name_changes: HashMap::new(),
973 }
974 }
975
976 pub(crate) fn is_probing_done<T>(
977 &mut self,
978 answer: &T,
979 service_name: &str,
980 start_time: u64,
981 ) -> bool
982 where
983 T: DnsRecordExt + Send + 'static,
984 {
985 if let Some(active_records) = self.active.get(answer.get_name()) {
986 for record in active_records.iter() {
987 if answer.matches(record.as_ref()) {
988 debug!(
989 "found active record {} {}",
990 answer.get_type(),
991 answer.get_name(),
992 );
993 return true;
994 }
995 }
996 }
997
998 let probe = self
999 .probing
1000 .entry(answer.get_name().to_string())
1001 .or_insert_with(|| {
1002 debug!("new probe of {}", answer.get_name());
1003 Probe::new(start_time)
1004 });
1005
1006 self.new_timers.push(probe.next_send);
1007
1008 for record in probe.records.iter() {
1009 if answer.matches(record.as_ref()) {
1010 debug!(
1011 "found existing record {} in probe of '{}'",
1012 answer.get_type(),
1013 answer.get_name(),
1014 );
1015 probe.waiting_services.insert(service_name.to_string());
1016 return false; }
1018 }
1019
1020 debug!(
1021 "insert record {} into probe of {}",
1022 answer.get_type(),
1023 answer.get_name(),
1024 );
1025 probe.insert_record(answer.clone_box());
1026 probe.waiting_services.insert(service_name.to_string());
1027
1028 false
1029 }
1030
1031 pub(crate) fn update_hostname(
1035 &mut self,
1036 original: &str,
1037 new_name: &str,
1038 probe_time: u64,
1039 ) -> bool {
1040 let mut found_records = Vec::new();
1041 let mut new_timer_added = false;
1042
1043 for (_name, probe) in self.probing.iter_mut() {
1044 probe.records.retain(|record| {
1045 if record.get_type() == RRType::SRV {
1046 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1047 if srv.host() == original {
1048 let mut new_record = srv.clone();
1049 new_record.set_host(new_name.to_string());
1050 found_records.push(new_record);
1051 return false;
1052 }
1053 }
1054 }
1055 true
1056 });
1057 }
1058
1059 for (_name, records) in self.active.iter_mut() {
1060 records.retain(|record| {
1061 if record.get_type() == RRType::SRV {
1062 if let Some(srv) = record.any().downcast_ref::<DnsSrv>() {
1063 if srv.host() == original {
1064 let mut new_record = srv.clone();
1065 new_record.set_host(new_name.to_string());
1066 found_records.push(new_record);
1067 return false;
1068 }
1069 }
1070 }
1071 true
1072 });
1073 }
1074
1075 for record in found_records {
1076 let probe = match self.probing.get_mut(record.get_name()) {
1077 Some(p) => {
1078 p.start_time = probe_time; p
1080 }
1081 None => {
1082 let new_probe = self
1083 .probing
1084 .entry(record.get_name().to_string())
1085 .or_insert_with(|| Probe::new(probe_time));
1086 new_timer_added = true;
1087 new_probe
1088 }
1089 };
1090
1091 debug!(
1092 "insert record {} with new hostname {new_name} into probe for: {}",
1093 record.get_type(),
1094 record.get_name()
1095 );
1096 probe.insert_record(Box::new(record));
1097 }
1098
1099 new_timer_added
1100 }
1101}
1102
1103pub(crate) fn split_sub_domain(domain: &str) -> (&str, Option<&str>) {
1105 if let Some((_, ty_domain)) = domain.rsplit_once("._sub.") {
1106 (ty_domain, Some(domain))
1107 } else {
1108 (domain, None)
1109 }
1110}
1111
1112#[non_exhaustive]
1115pub struct ResolvedService {
1116 pub ty_domain: String,
1118
1119 pub sub_ty_domain: Option<String>,
1125
1126 pub fullname: String,
1128
1129 pub host: String,
1131
1132 pub port: u16,
1134
1135 pub addresses: HashSet<IpAddr>,
1137
1138 pub txt_properties: TxtProperties,
1140}
1141
1142impl ResolvedService {
1143 pub fn is_valid(&self) -> bool {
1145 let some_missing = self.ty_domain.is_empty()
1146 || self.fullname.is_empty()
1147 || self.host.is_empty()
1148 || self.addresses.is_empty();
1149 !some_missing
1150 }
1151}
1152
1153#[cfg(test)]
1154mod tests {
1155 use super::{
1156 decode_txt, encode_txt, u8_slice_to_hex, valid_two_addrs_on_intf, ServiceInfo, TxtProperty,
1157 };
1158 use if_addrs::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
1159 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
1160
1161 #[test]
1162 fn test_txt_encode_decode() {
1163 let properties = vec![
1164 TxtProperty::from(&("key1", "value1")),
1165 TxtProperty::from(&("key2", "value2")),
1166 ];
1167
1168 let property_count = properties.len();
1170 let encoded = encode_txt(properties.iter());
1171 assert_eq!(
1172 encoded.len(),
1173 "key1=value1".len() + "key2=value2".len() + property_count
1174 );
1175 assert_eq!(encoded[0] as usize, "key1=value1".len());
1176
1177 let decoded = decode_txt(&encoded);
1179 assert!(properties[..] == decoded[..]);
1180
1181 let properties = vec![TxtProperty::from(&("key3", ""))];
1183 let property_count = properties.len();
1184 let encoded = encode_txt(properties.iter());
1185 assert_eq!(encoded.len(), "key3=".len() + property_count);
1186
1187 let decoded = decode_txt(&encoded);
1188 assert_eq!(properties, decoded);
1189
1190 let binary_val: Vec<u8> = vec![123, 234, 0];
1192 let binary_len = binary_val.len();
1193 let properties = vec![TxtProperty::from(("key4", binary_val))];
1194 let property_count = properties.len();
1195 let encoded = encode_txt(properties.iter());
1196 assert_eq!(encoded.len(), "key4=".len() + binary_len + property_count);
1197
1198 let decoded = decode_txt(&encoded);
1199 assert_eq!(properties, decoded);
1200
1201 let properties = vec![TxtProperty::from(("key5", "val=5"))];
1203 let property_count = properties.len();
1204 let encoded = encode_txt(properties.iter());
1205 assert_eq!(
1206 encoded.len(),
1207 "key5=".len() + "val=5".len() + property_count
1208 );
1209
1210 let decoded = decode_txt(&encoded);
1211 assert_eq!(properties, decoded);
1212
1213 let properties = vec![TxtProperty::from("key6")];
1215 let property_count = properties.len();
1216 let encoded = encode_txt(properties.iter());
1217 assert_eq!(encoded.len(), "key6".len() + property_count);
1218 let decoded = decode_txt(&encoded);
1219 assert_eq!(properties, decoded);
1220
1221 let properties = vec![TxtProperty::from(
1223 String::from_utf8(vec![0x30; 1024]).unwrap().as_str(), )];
1225 let property_count = properties.len();
1226 let encoded = encode_txt(properties.iter());
1227 assert_eq!(encoded.len(), 255 + property_count);
1228 let decoded = decode_txt(&encoded);
1229 assert_eq!(
1230 vec![TxtProperty::from(
1231 String::from_utf8(vec![0x30; 255]).unwrap().as_str()
1232 )],
1233 decoded
1234 );
1235 }
1236
1237 #[test]
1238 fn test_set_properties_from_txt() {
1239 let properties = vec![
1241 TxtProperty::from(&("one", "1")),
1242 TxtProperty::from(&("ONE", "2")),
1243 TxtProperty::from(&("One", "3")),
1244 ];
1245 let encoded = encode_txt(properties.iter());
1246
1247 let decoded = decode_txt(&encoded);
1249 assert_eq!(decoded.len(), 3);
1250
1251 let mut service_info =
1253 ServiceInfo::new("_test._tcp", "prop_test", "localhost", "", 1234, None).unwrap();
1254 service_info.set_properties_from_txt(&encoded);
1255 assert_eq!(service_info.get_properties().len(), 1);
1256
1257 let prop = service_info.get_properties().iter().next().unwrap();
1259 assert_eq!(prop.key, "one");
1260 assert_eq!(prop.val_str(), "1");
1261 }
1262
1263 #[test]
1264 fn test_u8_slice_to_hex() {
1265 let bytes = [0x01u8, 0x02u8, 0x03u8];
1266 let hex = u8_slice_to_hex(&bytes);
1267 assert_eq!(hex.as_str(), "0x010203");
1268
1269 let slice = "abcdefghijklmnopqrstuvwxyz";
1270 let hex = u8_slice_to_hex(slice.as_bytes());
1271 assert_eq!(hex.len(), slice.len() * 2 + 2);
1272 assert_eq!(
1273 hex.as_str(),
1274 "0x6162636465666768696a6b6c6d6e6f707172737475767778797a"
1275 );
1276 }
1277
1278 #[test]
1279 fn test_txt_property_debug() {
1280 let prop_1 = TxtProperty {
1282 key: "key1".to_string(),
1283 val: Some("val1".to_string().into()),
1284 };
1285 let prop_1_debug = format!("{:?}", &prop_1);
1286 assert_eq!(
1287 prop_1_debug,
1288 "TxtProperty {key: \"key1\", val: Some(\"val1\")}"
1289 );
1290
1291 let prop_2 = TxtProperty {
1293 key: "key2".to_string(),
1294 val: Some(vec![150u8, 151u8, 152u8]),
1295 };
1296 let prop_2_debug = format!("{:?}", &prop_2);
1297 assert_eq!(
1298 prop_2_debug,
1299 "TxtProperty {key: \"key2\", val: Some(0x969798)}"
1300 );
1301 }
1302
1303 #[test]
1304 fn test_txt_decode_property_size_out_of_bounds() {
1305 let encoded: Vec<u8> = vec![
1307 0x0b, b'k', b'e', b'y', b'1', b'=', b'v', b'a', b'l', b'u', b'e',
1309 b'1', 0x10, b'k', b'e', b'y', b'2', b'=', b'v', b'a', b'l', b'u', b'e',
1312 b'2', ];
1314 let decoded = decode_txt(&encoded);
1316 assert_eq!(decoded.len(), 1);
1319 assert_eq!(decoded[0].key, "key1");
1321 }
1322
1323 #[test]
1324 fn test_valid_two_addrs_on_intf() {
1325 let ipv4_netmask = Ipv4Addr::new(192, 168, 1, 0);
1328 let ipv4_intf_addr = IfAddr::V4(Ifv4Addr {
1329 ip: Ipv4Addr::new(192, 168, 1, 10),
1330 netmask: ipv4_netmask,
1331 prefixlen: 24,
1332 broadcast: None,
1333 });
1334 let ipv4_intf = Interface {
1335 name: "e0".to_string(),
1336 addr: ipv4_intf_addr,
1337 index: Some(1),
1338 #[cfg(windows)]
1339 adapter_name: "ethernet".to_string(),
1340 };
1341 let ipv4_a = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10));
1342 let ipv4_b = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 11));
1343
1344 let result = valid_two_addrs_on_intf(&ipv4_a, &ipv4_b, &ipv4_intf);
1345 assert!(result);
1346
1347 let ipv4_c = IpAddr::V4(Ipv4Addr::new(172, 17, 0, 1));
1348 let result = valid_two_addrs_on_intf(&ipv4_a, &ipv4_c, &ipv4_intf);
1349 assert!(!result);
1350
1351 let ipv6_netmask = Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0); let ipv6_intf_addr = IfAddr::V6(Ifv6Addr {
1355 ip: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
1356 netmask: ipv6_netmask,
1357 prefixlen: 32,
1358 broadcast: None,
1359 });
1360 let ipv6_intf = Interface {
1361 name: "eth0".to_string(),
1362 addr: ipv6_intf_addr,
1363 index: Some(2),
1364 #[cfg(windows)]
1365 adapter_name: "ethernet".to_string(),
1366 };
1367 let ipv6_a = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
1368 let ipv6_b = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2));
1369
1370 let result = valid_two_addrs_on_intf(&ipv6_a, &ipv6_b, &ipv6_intf);
1371 assert!(result); let ipv6_c = IpAddr::V6(Ipv6Addr::new(0x2002, 0xdb8, 0, 0, 0, 0, 0, 1));
1374 let result = valid_two_addrs_on_intf(&ipv6_a, &ipv6_c, &ipv6_intf);
1375 assert!(!result); }
1377}