Skip to main content

mdns_proto/
config.rs

1//! Configuration types passed to Endpoint/Service/Query constructors.
2
3cfg_storage! {
4  use core::time::Duration;
5}
6
7cfg_heap! {
8  use crate::records::ServiceRecords;
9}
10cfg_storage! {
11  use crate::{
12    Name,
13    wire::{ResourceClass, ResourceType},
14  };
15}
16
17/// Configuration for an `Endpoint`.
18#[derive(Debug, Clone, Copy, Eq, PartialEq)]
19pub struct EndpointConfig {
20  probe_unique_names: bool,
21  answer_questions: bool,
22  populate_cache: bool,
23  trust_advertised_src_as_self: bool,
24}
25
26impl EndpointConfig {
27  /// Construct with defaults: probe on registration, answer questions, cache
28  /// observations, and DO NOT trust advertised-source matching as a self-
29  /// loopback signal. The default is appropriate for the supported async
30  /// driver, which passes an authoritative self-loopback flag to
31  /// [`Endpoint::handle`](crate::Endpoint::handle) (its `caller_is_self`
32  /// parameter) for every inbound datagram and so does not need the lossy
33  /// fallback. Single-process callers that cannot supply `caller_is_self` should
34  /// enable `trust_advertised_src_as_self` to recover the legacy
35  /// behaviour.
36  pub const fn new() -> Self {
37    Self {
38      probe_unique_names: true,
39      answer_questions: true,
40      populate_cache: true,
41      trust_advertised_src_as_self: false,
42    }
43  }
44
45  /// Whether to probe before claiming a service name.
46  #[inline(always)]
47  pub const fn probe_unique_names(&self) -> bool {
48    self.probe_unique_names
49  }
50
51  /// Whether to answer incoming questions for registered services.
52  #[inline(always)]
53  pub const fn answer_questions(&self) -> bool {
54    self.answer_questions
55  }
56
57  /// Whether to populate the cache from observed records.
58  #[inline(always)]
59  pub const fn populate_cache(&self) -> bool {
60    self.populate_cache
61  }
62
63  /// Set whether to probe before claiming a service name.
64  #[must_use]
65  pub const fn with_probe_unique_names(mut self, v: bool) -> Self {
66    self.probe_unique_names = v;
67    self
68  }
69
70  /// Set whether to answer incoming questions for registered services.
71  #[must_use]
72  pub const fn with_answer_questions(mut self, v: bool) -> Self {
73    self.answer_questions = v;
74    self
75  }
76
77  /// Set whether to populate the cache from observed records.
78  #[must_use]
79  pub const fn with_populate_cache(mut self, v: bool) -> Self {
80    self.populate_cache = v;
81    self
82  }
83
84  /// Whether to treat any inbound packet whose source IP matches an
85  /// advertised A/AAAA record as a self-loopback.
86  ///
87  /// Default: `false`. The driver-side self-send hash cache — surfaced to
88  /// the proto via the `caller_is_self` argument of
89  /// [`Endpoint::handle`](crate::Endpoint::handle) — supersedes this signal and
90  /// avoids the false positives that drop legitimate same-host peer traffic.
91  /// Enable only when running a single-process responder that cannot supply
92  /// `caller_is_self`.
93  #[inline(always)]
94  pub const fn trust_advertised_src_as_self(&self) -> bool {
95    self.trust_advertised_src_as_self
96  }
97
98  /// Set [`Self::trust_advertised_src_as_self`].
99  #[must_use]
100  pub const fn with_trust_advertised_src_as_self(mut self, v: bool) -> Self {
101    self.trust_advertised_src_as_self = v;
102    self
103  }
104}
105
106impl Default for EndpointConfig {
107  fn default() -> Self {
108    Self::new()
109  }
110}
111
112cfg_heap! {
113  /// Spec for registering a service.
114  #[derive(Debug, Clone, Eq, PartialEq)]
115  pub struct ServiceSpec {
116    records: ServiceRecords,
117  }
118
119  impl ServiceSpec {
120    /// Wrap a `ServiceRecords` bundle as a spec.
121    pub const fn new(records: ServiceRecords) -> Self {
122      Self { records }
123    }
124
125    /// Borrow the inner records.
126    #[inline(always)]
127    pub const fn records(&self) -> &ServiceRecords {
128      &self.records
129    }
130
131    /// Consume the spec, returning the inner records.
132    #[inline(always)]
133    pub fn into_records(self) -> ServiceRecords {
134      self.records
135    }
136  }
137}
138
139cfg_storage! {
140  /// Spec for starting a query.
141  #[derive(Debug, Clone, Eq, PartialEq)]
142  pub struct QuerySpec {
143    qname: Name,
144    qtype: ResourceType,
145    qclass: ResourceClass,
146    unicast_response: bool,
147    timeout: Option<Duration>,
148    max_answers: Option<usize>,
149  }
150
151  impl QuerySpec {
152    /// Build a new spec for an mDNS query.
153    pub fn new(qname: Name, qtype: ResourceType) -> Self {
154      Self {
155        qname,
156        qtype,
157        qclass: ResourceClass::In,
158        unicast_response: false,
159        timeout: None,
160        max_answers: None,
161      }
162    }
163
164    /// The query name.
165    #[inline(always)]
166    pub fn qname(&self) -> &Name {
167      &self.qname
168    }
169
170    /// The query resource type.
171    #[inline(always)]
172    pub const fn qtype(&self) -> ResourceType {
173      self.qtype
174    }
175
176    /// The query class.
177    #[inline(always)]
178    pub const fn qclass(&self) -> ResourceClass {
179      self.qclass
180    }
181
182    /// Whether to request unicast responses (RFC 6762 §5.4).
183    #[inline(always)]
184    pub const fn unicast_response(&self) -> bool {
185      self.unicast_response
186    }
187
188    /// The absolute timeout for the query, if set.
189    #[inline(always)]
190    pub const fn timeout(&self) -> Option<Duration> {
191      self.timeout
192    }
193
194    /// Maximum number of collected answers, if explicitly overridden.
195    ///
196    /// When `None` the `Query` state machine uses its built-in default (256).
197    #[inline(always)]
198    pub const fn max_answers(&self) -> Option<usize> {
199      self.max_answers
200    }
201
202    /// Override the query class.
203    #[must_use]
204    pub const fn with_qclass(mut self, v: ResourceClass) -> Self {
205      self.qclass = v;
206      self
207    }
208
209    /// Request unicast responses (RFC 6762 §5.4).
210    #[must_use]
211    pub const fn with_unicast_response(mut self, v: bool) -> Self {
212      self.unicast_response = v;
213      self
214    }
215
216    /// Set an absolute timeout for the query.
217    #[must_use]
218    pub const fn with_timeout(mut self, v: Duration) -> Self {
219      self.timeout = Some(v);
220      self
221    }
222
223    /// Override the maximum number of collected answers for this query.
224    ///
225    /// When the pool reaches `max`, the oldest entry (FIFO) is evicted to make
226    /// room.  Setting `max` to 0 disables answer collection entirely.
227    #[must_use]
228    pub const fn with_max_answers(mut self, max: usize) -> Self {
229      self.max_answers = Some(max);
230      self
231    }
232  }
233}
234
235#[cfg(test)]
236// QuerySpec / Name need a heap (or heapless) name store, so the test module is
237// gated to the tiers where they exist — `--no-default-features` is core-only.
238#[cfg(any(feature = "alloc", feature = "std"))]
239#[allow(clippy::unwrap_used)]
240mod tests;