1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
//! A small and safe library for Multicast DNS-SD (Service Discovery).
//!
//! This library creates one new thread to run a mDNS daemon, and exposes
//! its API that interacts with the daemon via a
//! [`flume`](https://crates.io/crates/flume) channel. The channel supports
//! both `recv()` and `recv_async()`.
//!
//! For example, a client querying (browsing) a service behaves like this:
//!```text
//! Client <channel> mDNS daemon thread
//! | | starts its run-loop.
//! | --- Browse --> |
//! | | detects services
//! | | finds service instance A
//! | <-- Found A -- |
//! | ... | resolves service A
//! | <-- Resolved A -- |
//! | ... |
//!```
//! All commands in the public API are sent to the daemon using the unblocking `try_send()`
//! so that the caller can use it with both sync and async code, with no dependency on any
//! particular async runtimes.
//!
//! # Usage
//!
//! The user starts with creating a daemon by calling [`ServiceDaemon::new()`].
//! Then as a mDNS querier, the user would call [`browse`](`ServiceDaemon::browse`) to
//! search for services, and/or as a mDNS responder, call [`register`](`ServiceDaemon::register`)
//! to publish (i.e. announce) its own service. And, the daemon type can be cloned and passed
//! around between threads.
//!
//! ## Example: a client querying for a service type.
//!
//! ```rust
//! use mdns_sd::{ServiceDaemon, ServiceEvent};
//!
//! // Create a daemon
//! let mdns = ServiceDaemon::new().expect("Failed to create daemon");
//!
//! // Browse for a service type.
//! let service_type = "_mdns-sd-my-test._udp.local.";
//! let receiver = mdns.browse(service_type).expect("Failed to browse");
//!
//! // Receive the browse events in sync or async. Here is
//! // an example of using a thread. Users can call `receiver.recv_async().await`
//! // if running in async environment.
//! std::thread::spawn(move || {
//! while let Ok(event) = receiver.recv() {
//! match event {
//! ServiceEvent::ServiceResolved(info) => {
//! println!("Resolved a new service: {}", info.get_fullname());
//! }
//! other_event => {
//! println!("Received other event: {:?}", &other_event);
//! }
//! }
//! }
//! });
//! ```
//!
//! ## Example: a server publishs a service and responds to queries.
//!
//! ```rust
//! use mdns_sd::{ServiceDaemon, ServiceInfo};
//! use std::collections::HashMap;
//!
//! // Create a daemon
//! let mdns = ServiceDaemon::new().expect("Failed to create daemon");
//!
//! // Create a service info.
//! let service_type = "_mdns-sd-my-test._udp.local.";
//! let instance_name = "my_instance";
//! let host_ipv4 = "192.168.1.12";
//! let host_name = "192.168.1.12.local.";
//! let port = 5200;
//! let mut properties = HashMap::new();
//! properties.insert("property_1".to_string(), "test".to_string());
//! properties.insert("property_2".to_string(), "1234".to_string());
//!
//! let my_service = ServiceInfo::new(
//! service_type,
//! instance_name,
//! host_name,
//! host_ipv4,
//! port,
//! Some(properties),
//! ).unwrap();
//!
//! // Register with the daemon, which publishes the service.
//! mdns.register(my_service).expect("Failed to register our service");
//! ```
//!
//! # Limitations
//!
//! This implementation is based on the following RFCs:
//! - mDNS: [RFC 6762](https://tools.ietf.org/html/rfc6762)
//! - DNS-SD: [RFC 6763](https://tools.ietf.org/html/rfc6763)
//! - DNS: [RFC 1035](https://tools.ietf.org/html/rfc1035)
//!
//! We focus on the common use cases at first, and currently have the following limitations:
//! - Only support IPv4, not IPv6.
//! - Only support multicast, not unicast send/recv.
//! - Only tested on Linux and MacOS, not on Windows or other OSes.
#![forbid(unsafe_code)]
mod dns_parser;
mod error;
mod service_daemon;
mod service_info;
pub use error::{Error, Result};
pub use service_daemon::{Metrics, ServiceDaemon, ServiceEvent, UnregisterStatus};
pub use service_info::{AsIpv4Addrs, ServiceInfo};
/// A handler to receive messages from [ServiceDaemon]. Re-export from `flume` crate.
pub use flume::Receiver;