1use crate::ot::WrongSize;
6use crate::prelude_internal::*;
7
8#[derive(Default, Clone)]
10pub struct DnsTxtEntry<'a>(pub otDnsTxtEntry, PhantomData<&'a str>);
11
12impl_ot_castable!(lifetime DnsTxtEntry<'_>, otDnsTxtEntry, Default::default());
13
14impl<'a> DnsTxtEntry<'a> {
15 pub fn try_new(
17 key: Option<&'a CStr>,
18 value: Option<&'a [u8]>,
19 ) -> Result<DnsTxtEntry<'a>, WrongSize> {
20 let mut ret = DnsTxtEntry::default();
21
22 ret.0.mKey = key.map(CStr::as_ptr).unwrap_or(std::ptr::null());
23
24 if let Some(value) = value {
25 ret.0.mValueLength = u16::try_from(value.len()).map_err(|_| WrongSize)?;
26 ret.0.mValue = value.as_ptr();
27 }
28
29 Ok(ret)
30 }
31
32 pub fn key_field(&self) -> Option<&'a CStr> {
34 if self.0.mKey.is_null() {
35 None
36 } else {
37 let cstr = unsafe { CStr::from_ptr(self.0.mKey) };
38 Some(cstr)
39 }
40 }
41
42 pub fn value_field(&self) -> Option<&'a [u8]> {
44 if self.0.mValue.is_null() {
45 None
46 } else {
47 let value =
48 unsafe { std::slice::from_raw_parts(self.0.mValue, self.0.mValueLength as usize) };
49 Some(value)
50 }
51 }
52
53 pub fn key(&self) -> Option<&'a str> {
57 if let Some(cstr) = self.key_field() {
58 match cstr.to_str() {
59 Ok(x) => Some(x),
60 Err(x) => {
61 warn!("Bad DNS TXT key {:?}: {:?}", cstr, x);
64 None
65 }
66 }
67 } else if let Some(value) = self.value_field() {
68 let key_bytes = value.splitn(2, |x| *x == b'=').next().unwrap();
69 match std::str::from_utf8(key_bytes) {
70 Ok(x) => Some(x),
71 Err(x) => {
72 warn!("Bad DNS TXT key {:?}: {:?}", key_bytes, x);
75 None
76 }
77 }
78 } else {
79 None
80 }
81 }
82
83 pub fn value(&self) -> Option<&'a [u8]> {
87 #[allow(clippy::manual_filter)]
88 if let Some(value) = self.value_field() {
89 if self.0.mKey.is_null() {
90 let mut iter = value.splitn(2, |x| *x == b'=');
91 let a = iter.next();
92 let b = iter.next();
93 match (a, b) {
94 (Some(_), Some(value)) => Some(value),
95 _ => None,
96 }
97 } else {
98 Some(value)
99 }
100 } else {
101 None
102 }
103 }
104
105 pub fn to_vec(&self) -> Vec<u8> {
107 let mut pair = vec![];
108 match (self.key(), self.value()) {
109 (Some(key), Some(value)) => {
110 pair.extend_from_slice(key.as_bytes());
111 pair.push(b'=');
112 pair.extend_from_slice(value);
113 }
114 (Some(key), None) => {
115 pair.extend_from_slice(key.as_bytes());
116 }
117 _ => {}
118 }
119 pair.truncate(u8::MAX as usize);
120 pair
121 }
122}
123
124impl std::fmt::Debug for DnsTxtEntry<'_> {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 f.debug_struct("DnsTxtEntry")
127 .field("key_field", &self.key_field())
128 .field("value_field", &self.value_field().map(ascii_dump))
129 .finish()
130 }
131}
132
133impl std::fmt::Display for DnsTxtEntry<'_> {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 write!(f, "{}", ascii_dump(&self.to_vec()))
136 }
137}
138
139#[derive(Default, Debug, Clone)]
141pub struct DnsTxtEntryIterator<'a>(pub otDnsTxtEntryIterator, PhantomData<&'a str>);
142
143impl_ot_castable!(lifetime DnsTxtEntryIterator<'_>, otDnsTxtEntryIterator, Default::default());
144
145impl<'a> DnsTxtEntryIterator<'a> {
146 pub fn try_new(txt_data: &'a [u8]) -> Result<DnsTxtEntryIterator<'a>, WrongSize> {
151 if let Ok(len) = u16::try_from(txt_data.len()) {
152 let mut ret = DnsTxtEntryIterator::default();
153 unsafe { otDnsInitTxtEntryIterator(ret.as_ot_mut_ptr(), txt_data.as_ptr(), len) }
155 Ok(ret)
156 } else {
157 Err(WrongSize)
158 }
159 }
160}
161
162impl<'a> Iterator for DnsTxtEntryIterator<'a> {
163 type Item = Result<DnsTxtEntry<'a>>;
164
165 fn next(&mut self) -> Option<Self::Item> {
166 let mut ret = DnsTxtEntry::default();
167
168 match Error::from(unsafe {
169 otDnsGetNextTxtEntry(self.as_ot_mut_ptr(), ret.as_ot_mut_ptr())
170 }) {
171 Error::None => Some(Ok(ret)),
172 Error::NotFound => None,
173 err => Some(Err(err)),
174 }
175 }
176}
177
178pub fn dns_txt_flatten<I: IntoIterator<Item = (String, Option<Vec<u8>>)>>(txt: I) -> Vec<u8> {
181 let mut ret = vec![];
182 for (key, value) in txt {
183 let mut pair = vec![];
184
185 if let Some(value) = value {
186 pair.extend_from_slice(key.as_bytes());
187 pair.push(b'=');
188 pair.extend_from_slice(&value);
189 } else {
190 pair.extend_from_slice(key.as_bytes());
191 }
192 pair.truncate(u8::MAX as usize);
193 ret.push(u8::try_from(pair.len()).unwrap());
194 ret.extend(pair);
195 }
196 ret
197}
198
199#[derive(Debug, Copy, Clone, Eq, Ord, PartialOrd, PartialEq, num_derive::FromPrimitive)]
203#[allow(missing_docs)]
204pub enum DnssdQueryType {
205 None = OT_DNSSD_QUERY_TYPE_NONE as isize,
207
208 Browse = OT_DNSSD_QUERY_TYPE_BROWSE as isize,
210
211 ResolveService = OT_DNSSD_QUERY_TYPE_RESOLVE as isize,
213
214 ResolveHost = OT_DNSSD_QUERY_TYPE_RESOLVE_HOST as isize,
216}
217
218#[repr(transparent)]
220pub struct DnssdQuery(otDnssdQuery, PhantomData<*mut otDnssdQuery>);
221
222impl_ot_castable!(opaque DnssdQuery, otDnssdQuery);
223
224impl std::fmt::Debug for DnssdQuery {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 let (query_type, name) = self.query_type_and_name();
227 f.debug_struct("otDnsQuery").field("type", &query_type).field("name", &name).finish()
228 }
229}
230
231impl DnssdQuery {
232 pub fn query_type_and_name(&self) -> (DnssdQueryType, CString) {
234 let mut bytes: [::std::os::raw::c_char; OT_DNS_MAX_NAME_SIZE as usize] =
235 [0; OT_DNS_MAX_NAME_SIZE as usize];
236 let query_type = unsafe {
237 otDnssdGetQueryTypeAndName(
238 self.as_ot_ptr(),
239 bytes.as_mut_ptr() as *mut [::std::os::raw::c_char; OT_DNS_MAX_NAME_SIZE as usize],
240 )
241 };
242 let name = unsafe { CStr::from_ptr(bytes.as_ptr()) };
243
244 (DnssdQueryType::from_u32(query_type).unwrap(), name.to_owned())
245 }
246}
247
248pub trait Dnssd {
252 fn dnssd_get_next_query(&self, prev: Option<&DnssdQuery>) -> Option<&DnssdQuery>;
258
259 fn dnssd_query_handle_discovered_host(
261 &self,
262 hostname: &CStr,
263 addresses: &[Ip6Address],
264 ttl: u32,
265 );
266
267 #[allow(clippy::too_many_arguments)]
283 fn dnssd_query_handle_discovered_service_instance(
284 &self,
285 service_full_name: &CStr,
286 addresses: &[Ip6Address],
287 full_name: &CStr,
288 host_name: &CStr,
289 port: u16,
290 priority: u16,
291 ttl: u32,
292 txt_data: &[u8],
293 weight: u16,
294 );
295
296 fn dnssd_query_set_callbacks<'a, F>(&'a self, f: Option<F>)
304 where
305 F: FnMut(bool, &CStr) + 'a;
306
307 fn dnssd_get_counters(&self) -> &DnssdCounters;
310
311 fn dnssd_upstream_query_is_enabled(&self) -> bool;
314
315 fn dnssd_upstream_query_set_enabled(&self, enabled: bool);
318}
319
320impl<T: Dnssd + Boxable> Dnssd for ot::Box<T> {
321 fn dnssd_get_next_query(&self, prev: Option<&DnssdQuery>) -> Option<&DnssdQuery> {
322 self.as_ref().dnssd_get_next_query(prev)
323 }
324
325 fn dnssd_query_handle_discovered_host(
326 &self,
327 hostname: &CStr,
328 addresses: &[Ip6Address],
329 ttl: u32,
330 ) {
331 self.as_ref().dnssd_query_handle_discovered_host(hostname, addresses, ttl);
332 }
333
334 fn dnssd_query_handle_discovered_service_instance(
335 &self,
336 service_full_name: &CStr,
337 addresses: &[Ip6Address],
338 full_name: &CStr,
339 host_name: &CStr,
340 port: u16,
341 priority: u16,
342 ttl: u32,
343 txt_data: &[u8],
344 weight: u16,
345 ) {
346 self.as_ref().dnssd_query_handle_discovered_service_instance(
347 service_full_name,
348 addresses,
349 full_name,
350 host_name,
351 port,
352 priority,
353 ttl,
354 txt_data,
355 weight,
356 );
357 }
358
359 fn dnssd_query_set_callbacks<'a, F>(&'a self, f: Option<F>)
360 where
361 F: FnMut(bool, &CStr) + 'a,
362 {
363 self.as_ref().dnssd_query_set_callbacks(f)
364 }
365
366 fn dnssd_get_counters(&self) -> &DnssdCounters {
367 self.as_ref().dnssd_get_counters()
368 }
369
370 fn dnssd_upstream_query_is_enabled(&self) -> bool {
371 self.as_ref().dnssd_upstream_query_is_enabled()
372 }
373
374 fn dnssd_upstream_query_set_enabled(&self, enabled: bool) {
375 self.as_ref().dnssd_upstream_query_set_enabled(enabled)
376 }
377}
378
379impl Dnssd for Instance {
380 fn dnssd_get_next_query(&self, prev: Option<&DnssdQuery>) -> Option<&DnssdQuery> {
381 use std::ptr::null_mut;
382 unsafe {
383 DnssdQuery::ref_from_ot_ptr(otDnssdGetNextQuery(
384 self.as_ot_ptr(),
385 prev.map(DnssdQuery::as_ot_ptr).unwrap_or(null_mut()),
386 ) as *mut otDnssdQuery)
387 }
388 }
389
390 fn dnssd_query_handle_discovered_host(
391 &self,
392 hostname: &CStr,
393 addresses: &[Ip6Address],
394 ttl: u32,
395 ) {
396 unsafe {
397 otDnssdQueryHandleDiscoveredHost(
398 self.as_ot_ptr(),
399 hostname.as_ptr(),
400 &mut otDnssdHostInfo {
401 mAddressNum: addresses.len().try_into().unwrap(),
402 mAddresses: addresses.as_ptr() as *const otIp6Address,
403 mTtl: ttl,
404 } as *mut otDnssdHostInfo,
405 )
406 }
407 }
408
409 fn dnssd_query_handle_discovered_service_instance(
410 &self,
411 service_full_name: &CStr,
412 addresses: &[Ip6Address],
413 full_name: &CStr,
414 host_name: &CStr,
415 port: u16,
416 priority: u16,
417 ttl: u32,
418 txt_data: &[u8],
419 weight: u16,
420 ) {
421 unsafe {
422 otDnssdQueryHandleDiscoveredServiceInstance(
423 self.as_ot_ptr(),
424 service_full_name.as_ptr(),
425 &mut otDnssdServiceInstanceInfo {
426 mFullName: full_name.as_ptr(),
427 mHostName: host_name.as_ptr(),
428 mAddressNum: addresses.len().try_into().unwrap(),
429 mAddresses: addresses.as_ptr() as *const otIp6Address,
430 mPort: port,
431 mPriority: priority,
432 mWeight: weight,
433 mTxtLength: txt_data.len().try_into().unwrap(),
434 mTxtData: txt_data.as_ptr(),
435 mTtl: ttl,
436 } as *mut otDnssdServiceInstanceInfo,
437 )
438 }
439 }
440
441 fn dnssd_query_set_callbacks<'a, F>(&'a self, f: Option<F>)
442 where
443 F: FnMut(bool, &CStr) + 'a,
444 {
445 unsafe extern "C" fn _ot_dnssd_query_subscribe_callback<'a, F: FnMut(bool, &CStr) + 'a>(
446 context: *mut ::std::os::raw::c_void,
447 full_name_ptr: *const c_char,
448 ) {
449 trace!("_ot_dnssd_query_subscribe_callback");
450
451 let full_name_cstr = CStr::from_ptr(full_name_ptr);
452
453 let sender = &mut *(context as *mut F);
455
456 sender(true, full_name_cstr)
457 }
458
459 unsafe extern "C" fn _ot_dnssd_query_unsubscribe_callback<
460 'a,
461 F: FnMut(bool, &CStr) + 'a,
462 >(
463 context: *mut ::std::os::raw::c_void,
464 full_name_ptr: *const c_char,
465 ) {
466 trace!("_ot_dnssd_query_unsubscribe_callback");
467
468 let full_name_cstr = CStr::from_ptr(full_name_ptr);
469
470 let sender = &mut *(context as *mut F);
472
473 sender(false, full_name_cstr)
474 }
475
476 let (fn_ptr, fn_box, cb_sub, cb_unsub): (
477 _,
478 _,
479 otDnssdQuerySubscribeCallback,
480 otDnssdQueryUnsubscribeCallback,
481 ) = if let Some(f) = f {
482 let mut x = Box::new(f);
483
484 (
485 x.as_mut() as *mut F as *mut ::std::os::raw::c_void,
486 Some(x as Box<dyn FnMut(bool, &CStr) + 'a>),
487 Some(_ot_dnssd_query_subscribe_callback::<F>),
488 Some(_ot_dnssd_query_unsubscribe_callback::<F>),
489 )
490 } else {
491 (std::ptr::null_mut() as *mut ::std::os::raw::c_void, None, None, None)
492 };
493
494 unsafe {
495 otDnssdQuerySetCallbacks(self.as_ot_ptr(), cb_sub, cb_unsub, fn_ptr);
496
497 self.borrow_backing().dnssd_query_sub_unsub_fn.set(std::mem::transmute::<
503 Option<Box<dyn FnMut(bool, &CStr) + 'a>>,
504 Option<Box<dyn FnMut(bool, &CStr) + 'static>>,
505 >(fn_box));
506 }
507 }
508
509 fn dnssd_get_counters(&self) -> &DnssdCounters {
510 unsafe { DnssdCounters::ref_from_ot_ptr(otDnssdGetCounters(self.as_ot_ptr())).unwrap() }
511 }
512
513 fn dnssd_upstream_query_is_enabled(&self) -> bool {
514 unsafe { otDnssdUpstreamQueryIsEnabled(self.as_ot_ptr()) }
515 }
516
517 fn dnssd_upstream_query_set_enabled(&self, enabled: bool) {
518 unsafe { otDnssdUpstreamQuerySetEnabled(self.as_ot_ptr(), enabled) }
519 }
520}
521
522#[derive(Debug, Clone)]
524pub struct DnssdQueryIterator<'a, T: Dnssd> {
525 prev: Option<&'a DnssdQuery>,
526 ot_instance: &'a T,
527}
528
529impl<'a, T: Dnssd> Iterator for DnssdQueryIterator<'a, T> {
530 type Item = &'a DnssdQuery;
531
532 fn next(&mut self) -> Option<Self::Item> {
533 self.prev = self.ot_instance.dnssd_get_next_query(self.prev);
534 self.prev
535 }
536}
537
538pub trait DnssdExt: Dnssd {
540 fn dnssd_queries(&self) -> DnssdQueryIterator<'_, Self>
542 where
543 Self: Sized,
544 {
545 DnssdQueryIterator { prev: None, ot_instance: self }
546 }
547}
548
549impl<T: Dnssd> DnssdExt for T {}
550
551#[cfg(test)]
552mod test {
553 use super::*;
554
555 #[test]
556 fn test_dnstxtentry_new() {
557 let cstring = CString::new("CRA").unwrap();
558 assert_eq!(
559 DnsTxtEntry::try_new(Some(&cstring), Some(b"300")).unwrap().to_string(),
560 "CRA=300".to_string()
561 );
562 assert_eq!(
563 DnsTxtEntry::try_new(None, Some(b"CRA=300")).unwrap().to_string(),
564 "CRA=300".to_string()
565 );
566 assert_eq!(
567 DnsTxtEntry::try_new(Some(&cstring), None).unwrap().to_string(),
568 "CRA".to_string()
569 );
570 }
571
572 #[test]
573 fn test_split_txt() {
574 assert_eq!(
575 DnsTxtEntryIterator::try_new(b"")
576 .unwrap()
577 .map(|x| x.unwrap().to_string())
578 .collect::<Vec<_>>(),
579 Vec::<String>::new()
580 );
581 assert_eq!(
582 DnsTxtEntryIterator::try_new(b"\x13xa=a7bfc4981f4e4d22\x13xp=029c6f4dbae059cb")
583 .unwrap()
584 .map(|x| x.unwrap().to_string())
585 .collect::<Vec<_>>(),
586 vec!["xa=a7bfc4981f4e4d22".to_string(), "xp=029c6f4dbae059cb".to_string()]
587 );
588 assert_eq!(
589 DnsTxtEntryIterator::try_new(b"\x13xa=a7bfc4981f4e4d22\x11xp=029c6f4dbae059")
590 .unwrap()
591 .map(|x| x.unwrap().to_string())
592 .collect::<Vec<_>>(),
593 vec!["xa=a7bfc4981f4e4d22".to_string(), "xp=029c6f4dbae059".to_string()]
594 );
595 assert_eq!(
596 DnsTxtEntryIterator::try_new(b"\x13xa=a7bfc4981f4e4d22\x04flag\x11xp=029c6f4dbae059")
597 .unwrap()
598 .map(|x| x.unwrap().to_string())
599 .collect::<Vec<_>>(),
600 vec![
601 "xa=a7bfc4981f4e4d22".to_string(),
602 "flag".to_string(),
603 "xp=029c6f4dbae059".to_string()
604 ]
605 );
606 }
607
608 #[test]
609 fn test_flatten_txt() {
610 assert_eq!(ot::dns_txt_flatten(None), vec![]);
611 assert_eq!(ot::dns_txt_flatten(vec![]), vec![]);
612 assert_eq!(
613 ot::dns_txt_flatten(vec![("xa".to_string(), Some(b"a7bfc4981f4e4d22".to_vec()))]),
614 b"\x13xa=a7bfc4981f4e4d22".to_vec()
615 );
616 assert_eq!(
617 ot::dns_txt_flatten(vec![
618 ("xa".to_string(), Some(b"a7bfc4981f4e4d22".to_vec())),
619 ("xp".to_string(), Some(b"029c6f4dbae059cb".to_vec()))
620 ]),
621 b"\x13xa=a7bfc4981f4e4d22\x13xp=029c6f4dbae059cb".to_vec()
622 );
623 assert_eq!(
624 ot::dns_txt_flatten(vec![
625 ("xa".to_string(), Some(b"a7bfc4981f4e4d22".to_vec())),
626 ("flag".to_string(), None),
627 ("xp".to_string(), Some(b"029c6f4dbae059cb".to_vec()))
628 ]),
629 b"\x13xa=a7bfc4981f4e4d22\x04flag\x13xp=029c6f4dbae059cb".to_vec()
630 );
631 }
632}