agnostic_mdns/lib.rs
1#![doc = include_str!("../README.md")]
2#![forbid(unsafe_code)]
3#![deny(missing_docs)]
4#![allow(unexpected_cfgs)]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![cfg_attr(docsrs, allow(unused_attributes))]
7#![allow(clippy::needless_return)]
8#![allow(unreachable_code)]
9
10#[cfg(test)]
11mod tests;
12
13use std::{
14 io,
15 net::{Ipv4Addr, Ipv6Addr},
16 time::Duration,
17};
18
19const IPV4_MDNS: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 251);
20const IPV6_MDNS: Ipv6Addr = Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0xfb);
21const IPV4_SIZE: usize = core::mem::size_of::<Ipv4Addr>();
22const IPV6_SIZE: usize = core::mem::size_of::<Ipv6Addr>();
23const MDNS_PORT: u16 = 5353;
24const MAX_INLINE_PACKET_SIZE: usize = 1500;
25
26pub use mdns_proto::{error, proto::Label};
27
28/// synchronous mDNS server implementation
29pub mod sync;
30
31/// Generic asynchronous mDNS implementation for work stealing runtimes
32#[cfg(feature = "worksteal")]
33#[cfg_attr(docsrs, doc(cfg(feature = "worksteal")))]
34pub mod worksteal;
35
36/// A builtin service that can be used with the mDNS server
37pub mod service;
38
39pub use iprobe as netprobe;
40pub use service::{Service, ServiceBuilder};
41pub use smol_str::{SmolStr, format_smolstr};
42
43/// The options for [`Server`].
44#[derive(Clone, Debug)]
45pub struct ServerOptions {
46 pub(crate) ipv4_interface: Option<Ipv4Addr>,
47 pub(crate) ipv6_interface: Option<u32>,
48 pub(crate) log_empty_responses: bool,
49 pub(crate) max_payload_size: usize,
50}
51
52impl Default for ServerOptions {
53 #[inline]
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59impl ServerOptions {
60 /// Returns a new instance of [`ServerOptions`].
61 #[inline]
62 pub const fn new() -> Self {
63 Self {
64 ipv4_interface: None,
65 ipv6_interface: None,
66 log_empty_responses: false,
67 max_payload_size: 1500,
68 }
69 }
70
71 /// Returns the Ipv4 interface to bind the multicast listener to.
72 ///
73 /// ## Example
74 ///
75 /// ```rust
76 /// use agnostic_mdns::ServerOptions;
77 /// use std::net::Ipv4Addr;
78 ///
79 /// let opts = ServerOptions::new().with_ipv4_interface(Ipv4Addr::new(192, 168, 1, 1));
80 /// assert_eq!(opts.ipv4_interface(), Some(&Ipv4Addr::new(192, 168, 1, 1)));
81 /// ```
82 #[inline]
83 pub const fn ipv4_interface(&self) -> Option<&Ipv4Addr> {
84 self.ipv4_interface.as_ref()
85 }
86
87 /// Sets the IPv4 interface to bind the multicast listener to.
88 ///
89 /// ## Example
90 ///
91 /// ```rust
92 /// use agnostic_mdns::ServerOptions;
93 /// use std::net::Ipv4Addr;
94 ///
95 /// let opts = ServerOptions::new().with_ipv4_interface(Ipv4Addr::new(192, 168, 1, 1));
96 /// ```
97 #[inline]
98 pub fn with_ipv4_interface(mut self, iface: Ipv4Addr) -> Self {
99 self.ipv4_interface = Some(iface);
100 self
101 }
102
103 /// Returns the Ipv6 interface to bind the multicast listener to.
104 ///
105 /// ## Example
106 ///
107 /// ```rust
108 /// use agnostic_mdns::ServerOptions;
109 ///
110 /// let opts = ServerOptions::new().with_ipv6_interface(1);
111 /// assert_eq!(opts.ipv6_interface(), Some(1));
112 /// ```
113 #[inline]
114 pub const fn ipv6_interface(&self) -> Option<u32> {
115 self.ipv6_interface
116 }
117
118 /// Sets the IPv6 interface to bind the multicast listener to.
119 ///
120 /// ## Example
121 ///
122 /// ```rust
123 /// use agnostic_mdns::ServerOptions;
124 ///
125 /// let opts = ServerOptions::new().with_ipv6_interface(1);
126 /// ```
127 #[inline]
128 pub fn with_ipv6_interface(mut self, index: u32) -> Self {
129 self.ipv6_interface = Some(index);
130 self
131 }
132
133 /// Sets whether the server should print an informative message
134 /// when there is an mDNS query for which the server has no response.
135 ///
136 /// Default is `false`.
137 ///
138 /// ## Example
139 ///
140 /// ```rust
141 /// use agnostic_mdns::ServerOptions;
142 ///
143 /// let opts = ServerOptions::new().with_log_empty_responses(true);
144 /// assert_eq!(opts.log_empty_responses(), true);
145 /// ```
146 #[inline]
147 pub fn with_log_empty_responses(mut self, log_empty_responses: bool) -> Self {
148 self.log_empty_responses = log_empty_responses;
149 self
150 }
151
152 /// Returns whether the server should print an informative message
153 /// when there is an mDNS query for which the server has no response.
154 ///
155 /// ## Example
156 ///
157 /// ```rust
158 /// use agnostic_mdns::ServerOptions;
159 ///
160 /// let opts = ServerOptions::new().with_log_empty_responses(true);
161 /// assert_eq!(opts.log_empty_responses(), true);
162 /// ```
163 #[inline]
164 pub const fn log_empty_responses(&self) -> bool {
165 self.log_empty_responses
166 }
167
168 /// Returns the configured maximum payload size for mDNS message packets.
169 ///
170 /// This value limits how large each mDNS packet can be when sending queries or
171 /// receiving responses.
172 ///
173 /// Smaller values may be necessary on networks with MTU constraints or when
174 /// working with devices that have limited buffer sizes.
175 ///
176 /// Default is `1500` bytes.
177 ///
178 /// ## Example
179 ///
180 /// ```rust
181 /// use agnostic_mdns::ServerOptions;
182 ///
183 /// let opts = ServerOptions::new().with_max_payload_size(1500);
184 /// ```
185 #[inline]
186 pub const fn max_payload_size(&self) -> usize {
187 self.max_payload_size
188 }
189
190 /// Sets the maximum payload size for mDNS message packets.
191 ///
192 /// This controls how large each mDNS packet can be when sending queries or
193 /// receiving responses.
194 ///
195 /// You might want to adjust this value to:
196 /// - Reduce the size to avoid IP fragmentation on networks with lower MTUs
197 /// - Match device-specific constraints in IoT environments
198 /// - Optimize for specific network conditions
199 ///
200 /// Default is `1500` bytes.
201 ///
202 /// ## Example
203 ///
204 /// ```rust
205 /// use agnostic_mdns::ServerOptions;
206 ///
207 /// let opts = ServerOptions::new().with_max_payload_size(1500);
208 ///
209 /// assert_eq!(opts.max_payload_size(), 1500);
210 /// ```
211 #[inline]
212 pub const fn with_max_payload_size(mut self, max_payload_size: usize) -> Self {
213 self.max_payload_size = max_payload_size;
214 self
215 }
216}
217
218/// How a lookup is performed.
219#[derive(Clone, Debug)]
220pub struct QueryParam<'a> {
221 service: Label<'a>,
222 domain: Label<'a>,
223 timeout: Duration,
224 ipv4_interface: Option<Ipv4Addr>,
225 ipv6_interface: Option<u32>,
226 cap: Option<usize>,
227 want_unicast_response: bool, // Unicast response desired, as per 5.4 in RFC
228 // Whether to disable usage of IPv4 for MDNS operations. Does not affect discovered addresses.
229 disable_ipv4: bool,
230 // Whether to disable usage of IPv6 for MDNS operations. Does not affect discovered addresses.
231 disable_ipv6: bool,
232 max_payload_size: usize,
233}
234
235impl<'a> QueryParam<'a> {
236 /// Creates a new query parameter with default values.
237 #[inline]
238 pub fn new(service: Label<'a>) -> Self {
239 Self {
240 service,
241 domain: Label::from("local"),
242 timeout: Duration::from_secs(1),
243 ipv4_interface: None,
244 ipv6_interface: None,
245 want_unicast_response: false,
246 disable_ipv4: false,
247 disable_ipv6: false,
248 cap: None,
249 max_payload_size: 1500,
250 }
251 }
252
253 /// Sets the domain to search in.
254 ///
255 /// ## Example
256 ///
257 /// ```rust
258 /// use agnostic_mdns::QueryParam;
259 ///
260 /// let params = QueryParam::new("service._tcp".into())
261 /// .with_domain("local.".into());
262 /// ```
263 pub fn with_domain(mut self, domain: Label<'a>) -> Self {
264 self.domain = domain;
265 self
266 }
267
268 /// Returns the domain to search in.
269 ///
270 /// ## Example
271 ///
272 /// ```rust
273 /// use agnostic_mdns::{QueryParam, Label};
274 ///
275 /// let params = QueryParam::new("service._tcp".into())
276 /// .with_domain("local.".into());
277 ///
278 /// assert_eq!(params.domain(), &Label::from("local"));
279 pub const fn domain(&self) -> &Label<'a> {
280 &self.domain
281 }
282
283 /// Sets the service to search for.
284 ///
285 /// ## Example
286 ///
287 /// ```rust
288 /// use agnostic_mdns::QueryParam;
289 ///
290 /// let params = QueryParam::new("service._tcp".into())
291 /// .with_service("service._udp".into());
292 /// ```
293 pub fn with_service(mut self, service: Label<'a>) -> Self {
294 self.service = service;
295 self
296 }
297
298 /// Returns the service to search for.
299 ///
300 /// ## Example
301 ///
302 /// ```rust
303 /// use agnostic_mdns::{QueryParam, Label};
304 ///
305 /// let params = QueryParam::new("service._tcp".into())
306 /// .with_service("service._udp".into());
307 ///
308 /// assert_eq!(params.service(), &Label::from("service._udp"));
309 pub const fn service(&self) -> &Label<'a> {
310 &self.service
311 }
312
313 /// Sets the timeout for the query.
314 ///
315 /// ## Example
316 ///
317 /// ```rust
318 /// use agnostic_mdns::QueryParam;
319 ///
320 /// let params = QueryParam::new("service._tcp".into())
321 /// .with_timeout(std::time::Duration::from_secs(1));
322 /// ```
323 pub fn with_timeout(mut self, timeout: Duration) -> Self {
324 self.timeout = timeout;
325 self
326 }
327
328 /// Returns the timeout for the query.
329 ///
330 /// ## Example
331 ///
332 /// ```rust
333 /// use agnostic_mdns::QueryParam;
334 ///
335 /// let params = QueryParam::new("service._tcp".into())
336 /// .with_timeout(std::time::Duration::from_secs(1));
337 ///
338 /// assert_eq!(params.timeout(), std::time::Duration::from_secs(1));
339 /// ```
340 pub const fn timeout(&self) -> Duration {
341 self.timeout
342 }
343
344 /// Sets the IPv4 interface to use for queries.
345 ///
346 /// ## Example
347 ///
348 /// ```rust
349 /// use agnostic_mdns::QueryParam;
350 ///
351 /// let params = QueryParam::new("service._tcp".into())
352 /// .with_ipv4_interface("0.0.0.0".parse().unwrap());
353 /// ```
354 pub fn with_ipv4_interface(mut self, ipv4_interface: Ipv4Addr) -> Self {
355 self.ipv4_interface = Some(ipv4_interface);
356 self
357 }
358
359 /// Returns the IPv4 interface to use for queries.
360 ///
361 /// ## Example
362 ///
363 /// ```rust
364 /// use agnostic_mdns::QueryParam;
365 ///
366 /// let params = QueryParam::new("service._tcp".into())
367 /// .with_ipv4_interface("0.0.0.0".parse().unwrap());
368 ///
369 /// assert_eq!(params.ipv4_interface().unwrap(), &"0.0.0.0".parse::<std::net::Ipv4Addr>().unwrap());
370 /// ```
371 pub const fn ipv4_interface(&self) -> Option<&Ipv4Addr> {
372 self.ipv4_interface.as_ref()
373 }
374
375 /// Sets the IPv6 interface to use for queries.
376 ///
377 /// ## Example
378 ///
379 /// ```rust
380 /// use agnostic_mdns::QueryParam;
381 ///
382 /// let params = QueryParam::new("service._tcp".into())
383 /// .with_ipv6_interface(1);
384 /// ```
385 pub fn with_ipv6_interface(mut self, ipv6_interface: u32) -> Self {
386 self.ipv6_interface = Some(ipv6_interface);
387 self
388 }
389
390 /// Returns the IPv6 interface to use for queries.
391 ///
392 /// ## Example
393 ///
394 /// ```rust
395 /// use agnostic_mdns::QueryParam;
396 ///
397 /// let params = QueryParam::new("service._tcp".into())
398 /// .with_ipv6_interface(1);
399 /// assert_eq!(params.ipv6_interface().unwrap(), 1);
400 /// ```
401 pub const fn ipv6_interface(&self) -> Option<u32> {
402 self.ipv6_interface
403 }
404
405 /// Sets whether to request unicast responses.
406 ///
407 /// ## Example
408 ///
409 /// ```rust
410 /// use agnostic_mdns::QueryParam;
411 ///
412 /// let params = QueryParam::new("service._tcp".into())
413 /// .with_unicast_response(true);
414 /// ```
415 pub fn with_unicast_response(mut self, want_unicast_response: bool) -> Self {
416 self.want_unicast_response = want_unicast_response;
417 self
418 }
419
420 /// Returns whether to request unicast responses.
421 ///
422 /// ## Example
423 ///
424 /// ```rust
425 /// use agnostic_mdns::QueryParam;
426 ///
427 /// let params = QueryParam::new("service._tcp".into())
428 /// .with_unicast_response(true);
429 ///
430 /// assert_eq!(params.want_unicast_response(), true);
431 /// ```
432 pub const fn want_unicast_response(&self) -> bool {
433 self.want_unicast_response
434 }
435
436 /// Sets whether to disable IPv4 for MDNS operations.
437 ///
438 /// ## Example
439 ///
440 /// ```rust
441 /// use agnostic_mdns::QueryParam;
442 ///
443 /// let params = QueryParam::new("service._tcp".into())
444 /// .with_disable_ipv4(true);
445 /// ```
446 pub fn with_disable_ipv4(mut self, disable_ipv4: bool) -> Self {
447 self.disable_ipv4 = disable_ipv4;
448 self
449 }
450
451 /// Returns whether to disable IPv4 for MDNS operations.
452 ///
453 /// ## Example
454 ///
455 /// ```rust
456 /// use agnostic_mdns::QueryParam;
457 ///
458 /// let params = QueryParam::new("service._tcp".into())
459 /// .with_disable_ipv4(true);
460 ///
461 /// assert_eq!(params.disable_ipv4(), true);
462 /// ```
463 pub const fn disable_ipv4(&self) -> bool {
464 self.disable_ipv4
465 }
466
467 /// Sets whether to disable IPv6 for MDNS operations.
468 ///
469 /// ## Example
470 ///
471 /// ```rust
472 /// use agnostic_mdns::QueryParam;
473 ///
474 /// let params = QueryParam::new("service._tcp".into())
475 /// .with_disable_ipv6(true);
476 /// ```
477 pub fn with_disable_ipv6(mut self, disable_ipv6: bool) -> Self {
478 self.disable_ipv6 = disable_ipv6;
479 self
480 }
481
482 /// Returns whether to disable IPv6 for MDNS operations.
483 ///
484 /// ## Example
485 ///
486 /// ```rust
487 /// use agnostic_mdns::QueryParam;
488 ///
489 /// let params = QueryParam::new("service._tcp".into())
490 /// .with_disable_ipv6(true);
491 ///
492 /// assert_eq!(params.disable_ipv6(), true);
493 /// ```
494 pub const fn disable_ipv6(&self) -> bool {
495 self.disable_ipv6
496 }
497
498 /// Returns the configured maximum payload size for mDNS message packets.
499 ///
500 /// This value limits how large each mDNS packet can be when sending queries or
501 /// receiving responses.
502 ///
503 /// Smaller values may be necessary on networks with MTU constraints or when
504 /// working with devices that have limited buffer sizes.
505 ///
506 /// Default is `1500` bytes.
507 ///
508 /// ## Example
509 ///
510 /// ```rust
511 /// use agnostic_mdns::QueryParam;
512 ///
513 /// let params = QueryParam::new("service._tcp".into())
514 /// .with_max_payload_size(1500);
515 /// ```
516 #[inline]
517 pub const fn max_payload_size(&self) -> usize {
518 self.max_payload_size
519 }
520
521 /// Sets the maximum payload size for mDNS message packets.
522 ///
523 /// This controls how large each mDNS packet can be when sending queries or
524 /// receiving responses.
525 ///
526 /// You might want to adjust this value to:
527 /// - Reduce the size to avoid IP fragmentation on networks with lower MTUs
528 /// - Match device-specific constraints in IoT environments
529 /// - Optimize for specific network conditions
530 ///
531 /// Default is `1500` bytes.
532 ///
533 /// ## Example
534 ///
535 /// ```rust
536 /// use agnostic_mdns::QueryParam;
537 ///
538 /// let params = QueryParam::new("service._tcp".into())
539 /// .with_max_payload_size(1500);
540 ///
541 /// assert_eq!(params.max_payload_size(), 1500);
542 /// ```
543 #[inline]
544 pub const fn with_max_payload_size(mut self, max_payload_size: usize) -> Self {
545 self.max_payload_size = max_payload_size;
546 self
547 }
548
549 /// Returns the channel capacity for the [`Lookup`] stream.
550 ///
551 /// If `None`, the channel is unbounded.
552 ///
553 /// Default is `None`.
554 ///
555 /// ## Example
556 ///
557 /// ```rust
558 /// use agnostic_mdns::QueryParam;
559 ///
560 /// let params = QueryParam::new("service._tcp".into())
561 /// .with_capacity(Some(10));
562 ///
563 /// assert_eq!(params.capacity().unwrap(), 10);
564 /// ```
565 #[inline]
566 pub const fn capacity(&self) -> Option<usize> {
567 self.cap
568 }
569
570 /// Sets the channel capacity for the [`Lookup`] stream.
571 ///
572 /// If `None`, the channel is unbounded.
573 ///
574 /// Default is `None`.
575 ///
576 /// ## Example
577 ///
578 /// ```rust
579 /// use agnostic_mdns::QueryParam;
580 ///
581 /// let params = QueryParam::new("service._tcp".into())
582 /// .with_capacity(Some(10));
583 /// ```
584 #[inline]
585 pub fn with_capacity(mut self, cap: Option<usize>) -> Self {
586 self.cap = cap;
587 self
588 }
589}
590
591/// Types for `tokio` runtime
592#[cfg(feature = "tokio")]
593#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
594pub mod tokio {
595 use std::io;
596
597 use super::{QueryParam, service::Service, worksteal::ServiceEntry};
598 use mdns_proto::proto::Label;
599
600 pub use agnostic_net::{runtime::tokio::TokioRuntime as Runtime, tokio::Net};
601 pub use async_channel as channel;
602
603 /// A server that can be used with `tokio` runtime
604 pub type Server = super::worksteal::Server<Net, Service>;
605
606 /// Looks up a given service, in a domain, waiting at most
607 /// for a timeout before finishing the query. The results are streamed
608 /// to a channel. Sends will not block, so clients should make sure to
609 /// either read or buffer. This method will attempt to stop the query
610 /// on cancellation.
611 #[inline]
612 pub async fn query(params: QueryParam<'_>, tx: channel::Sender<ServiceEntry>) -> io::Result<()> {
613 super::worksteal::query::<Net>(params, tx).await
614 }
615
616 /// Similar to [`query`], however it uses all the default parameters
617 #[inline]
618 pub async fn lookup(service: Label<'_>, tx: channel::Sender<ServiceEntry>) -> io::Result<()> {
619 super::worksteal::lookup::<Net>(service, tx).await
620 }
621}
622
623/// Types for `smol` runtime
624#[cfg(feature = "smol")]
625#[cfg_attr(docsrs, doc(cfg(feature = "smol")))]
626pub mod smol {
627 use super::{Label, QueryParam, service::Service, worksteal::ServiceEntry};
628 use std::io;
629
630 pub use agnostic_net::{runtime::smol::SmolRuntime as Runtime, smol::Net};
631 pub use async_channel as channel;
632
633 /// A server that can be used with `smol` runtime
634 pub type Server = super::worksteal::Server<Net, Service>;
635
636 /// Looks up a given service, in a domain, waiting at most
637 /// for a timeout before finishing the query. The results are streamed
638 /// to a channel. Sends will not block, so clients should make sure to
639 /// either read or buffer. This method will attempt to stop the query
640 /// on cancellation.
641 #[inline]
642 pub async fn query(params: QueryParam<'_>, tx: channel::Sender<ServiceEntry>) -> io::Result<()> {
643 super::worksteal::query::<Net>(params, tx).await
644 }
645
646 /// Similar to [`query`], however it uses all the default parameters
647 #[inline]
648 pub async fn lookup(service: Label<'_>, tx: channel::Sender<ServiceEntry>) -> io::Result<()> {
649 super::worksteal::lookup::<Net>(service, tx).await
650 }
651}
652
653/// Types for `async-std` runtime
654#[cfg(feature = "async-std")]
655#[cfg_attr(docsrs, doc(cfg(feature = "async-std")))]
656pub mod async_std {
657 use super::{Label, QueryParam, service::Service, worksteal::ServiceEntry};
658 use std::io;
659
660 pub use agnostic_net::{async_std::Net, runtime::async_std::AsyncStdRuntime as Runtime};
661 pub use async_channel as channel;
662
663 /// A server that can be used with `async-std` runtime
664 pub type Server = super::worksteal::Server<Net, Service>;
665
666 /// Looks up a given service, in a domain, waiting at most
667 /// for a timeout before finishing the query. The results are streamed
668 /// to a channel. Sends will not block, so clients should make sure to
669 /// either read or buffer. This method will attempt to stop the query
670 /// on cancellation.
671 #[inline]
672 pub async fn query(params: QueryParam<'_>, tx: channel::Sender<ServiceEntry>) -> io::Result<()> {
673 super::worksteal::query::<Net>(params, tx).await
674 }
675
676 /// Similar to [`query`], however it uses all the default parameters
677 #[inline]
678 pub async fn lookup(service: Label<'_>, tx: channel::Sender<ServiceEntry>) -> io::Result<()> {
679 super::worksteal::lookup::<Net>(service, tx).await
680 }
681}
682
683mod utils;
684
685/// Returns `true` if a domain name is fully qualified domain name
686#[inline]
687pub fn is_fqdn(s: &str) -> bool {
688 let len = s.len();
689 if s.is_empty() || !s.ends_with('.') {
690 return false;
691 }
692
693 let s = &s[..len - 1];
694
695 if s.is_empty() || !s.ends_with('\\') {
696 return true;
697 }
698
699 // Count backslashes at the end
700 let last_non_backslash = s.rfind(|c| c != '\\').unwrap_or(0);
701
702 (len - last_non_backslash) % 2 == 0
703}
704
705/// Returns the hostname of the current machine.
706///
707/// On wasm target, this function always returns `None`.
708///
709/// ## Examples
710///
711/// ```
712/// use agnostic_mdns::hostname;
713///
714/// let hostname = hostname();
715/// println!("hostname: {hostname:?}");
716/// ```
717#[allow(unreachable_code)]
718pub fn hostname() -> io::Result<SmolStr> {
719 #[cfg(not(any(windows, target_os = "wasi")))]
720 return {
721 let name = rustix::system::uname();
722 let name = name.nodename().to_string_lossy();
723 Ok(SmolStr::from(name.as_ref()))
724 };
725
726 #[cfg(windows)]
727 return {
728 match ::hostname::get() {
729 Ok(name) => {
730 let name = name.to_string_lossy();
731 Ok(SmolStr::from(name.as_ref()))
732 }
733 Err(e) => Err(e),
734 }
735 };
736
737 Err(io::Error::new(
738 io::ErrorKind::Unsupported,
739 "hostname is not supported on this platform",
740 ))
741}
742
743fn hostname_fqdn() -> io::Result<SmolStr> {
744 #[cfg(not(any(windows, target_os = "wasi")))]
745 return {
746 let name = rustix::system::uname();
747 let name = name.nodename().to_string_lossy();
748 Ok(format_smolstr!("{}.", Label::from(name.as_ref())))
749 };
750
751 #[cfg(windows)]
752 return {
753 match ::hostname::get() {
754 Ok(name) => {
755 let name = name.to_string_lossy();
756 Ok(format_smolstr!("{}.", Label::from(name.as_ref())))
757 }
758 Err(e) => Err(e),
759 }
760 };
761
762 Err(io::Error::new(
763 io::ErrorKind::Unsupported,
764 "hostname is not supported on this platform",
765 ))
766}
767
768fn invalid_input_err<E>(e: E) -> io::Error
769where
770 E: Into<Box<dyn std::error::Error + Send + Sync>>,
771{
772 io::Error::new(io::ErrorKind::InvalidInput, e)
773}
774
775#[allow(clippy::large_enum_variant)]
776#[derive(Clone)]
777enum Buffer {
778 Heap(Vec<u8>),
779 Stack([u8; MAX_INLINE_PACKET_SIZE]),
780}
781
782impl Buffer {
783 fn zerod(cap: usize) -> Self {
784 if cap <= MAX_INLINE_PACKET_SIZE {
785 Buffer::Stack([0; MAX_INLINE_PACKET_SIZE])
786 } else {
787 Buffer::Heap(vec![0; cap])
788 }
789 }
790}
791
792impl From<usize> for Buffer {
793 fn from(size: usize) -> Self {
794 if size <= MAX_INLINE_PACKET_SIZE {
795 Buffer::Stack([0; MAX_INLINE_PACKET_SIZE])
796 } else {
797 Buffer::Heap(vec![0; size])
798 }
799 }
800}
801
802impl core::ops::Deref for Buffer {
803 type Target = [u8];
804
805 fn deref(&self) -> &[u8] {
806 match self {
807 Buffer::Heap(v) => v,
808 Buffer::Stack(v) => v,
809 }
810 }
811}
812
813impl core::ops::DerefMut for Buffer {
814 fn deref_mut(&mut self) -> &mut [u8] {
815 match self {
816 Buffer::Heap(v) => v,
817 Buffer::Stack(v) => v,
818 }
819 }
820}