Skip to content

Commit

Permalink
mDNS example
Browse files Browse the repository at this point in the history
  • Loading branch information
ivmarkov committed Jan 21, 2024
1 parent 2b7375c commit 4d9adf8
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 76 deletions.
76 changes: 76 additions & 0 deletions edge-mdns/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# edge-mdns

[![CI](https://github.com/ivmarkov/edge-net/actions/workflows/ci.yml/badge.svg)](https://github.com/ivmarkov/edge-net/actions/workflows/ci.yml)
![crates.io](https://img.shields.io/crates/v/edge-net.svg)
[![Documentation](https://docs.rs/edge-net/badge.svg)](https://docs.rs/edge-net)

Async + `no_std` + no-alloc implementation of an mDNS responder.

The implementation is based on the splendid [domain](https://github.com/NLnetLabs/domain) library.

For other protocols, look at the [edge-net](https://github.com/ivmarkov/edge-net) aggregator crate documentation.

## Example

```rust
use embedded_nal_async::{Ipv4Addr, UdpStack};
use embedded_nal_async_xtra::Multicast;

use edge_mdns::io::{self, MdnsIoError, MdnsRunBuffers, UdpSplitBuffer, DEFAULT_SOCKET};
use edge_mdns::Host;

use edge_std_nal_async::StdUdpStack;

use log::*;

// Change this to the IP address of the machine where you'll run this example
const OUR_IP: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);

const OUR_NAME: &str = "mypc";

fn main() {
env_logger::init_from_env(
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
);

let stack = StdUdpStack::new();

let mut udp_buffer = UdpSplitBuffer::new();
let mut buffers = MdnsRunBuffers::new();

futures_lite::future::block_on(run(&stack, &mut udp_buffer, &mut buffers, OUR_NAME, OUR_IP))
.unwrap();
}

async fn run<T: UdpStack>(
stack: &T,
udp_buffer: &mut UdpSplitBuffer,
buffers: &mut MdnsRunBuffers,
our_name: &str,
our_ip: Ipv4Addr,
) -> Result<(), MdnsIoError<T::Error>>
where
T: UdpStack,
<T as UdpStack>::UniquelyBound: Multicast<Error = T::Error>,
{
info!("About to run an mDNS responder for our PC. It will be addressable using {our_name}.local, so try to `ping {our_name}.local`.");

let host = Host {
id: 0,
hostname: our_name,
ip: our_ip.octets(),
ipv6: None,
};

io::run(
&host,
Some(0),
[],
stack,
DEFAULT_SOCKET,
udp_buffer,
buffers,
)
.await
}
```
34 changes: 21 additions & 13 deletions edge-mdns/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ use embedded_nal_async_xtra::Multicast;

use log::{info, warn};

use self::split::{UdpSplit, UdpSplitBuffer, UdpSplitReceive, UdpSplitSend};
use self::split::{UdpSplit, UdpSplitReceive, UdpSplitSend};

use super::*;

pub use split::UdpSplitBuffer;

mod split;

pub const DEFAULT_SOCKET: SocketAddr = SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), PORT);
Expand Down Expand Up @@ -71,7 +73,7 @@ impl MdnsRunBuffers {
}
}

pub async fn run<T, S>(
pub async fn run<'s, T, S>(
host: &Host<'_>,
interface: Option<u32>,
services: T,
Expand All @@ -81,7 +83,7 @@ pub async fn run<T, S>(
buffers: &mut MdnsRunBuffers,
) -> Result<(), MdnsIoError<S::Error>>
where
T: Services,
T: IntoIterator<Item = Service<'s>> + Clone,
S: UdpStack,
S::UniquelyBound: Multicast<Error = S::Error>,
{
Expand All @@ -106,8 +108,14 @@ where

let send = Mutex::<NoopRawMutex, _>::new((send, send_buf));

let mut broadcast = pin!(broadcast(host, &services, local_addr, interface, &send));
let mut respond = pin!(respond(host, &services, recv, recv_buf, &send));
let mut broadcast = pin!(broadcast(
host,
services.clone(),
local_addr,
interface,
&send
));
let mut respond = pin!(respond(host, services, recv, recv_buf, &send));

let result = select(&mut broadcast, &mut respond).await;

Expand All @@ -117,20 +125,18 @@ where
}
}

async fn broadcast<T, S, E>(
async fn broadcast<'s, T, S, E>(
host: &Host<'_>,
services: T,
local_addr: SocketAddr,
interface: Option<u32>,
send: &Mutex<impl RawMutex, (UdpSplitSend<'_, impl RawMutex, S>, &mut [u8])>,
) -> Result<(), MdnsIoError<E>>
where
T: Services,
T: IntoIterator<Item = Service<'s>> + Clone,
S: UnconnectedUdp<Error = E> + Multicast<Error = E>,
{
loop {
Timer::after(Duration::from_secs(30)).await;

for remote_addr in
core::iter::once(SocketAddr::V4(SocketAddrV4::new(IP_BROADCAST_ADDR, PORT))).chain(
interface
Expand All @@ -143,7 +149,7 @@ where
let mut guard = send.lock().await;
let (send, send_buf) = &mut *guard;

let len = host.broadcast(&services, send_buf, 60)?;
let len = host.broadcast(services.clone(), send_buf, 60)?;

if len > 0 {
info!("Broadcasting mDNS entry to {remote_addr}");
Expand All @@ -152,18 +158,20 @@ where
.map_err(MdnsIoError::IoError)?;
}
}

Timer::after(Duration::from_secs(30)).await;
}
}

async fn respond<T, S>(
async fn respond<'s, T, S>(
host: &Host<'_>,
services: T,
mut recv: UdpSplitReceive<'_, impl RawMutex, S>,
recv_buf: &mut [u8],
send: &Mutex<impl RawMutex, (UdpSplitSend<'_, impl RawMutex, S>, &mut [u8])>,
) -> Result<(), MdnsIoError<S::Error>>
where
T: Services,
T: IntoIterator<Item = Service<'s>> + Clone,
S: UnconnectedUdp,
{
loop {
Expand All @@ -175,7 +183,7 @@ where
let mut guard = send.lock().await;
let (send, send_buf) = &mut *guard;

let len = match host.respond(&services, &recv_buf[..len], send_buf, 60) {
let len = match host.respond(services.clone(), &recv_buf[..len], send_buf, 60) {
Ok(len) => len,
Err(err) => match err {
MdnsError::InvalidMessage => {
Expand Down
Loading

0 comments on commit 4d9adf8

Please sign in to comment.