1use core::{
4 net::{IpAddr, Ipv4Addr, Ipv6Addr},
5 time::Duration,
6};
7use std::{
8 io::{self, ErrorKind},
9 rc::Rc,
10 time::Instant,
11};
12
13use futures::future::select_all;
14use hick_trace::*;
15use hick_udp::{
16 MulticastOptionsV4, MulticastOptionsV6, try_bind_v4, try_bind_v6, try_join_v4, try_join_v6,
17};
18use mdns_proto::{
19 Name, QuerySpec, ServiceSpec,
20 wire::{A, AAAA, ResourceType},
21};
22
23use crate::{
24 discovery::{Lookup, QueryParam, Resolver, ServiceEntry, Step, feed, fold, push_capped},
25 driver::{EndpointInner, run},
26 error::{RegisterError, ServerError, StartQueryError},
27 options::ServerOptions,
28 query::{Query, QueryEvent},
29 service::Service,
30 socket::Socket,
31};
32
33#[derive(Clone)]
40pub struct Endpoint {
41 pub(crate) inner: Rc<EndpointInner>,
42}
43
44impl Endpoint {
45 pub async fn server(opts: ServerOptions) -> Result<Self, ServerError> {
48 if !opts.ipv4() && !opts.ipv6() {
49 return Err(ServerError::NoFamilyEnabled);
50 }
51
52 let interface_index = match opts.interface_index() {
53 Some(i) => i,
54 None => pick_default_interface_index(opts.ipv4(), opts.ipv6()).ok_or_else(|| {
55 ServerError::Io(io::Error::new(
56 ErrorKind::NotFound,
57 "no multicast-capable interface found",
58 ))
59 })?,
60 };
61
62 let iface_has_v4 = match getifs::interface_by_index(interface_index) {
66 Ok(Some(i)) => matches!(i.ipv4_addrs(), Ok(ref a) if !a.is_empty()),
67 _ => false,
68 };
69 let iface_has_v6 = match getifs::interface_by_index(interface_index) {
70 Ok(Some(i)) => matches!(i.ipv6_addrs(), Ok(ref a) if !a.is_empty()),
71 _ => false,
72 };
73 let bind_v4 = opts.ipv4() && iface_has_v4;
74 let bind_v6 = opts.ipv6() && iface_has_v6;
75 if !bind_v4 && !bind_v6 {
76 return Err(ServerError::Io(io::Error::new(
77 ErrorKind::AddrNotAvailable,
78 "interface has no address in any requested family",
79 )));
80 }
81
82 let std_v4 = if bind_v4 {
83 match try_bind_v4(MulticastOptionsV4::new(interface_index)) {
84 Ok(s) => {
85 debug!(interface_index, "bound v4 mDNS socket");
86 match try_join_v4(&s, interface_index) {
90 Ok(()) => debug!(interface_index, "joined v4 mDNS multicast group"),
91 Err(e) => {
92 warn!(error = %e, interface_index, "failed to join v4 mDNS multicast group");
93 return Err(ServerError::JoinV4(e));
94 }
95 }
96 s.set_nonblocking(true).map_err(ServerError::Io)?;
97 Some(s)
98 }
99 Err(e) => {
100 warn!(error = %e, interface_index, "failed to bind v4 mDNS socket");
101 return Err(ServerError::BindV4(e));
102 }
103 }
104 } else {
105 None
106 };
107
108 let std_v6 = if bind_v6 {
109 match try_bind_v6(MulticastOptionsV6::new(interface_index)) {
110 Ok(s) => {
111 debug!(interface_index, "bound v6 mDNS socket");
112 match try_join_v6(&s, interface_index) {
113 Ok(()) => debug!(interface_index, "joined v6 mDNS multicast group"),
114 Err(e) => {
115 warn!(error = %e, interface_index, "failed to join v6 mDNS multicast group");
116 return Err(ServerError::JoinV6(e));
117 }
118 }
119 s.set_nonblocking(true).map_err(ServerError::Io)?;
120 Some(s)
121 }
122 Err(e) => {
123 warn!(error = %e, interface_index, "failed to bind v6 mDNS socket");
124 return Err(ServerError::BindV6(e));
125 }
126 }
127 } else {
128 None
129 };
130
131 let sock_v4 = if let Some(s) = std_v4 {
132 Some(Rc::new(
133 Socket::from_std(s).await.map_err(ServerError::WrapSocket)?,
134 ))
135 } else {
136 None
137 };
138 let sock_v6 = if let Some(s) = std_v6 {
139 Some(Rc::new(
140 Socket::from_std(s).await.map_err(ServerError::WrapSocket)?,
141 ))
142 } else {
143 None
144 };
145
146 let inner = EndpointInner::new(
147 *opts.endpoint_config(),
148 opts.max_payload_size(),
149 opts.max_recv_packet_size(),
150 );
151
152 {
155 let mut st = inner.state.borrow_mut();
156 st.bound_interface = interface_index;
157 st.local_subnets = collect_local_subnets(interface_index);
158 }
159
160 let driver_fut = run(inner.clone(), sock_v4, sock_v6);
161 compio_runtime::spawn(driver_fut).detach();
162
163 Ok(Self { inner })
164 }
165
166 #[cfg(feature = "stats")]
173 #[cfg_attr(docsrs, doc(cfg(feature = "stats")))]
174 pub fn stats(&self) -> stats::StatsSnapshot {
175 self.inner.state.borrow().stats.snapshot()
176 }
177
178 pub async fn register_service(&self, spec: ServiceSpec) -> Result<Service, RegisterError> {
188 let now = Instant::now();
189 let mailbox = crate::service::new_service_mailbox();
193 let handle = {
194 let mut st = self.inner.state.borrow_mut();
195 st.register_service(spec, now, std::rc::Rc::clone(&mailbox))
196 .map_err(RegisterError::from)?
197 };
198 self.inner.mark_dirty();
202 Ok(Service {
203 inner: self.inner.clone(),
204 handle,
205 mailbox,
206 })
207 }
208
209 pub async fn start_query(&self, spec: QuerySpec) -> Result<Query, StartQueryError> {
215 let now = Instant::now();
216 let handle = {
217 let mut st = self.inner.state.borrow_mut();
218 st.start_query(spec, now)
219 .map_err(|_| StartQueryError::StorageFull)?
220 };
221 self.inner.mark_dirty();
226 Ok(Query {
227 inner: self.inner.clone(),
228 handle,
229 terminal_delivered: core::cell::Cell::new(false),
230 })
231 }
232
233 pub async fn browse(&self, param: QueryParam) -> Result<Lookup, StartQueryError> {
236 let resolve_timeout = param.resolve_timeout.unwrap_or(param.timeout);
237 let ptr_spec = QuerySpec::new(param.service, ResourceType::Ptr)
242 .with_timeout(param.timeout)
243 .with_unicast_response(param.unicast_response)
244 .with_max_answers(param.max_entries);
245 let ptr_query = self.start_query(ptr_spec).await?;
246 Ok(Lookup {
247 endpoint: self.clone(),
248 queries: vec![(ptr_query, Step::Ptr)],
249 resolver: Resolver::new(param.max_entries),
250 resolve_timeout,
251 unicast: param.unicast_response,
252 finished: false,
253 })
254 }
255
256 pub async fn lookup(&self, service: Name, timeout: Duration) -> Result<Lookup, StartQueryError> {
259 self
260 .browse(QueryParam::new(service).with_timeout(timeout))
261 .await
262 }
263
264 pub async fn resolve_host(
274 &self,
275 host: Name,
276 timeout: Duration,
277 ) -> Result<Vec<IpAddr>, StartQueryError> {
278 let deadline = Instant::now().checked_add(timeout);
283 let remaining = || deadline.map_or(timeout, |d| d.saturating_duration_since(Instant::now()));
284 let host_key = fold(&host);
285 let qa = self
286 .start_query(QuerySpec::new(host.clone(), ResourceType::A).with_timeout(remaining()))
287 .await?;
288 let qaaaa = self
289 .start_query(QuerySpec::new(host, ResourceType::AAAA).with_timeout(remaining()))
290 .await?;
291 let mut ipv4: Vec<Ipv4Addr> = Vec::new();
292 let mut ipv6: Vec<Ipv6Addr> = Vec::new();
293 let mut queries: Vec<(Query, Step)> = vec![
294 (qa, Step::A(host_key.clone())),
295 (qaaaa, Step::AAAA(host_key)),
296 ];
297 while !queries.is_empty() {
298 let (result, idx) = {
299 let futs: Vec<_> = queries.iter().map(|(q, _)| Box::pin(q.next())).collect();
300 let (result, idx, _remaining) = select_all(futs).await;
301 (result, idx)
302 };
303 match result {
304 Some(QueryEvent::Answer(ans)) => match &queries[idx].1 {
305 Step::A(_) if ans.rtype() == ResourceType::A => {
306 if let Ok(r) = A::try_from_rdata(ans.rdata_slice()) {
307 push_capped(&mut ipv4, r.addr());
308 }
309 }
310 Step::AAAA(_) if ans.rtype() == ResourceType::AAAA => {
311 if let Ok(r) = AAAA::try_from_rdata(ans.rdata_slice()) {
312 push_capped(&mut ipv6, r.addr());
313 }
314 }
315 _ => {}
316 },
317 Some(QueryEvent::Terminal(_)) | None => {
318 queries.swap_remove(idx);
319 }
320 }
321 }
322 Ok(
323 ipv4
324 .into_iter()
325 .map(IpAddr::V4)
326 .chain(ipv6.into_iter().map(IpAddr::V6))
327 .collect(),
328 )
329 }
330
331 pub async fn resolve_instance(
341 &self,
342 instance: Name,
343 timeout: Duration,
344 ) -> Result<Option<ServiceEntry>, StartQueryError> {
345 let deadline = Instant::now().checked_add(timeout);
348 let remaining = || deadline.map_or(timeout, |d| d.saturating_duration_since(Instant::now()));
349 let mut resolver = Resolver::new(1);
350 let mut queries: Vec<(Query, Step)> = Vec::new();
351 for start in resolver.on_ptr(instance) {
352 let qtype = start.qtype();
353 let q = self
354 .start_query(QuerySpec::new(start.name, qtype).with_timeout(remaining()))
355 .await?;
356 queries.push((q, start.step));
357 }
358 loop {
359 if let Some(e) = resolver.take_ready() {
360 return Ok(Some(e));
361 }
362 if queries.is_empty() {
363 return Ok(None);
364 }
365 let (result, idx) = {
366 let futs: Vec<_> = queries.iter().map(|(q, _)| Box::pin(q.next())).collect();
367 let (result, idx, _remaining) = select_all(futs).await;
368 (result, idx)
369 };
370 match result {
371 Some(QueryEvent::Answer(_)) => {
372 let step = queries[idx].1.clone();
373 let event = result.expect("matched Some above");
374 for start in feed(&mut resolver, step, event) {
375 let qtype = start.qtype();
376 let q = self
377 .start_query(QuerySpec::new(start.name, qtype).with_timeout(remaining()))
378 .await?;
379 queries.push((q, start.step));
380 }
381 }
382 Some(QueryEvent::Terminal(_)) | None => {
383 queries.swap_remove(idx);
384 }
385 }
386 }
387 }
388}
389
390impl Drop for Endpoint {
395 fn drop(&mut self) {
396 self.inner.notify.notify();
397 }
398}
399
400fn collect_local_subnets(iface_index: u32) -> Vec<(IpAddr, u8)> {
404 let mut out: Vec<(IpAddr, u8)> = Vec::new();
405 if iface_index == 0 {
406 return out;
407 }
408 if let Ok(Some(i)) = getifs::interface_by_index(iface_index) {
409 if let Ok(v4s) = i.ipv4_addrs() {
410 for n in v4s.iter() {
411 out.push((IpAddr::V4(n.addr()), n.prefix_len()));
412 }
413 }
414 if let Ok(v6s) = i.ipv6_addrs() {
415 for n in v6s.iter() {
416 out.push((IpAddr::V6(n.addr()), n.prefix_len()));
417 }
418 }
419 }
420 out
421}
422
423fn pick_default_interface_index(want_v4: bool, want_v6: bool) -> Option<u32> {
429 let ifs = getifs::interfaces().ok()?;
430 let has_v4 = |i: &getifs::Interface| matches!(i.ipv4_addrs(), Ok(ref v) if !v.is_empty());
431 let has_v6 = |i: &getifs::Interface| matches!(i.ipv6_addrs(), Ok(ref v) if !v.is_empty());
432 let multicast_up_non_loopback = |i: &getifs::Interface| -> bool {
433 let f = i.flags();
434 f.contains(getifs::Flags::UP)
435 && f.contains(getifs::Flags::MULTICAST)
436 && !f.contains(getifs::Flags::LOOPBACK)
437 && i.index() != 0
438 };
439 let loopback_up = |i: &getifs::Interface| -> bool {
440 i.flags().contains(getifs::Flags::LOOPBACK) && i.flags().contains(getifs::Flags::UP)
441 };
442 let strict =
443 |i: &&getifs::Interface| -> bool { (!want_v4 || has_v4(i)) && (!want_v6 || has_v6(i)) };
444 let loose = |i: &&getifs::Interface| -> bool { (want_v4 && has_v4(i)) || (want_v6 && has_v6(i)) };
445 ifs
446 .iter()
447 .find(|i| multicast_up_non_loopback(i) && strict(i))
448 .or_else(|| {
449 ifs
450 .iter()
451 .find(|i| multicast_up_non_loopback(i) && loose(i))
452 })
453 .or_else(|| ifs.iter().find(|i| loopback_up(i) && strict(i)))
454 .or_else(|| ifs.iter().find(|i| loopback_up(i) && loose(i)))
455 .map(|i| i.index())
456}
457
458#[cfg(test)]
459mod tests;