Skip to content

Commit

Permalink
Merge pull request #94 from ssnover/zeroconf
Browse files Browse the repository at this point in the history
Add feature zeroconf in order to support avahi mDNS registration via zeroconf crate
  • Loading branch information
kedars committed Oct 6, 2023
2 parents 89f2bc4 + 7caf1fe commit 97c9619
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 0 deletions.
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)
}
}

0 comments on commit 97c9619

Please sign in to comment.