oko_mdns/lib.rs
1//! [Multicast DNS](https://en.wikipedia.org/wiki/Multicast_DNS) library with built-in networking.
2//!
3//! This crate can be used to discover mDNS devices that are listening
4//! on a network.
5//!
6//! # Basic usage
7//!
8//! This example finds all [Chromecast](https://en.wikipedia.org/wiki/Chromecast) devices on the
9//! same LAN as the executing computer.
10//!
11//! Once the devices are discovered, they respond with standard DNS records, with a few minor
12//! low-level protocol differences.
13//!
14//! The only Chromecast-specific piece of code here is the `SERVICE_NAME`. In order to discover
15//! other types of devices, simply change the service name to the one your device uses.
16//!
17//! This example obtains the IP addresses of the cast devices by looking up `A`/`AAAA` records.
18//!
19//! ```rust,no_run
20//! use futures_util::{pin_mut, stream::StreamExt};
21//! use mdns::{Error, Record, RecordKind};
22//! use std::{net::IpAddr, time::Duration};
23//!
24//! /// The hostname of the devices we are searching for.
25//! /// Every Chromecast will respond to the service name in this example.
26//! const SERVICE_NAME: &'static str = "_googlecast._tcp.local";
27//!
28//! #[cfg_attr(feature = "runtime-async-std", async_std::main)]
29//! #[cfg_attr(feature = "runtime-tokio", tokio::main)]
30//! async fn main() -> Result<(), Error> {
31//! // Iterate through responses from each Cast device, asking for new devices every 15s
32//! let stream = mdns::discover::all(SERVICE_NAME, Duration::from_secs(15))?.listen();
33//! pin_mut!(stream);
34//!
35//! while let Some(Ok(response)) = stream.next().await {
36//! let addr = response.records()
37//! .filter_map(self::to_ip_addr)
38//! .next();
39//!
40//! if let Some(addr) = addr {
41//! println!("found cast device at {}", addr);
42//! } else {
43//! println!("cast device does not advertise address");
44//! }
45//! }
46//!
47//! Ok(())
48//! }
49//!
50//! fn to_ip_addr(record: &Record) -> Option<IpAddr> {
51//! match record.kind {
52//! RecordKind::A(addr) => Some(addr.into()),
53//! RecordKind::AAAA(addr) => Some(addr.into()),
54//! _ => None,
55//! }
56//! }
57//! ```
58
59#![recursion_limit = "1024"]
60
61#[cfg(all(feature = "runtime-async-std", feature = "runtime-tokio"))]
62compile_error!("\"runtime-async-std\" and \"runtime-tokio\" cannot be enabled simultaneously");
63
64#[cfg(not(any(feature = "runtime-async-std", feature = "runtime-tokio")))]
65compile_error!("At least one runtime (\"runtime-async-std\" or \"runtime-tokio\") cargo feature must be enabled");
66
67use std::net::SocketAddr;
68
69use async_trait::async_trait;
70use multicast_socket::MulticastSocket;
71
72pub use self::errors::Error;
73pub use self::response::{Record, RecordKind, Response, TxtRecordValue};
74
75pub mod discover;
76pub mod resolve;
77
78mod runtime;
79
80mod errors;
81mod mdns;
82mod response;
83
84pub(crate) enum IntermediateSocketType {
85 StdNetSocket(std::net::UdpSocket),
86 #[cfg(feature = "multihome")]
87 MultihomeSocket(MulticastSocket),
88}
89
90impl IntermediateSocket {
91 pub(crate) fn take_inner(self) -> IntermediateSocketType {
92 self.0
93 }
94}
95
96pub(crate) struct IntermediateSocket(IntermediateSocketType);
97
98impl From<std::net::UdpSocket> for IntermediateSocket {
99 fn from(value: std::net::UdpSocket) -> Self {
100 IntermediateSocket(IntermediateSocketType::StdNetSocket(value))
101 }
102}
103
104#[cfg(feature = "multihome")]
105impl From<multicast_socket::MulticastSocket> for IntermediateSocket {
106 fn from(value: multicast_socket::MulticastSocket) -> Self {
107 IntermediateSocket(IntermediateSocketType::MultihomeSocket(value))
108 }
109}
110
111#[async_trait]
112pub trait AsyncUdpSocket: Send + Clone {
113 async fn send_to(
114 &self,
115 buf: &[u8],
116 target: impl Into<SocketAddr> + Send,
117 ) -> std::io::Result<usize>;
118 async fn recv_from(&self, buf: &mut [u8]) -> std::io::Result<(usize, SocketAddr)>;
119}
120
121pub use self::mdns::mDNSListener;