# `mdns-sd-discovery`

[](https://crates.io/crates/mdns-sd-discovery)
[](https://github.com/abbyssoul/mdns_sd_discovery/actions/workflows/ci-test.yml)
[](https://docs.rs/mdns-sd-discovery)
[](LICENSE)
[](https://sonarcloud.io/summary/new_code?id=abbyssoul_mdns_sd_discovery)
Access the operating system's built-in
[DNS-SD](https://en.wikipedia.org/wiki/Zero-configuration_networking#DNS-based_service_discovery) /
[mDNS](https://en.wikipedia.org/wiki/Multicast_DNS) stack for service discovery.
This crate provides a cross-platform async API (using [Tokio](https://tokio.rs)) to browse
DNS-SD services via native OS facilities — no bundled mDNS implementation, no extra system
dependencies to install. Service _registration_ lives in a separate crate.
## Platform Support
| macOS | native [DNS-SD framework](https://developer.apple.com/documentation/dnssd) | macOS 10.12 |
| Windows | native [Win32 DNS-SD API](https://learn.microsoft.com/en-us/uwp/api/windows.networking.servicediscovery.dnssd?view=winrt-28000) | Windows 10 |
| Linux / BSD | [Avahi](https://avahi.org) via D-Bus | Avahi daemon running |
- **macOS** and **Windows** link against system libraries that are always present on supported OS versions.
- **Linux/FreeBSD** communicate with Avahi over D-Bus — there is no binary dependency on `libavahi` or the Bonjour compatibility layer. Binaries will run on systems without Avahi installed but return an error when attempting to browse.
## Why Use the OS Stack?
There exist various crates implementing DNS-SD/mDNS in pure Rust. Compared to these, using the operating system's DNS-SD stack has the following benefits:
- **Battle-tested**: OS stacks have been tested widely over many years (sometimes decades) to handle OS-dependent edge cases (sleep/wake, interface changes) properly
- **Shared cache**: all applications on the system share a single DNS-SD/mDNS responder & cache, reducing network traffic and keeping answers consistent.
- **Smaller binary**: no embedded mDNS responder; just thin FFI/D-Bus bindings.
There also exist various crates implementing a wrapper on top of the Apple DNS-SD API, which is natively available on macOS, but only on
Windows if users install Bonjour for Windows, and on Linux if the `libavahi-compat-libdnssd` library is installed. Compared to these, this
crate requires no additional dependencies to install for end users.
## Examples
### Browse for services
By default a `ServiceBrowser` discovers **all** service types on the network; filter on
`DiscoveredService::service_type`, or narrow to a single type with `.service_type("_http._tcp")`.
```rust
use mdns_sd_discovery::{BrowseEvent, ServiceBrowserBuilder};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut browser = ServiceBrowserBuilder::new().browse().await?;
while let Some(event) = browser.recv().await {
match event? {
BrowseEvent::Found(svc) => {
println!("+ [{}] {} at {}:{}", svc.service_type, svc.name, svc.host_name, svc.port);
}
BrowseEvent::Removed(svc) => println!("- [{}] {}", svc.service_type, svc.name),
}
}
// Dropping `browser` stops the browse.
Ok(())
}
```
### Re-confirm a service is still alive
`ServiceResolverBuilder` resolves a single, already-known instance *now*, mapping
the identity fields from a previous browse event back to a connectable
`DiscoveredService`. A vanished instance fails with
`ServiceBrowseError::ResolveFailed` (rather than hanging), making it a useful
liveness probe — handy for the *no-goodbye* case where a host powered off or
dropped off the network without multicasting mDNS goodbye packets, so no
`Removed` event is delivered until the browse PTR record's (long) TTL expires.
```rust
use mdns_sd_discovery::{ServiceBrowseError, ServiceResolverBuilder};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let result = ServiceResolverBuilder::new("My Web Server", "_http._tcp", "local")
.timeout(Duration::from_secs(5))
.resolve()
.await;
match result {
Ok(svc) => println!("still alive at {}:{}", svc.host_name, svc.port),
Err(ServiceBrowseError::ResolveFailed(name, _)) => println!("{name} is gone"),
Err(err) => return Err(err.into()),
}
Ok(())
}
```
See the [`examples/`](examples/) directory for a full CLI tool that browses
(`discover-service`) services; pass `--resolve NAME --type _http._tcp` to probe a
single instance instead.
> **Platform note:** on Windows, service _removal_ (`BrowseEvent::Removed`) is not currently
> reported; appearances and resolution are. Removal events are delivered on macOS and Linux.
## License
[MIT](LICENSE)