Implement linux mdns with avahi/zerconf
This commit is contained in:
parent
320d1ec989
commit
65661f13db
3 changed files with 170 additions and 0 deletions
|
@ -22,6 +22,7 @@ openssl = ["alloc", "dep:openssl", "foreign-types", "hmac", "sha2"]
|
||||||
mbedtls = ["alloc", "dep:mbedtls"]
|
mbedtls = ["alloc", "dep:mbedtls"]
|
||||||
rustcrypto = ["alloc", "sha2", "hmac", "pbkdf2", "hkdf", "aes", "ccm", "p256", "elliptic-curve", "crypto-bigint", "x509-cert", "rand_core"]
|
rustcrypto = ["alloc", "sha2", "hmac", "pbkdf2", "hkdf", "aes", "ccm", "p256", "elliptic-curve", "crypto-bigint", "x509-cert", "rand_core"]
|
||||||
embassy-net = ["dep:embassy-net", "dep:embassy-net-driver", "smoltcp"]
|
embassy-net = ["dep:embassy-net", "dep:embassy-net-driver", "smoltcp"]
|
||||||
|
zeroconf = ["dep:zeroconf"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rs-matter-macros = { version = "0.1", path = "../rs-matter-macros" }
|
rs-matter-macros = { version = "0.1", path = "../rs-matter-macros" }
|
||||||
|
@ -76,6 +77,9 @@ x509-cert = { version = "0.2.0", default-features = false, features = ["pem"], o
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
astro-dnssd = { version = "0.3" }
|
astro-dnssd = { version = "0.3" }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
zeroconf = { version = "0.11.1", optional = true }
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "espidf"))'.dependencies]
|
[target.'cfg(not(target_os = "espidf"))'.dependencies]
|
||||||
mbedtls = { version = "0.9", optional = true }
|
mbedtls = { version = "0.9", optional = true }
|
||||||
env_logger = { version = "0.10.0", optional = true }
|
env_logger = { version = "0.10.0", optional = true }
|
||||||
|
|
|
@ -23,6 +23,8 @@ use crate::{data_model::cluster_basic_information::BasicInfoConfig, error::Error
|
||||||
pub mod astro;
|
pub mod astro;
|
||||||
pub mod builtin;
|
pub mod builtin;
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
|
#[cfg(all(feature = "std", feature = "zeroconf", target_os = "linux"))]
|
||||||
|
pub mod zeroconf;
|
||||||
|
|
||||||
pub trait Mdns {
|
pub trait Mdns {
|
||||||
fn add(&self, service: &str, mode: ServiceMode) -> Result<(), Error>;
|
fn add(&self, service: &str, mode: ServiceMode) -> Result<(), Error>;
|
||||||
|
|
164
rs-matter/src/mdns/zeroconf.rs
Normal file
164
rs-matter/src/mdns/zeroconf.rs
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::mpsc::{sync_channel, SyncSender};
|
||||||
|
|
||||||
|
use super::{MdnsRunBuffers, ServiceMode};
|
||||||
|
use crate::{
|
||||||
|
data_model::cluster_basic_information::BasicInfoConfig,
|
||||||
|
error::{Error, ErrorCode},
|
||||||
|
transport::pipe::Pipe,
|
||||||
|
};
|
||||||
|
use zeroconf::{prelude::TEventLoop, service::TMdnsService, txt_record::TTxtRecord, ServiceType};
|
||||||
|
|
||||||
|
/// Only for API-compatibility with builtin::MdnsRunner
|
||||||
|
pub struct MdnsUdpBuffers(());
|
||||||
|
|
||||||
|
/// Only for API-compatibility with builtin::MdnsRunner
|
||||||
|
impl MdnsUdpBuffers {
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MdnsService<'a> {
|
||||||
|
dev_det: &'a BasicInfoConfig<'a>,
|
||||||
|
matter_port: u16,
|
||||||
|
services: RefCell<HashMap<String, SyncSender<()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MdnsService<'a> {
|
||||||
|
/// This constructor takes extra parameters for API-compatibility with builtin::MdnsRunner
|
||||||
|
pub fn new(
|
||||||
|
_id: u16,
|
||||||
|
_hostname: &str,
|
||||||
|
_ip: [u8; 4],
|
||||||
|
_ipv6: Option<([u8; 16], u32)>,
|
||||||
|
dev_det: &'a BasicInfoConfig<'a>,
|
||||||
|
matter_port: u16,
|
||||||
|
) -> Self {
|
||||||
|
Self::native_new(dev_det, matter_port)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn native_new(dev_det: &'a BasicInfoConfig<'a>, matter_port: u16) -> Self {
|
||||||
|
Self {
|
||||||
|
dev_det,
|
||||||
|
matter_port,
|
||||||
|
services: RefCell::new(HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&self, name: &str, mode: ServiceMode) -> Result<(), Error> {
|
||||||
|
log::info!("Registering mDNS service {}/{:?}", name, mode);
|
||||||
|
|
||||||
|
let _ = self.remove(name);
|
||||||
|
|
||||||
|
mode.service(self.dev_det, self.matter_port, name, |service| {
|
||||||
|
let service_type = if !service.service_subtypes.is_empty() {
|
||||||
|
ServiceType::with_sub_types(
|
||||||
|
service.service,
|
||||||
|
service.protocol,
|
||||||
|
Vec::from(service.service_subtypes),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ServiceType::new(service.service, service.protocol)
|
||||||
|
}
|
||||||
|
.map_err(|err| {
|
||||||
|
log::error!(
|
||||||
|
"Encountered error building service type: {}",
|
||||||
|
err.to_string()
|
||||||
|
);
|
||||||
|
ErrorCode::MdnsError
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let (sender, receiver) = sync_channel(1);
|
||||||
|
|
||||||
|
let service_port = service.port;
|
||||||
|
let mut txt_kvs = vec![];
|
||||||
|
for (k, v) in service.txt_kvs {
|
||||||
|
txt_kvs.push((k.to_string(), v.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let mut mdns_service = zeroconf::MdnsService::new(service_type, service_port);
|
||||||
|
|
||||||
|
let mut txt_record = zeroconf::TxtRecord::new();
|
||||||
|
for (k, v) in txt_kvs {
|
||||||
|
log::info!("mDNS TXT key {k} val {v}");
|
||||||
|
if let Err(err) = txt_record.insert(&k, &v) {
|
||||||
|
log::error!(
|
||||||
|
"Encountered error inserting kv-pair into txt record {}",
|
||||||
|
err.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mdns_service.set_txt_record(txt_record);
|
||||||
|
|
||||||
|
match mdns_service.register() {
|
||||||
|
Ok(event_loop) => loop {
|
||||||
|
if let Ok(()) = receiver.try_recv() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if let Err(err) = event_loop.poll(std::time::Duration::from_secs(1)) {
|
||||||
|
log::error!(
|
||||||
|
"Failed to poll mDNS service event loop: {}",
|
||||||
|
err.to_string()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => log::error!(
|
||||||
|
"Encountered error registering mDNS service: {}",
|
||||||
|
err.to_string()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.services.borrow_mut().insert(name.to_owned(), sender);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&self, name: &str) -> Result<(), Error> {
|
||||||
|
if let Some(cancellation_notice) = self.services.borrow_mut().remove(name) {
|
||||||
|
log::info!("Deregistering mDNS service {}", name);
|
||||||
|
cancellation_notice
|
||||||
|
.send(())
|
||||||
|
.map_err(|_| ErrorCode::MdnsError)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only for API-compatibility with builtin::MdnsRunner
|
||||||
|
pub async fn run_piped(
|
||||||
|
&mut self,
|
||||||
|
_tx_pipe: &Pipe<'_>,
|
||||||
|
_rx_pipe: &Pipe<'_>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
core::future::pending::<Result<(), Error>>().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only for API-compatibility with builtin::MdnsRunner
|
||||||
|
pub async fn run<D>(
|
||||||
|
&self,
|
||||||
|
_stack: &crate::transport::network::NetworkStack<D>,
|
||||||
|
_buffers: &mut MdnsRunBuffers,
|
||||||
|
) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
D: crate::transport::network::NetworkStackDriver,
|
||||||
|
{
|
||||||
|
core::future::pending::<Result<(), Error>>().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> super::Mdns for MdnsService<'a> {
|
||||||
|
fn add(&self, service: &str, mode: ServiceMode) -> Result<(), Error> {
|
||||||
|
MdnsService::add(self, service, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(&self, service: &str) -> Result<(), Error> {
|
||||||
|
MdnsService::remove(self, service)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue