Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature zeroconf in order to support avahi mDNS registration via zeroconf crate #94

Merged
merged 6 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions rs-matter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ openssl = ["alloc", "dep:openssl", "foreign-types", "hmac", "sha2"]
mbedtls = ["alloc", "dep:mbedtls"]
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"]
zeroconf = ["dep:zeroconf"]

[dependencies]
rs-matter-macros = { version = "0.1", path = "../rs-matter-macros" }
Expand Down Expand Up @@ -76,6 +77,9 @@ x509-cert = { version = "0.2.0", default-features = false, features = ["pem"], o
[target.'cfg(target_os = "macos")'.dependencies]
astro-dnssd = { version = "0.3" }

[target.'cfg(target_os = "linux")'.dependencies]
zeroconf = { version = "0.12", optional = true }

[target.'cfg(not(target_os = "espidf"))'.dependencies]
mbedtls = { version = "0.9", optional = true }
env_logger = { version = "0.10.0", optional = true }
Expand Down
2 changes: 2 additions & 0 deletions rs-matter/src/mdns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ use crate::{data_model::cluster_basic_information::BasicInfoConfig, error::Error
pub mod astro;
pub mod builtin;
pub mod proto;
#[cfg(all(feature = "std", feature = "zeroconf", target_os = "linux"))]
pub mod zeroconf;

pub trait Mdns {
fn add(&self, service: &str, mode: ServiceMode) -> Result<(), Error>;
Expand Down
176 changes: 176 additions & 0 deletions rs-matter/src/mdns/zeroconf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
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_name = service.service.strip_prefix('_').unwrap_or(service.service);
let protocol = service
.protocol
.strip_prefix('_')
.unwrap_or(service.protocol);

let service_type = if !service.service_subtypes.is_empty() {
let subtypes = service
.service_subtypes
.into_iter()
.map(|subtype| subtype.strip_prefix('_').unwrap_or(*subtype))
.collect();

ServiceType::with_sub_types(service_name, protocol, subtypes)
} else {
ServiceType::new(service_name, 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()));
}

let name_copy = name.to_owned();

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_name(&name_copy);
mdns_service.set_txt_record(txt_record);
mdns_service.set_registered_callback(Box::new(|_, _| {}));

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)
}
}
2 changes: 1 addition & 1 deletion rs-matter/src/transport/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub use embassy_net_stack::*;

#[cfg(feature = "std")]
pub mod std_stack {
pub trait NetworkStackDriver {}
pub trait NetworkStackDriver: 'static {}

impl NetworkStackDriver for () {}

Expand Down
Loading