Skip to content

Commit

Permalink
Merge branch 'feature/allowed-ip-list'
Browse files Browse the repository at this point in the history
  • Loading branch information
willstott101 committed May 8, 2022
2 parents ea79f79 + ed813bb commit 870cd8d
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
23 changes: 23 additions & 0 deletions examples/register_with_ip_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pub fn main() {
let mut builder = env_logger::Builder::new();
builder.parse_filters("libmdns=debug");
builder.init();

// allow only these two IP address to be sent in A record
let vec: Vec<std::net::IpAddr> = vec![
"192.168.1.10".parse::<std::net::Ipv4Addr>().unwrap().into(),
std::net::Ipv6Addr::new(0, 0, 0, 0xfe80, 0x1ff, 0xfe23, 0x4567, 0x890a).into(),
];

let responder = libmdns::Responder::new_with_ip_list(vec).unwrap();
let _svc = responder.register(
"_http._tcp".to_owned(),
"libmdns Web Server".to_owned(),
80,
&["path=/"],
);

loop {
::std::thread::sleep(::std::time::Duration::from_secs(10));
}
}
12 changes: 11 additions & 1 deletion src/fsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,15 @@ pub struct FSM<AF: AddressFamily> {
commands: mpsc::UnboundedReceiver<Command>,
outgoing: VecDeque<(Vec<u8>, SocketAddr)>,
_af: PhantomData<AF>,
allowed_ip: Vec<IpAddr>,
}

impl<AF: AddressFamily> FSM<AF> {
// Will panic if called from outside the context of a runtime
pub fn new(services: &Services) -> io::Result<(FSM<AF>, mpsc::UnboundedSender<Command>)> {
pub fn new(
services: &Services,
allowed_ip: Vec<IpAddr>,
) -> io::Result<(FSM<AF>, mpsc::UnboundedSender<Command>)> {
let std_socket = AF::bind()?;
let socket = UdpSocket::from_std(std_socket)?;

Expand All @@ -53,6 +57,7 @@ impl<AF: AddressFamily> FSM<AF> {
commands: rx,
outgoing: VecDeque::new(),
_af: PhantomData,
allowed_ip: allowed_ip,
};

Ok((fsm, tx))
Expand Down Expand Up @@ -182,6 +187,11 @@ impl<AF: AddressFamily> FSM<AF> {
}

trace!("found interface {:?}", iface);
if !self.allowed_ip.is_empty() && !self.allowed_ip.contains(&iface.ip()) {
trace!(" -> interface dropped");
continue;
}

match (iface.ip(), AF::DOMAIN) {
(IpAddr::V4(ip), Domain::IPV4) => {
builder = builder.add_answer(hostname, QueryClass::IN, ttl, &RRData::A(ip))
Expand Down
33 changes: 28 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::cell::RefCell;
use std::future::Future;
use std::io;
use std::marker::Unpin;
use std::net::IpAddr;
use std::sync::{Arc, RwLock};

use std::thread;
Expand Down Expand Up @@ -39,8 +40,14 @@ pub struct Service {
type ResponderTask = Box<dyn Future<Output = ()> + Send + Unpin>;

impl Responder {
/// Spawn a responder task on an os thread.
/// Spawn a `Responder` task on an new os thread.
pub fn new() -> io::Result<Responder> {
Self::new_with_ip_list(Vec::new())
}
/// Spawn a `Responder` task on an new os thread.
/// DNS response records will have the reported IPs limited to those passed in here.
/// This can be particularly useful on machines with lots of networks created by tools such as docker.
pub fn new_with_ip_list(allowed_ips: Vec<IpAddr>) -> io::Result<Responder> {
let (tx, rx) = std::sync::mpsc::sync_channel(0);
thread::Builder::new()
.name("mdns-responder".to_owned())
Expand All @@ -50,7 +57,7 @@ impl Responder {
.build()
.unwrap();
rt.block_on(async {
match Self::with_default_handle() {
match Self::with_default_handle_and_ip_list(allowed_ips) {
Ok((responder, task)) => {
tx.send(Ok(responder)).expect("tx responder channel closed");
task.await;
Expand All @@ -77,13 +84,29 @@ impl Responder {
/// # }
/// ```
pub fn spawn(handle: &Handle) -> io::Result<Responder> {
let (responder, task) = Self::with_default_handle()?;
Self::spawn_with_ip_list(handle, Vec::new())
}

/// Spawn a `Responder` task with the provided tokio `Handle`.
/// DNS response records will have the reported IPs limited to those passed in here.
/// This can be particularly useful on machines with lots of networks created by tools such as docker.
pub fn spawn_with_ip_list(handle: &Handle, allowed_ips: Vec<IpAddr>) -> io::Result<Responder> {
let (responder, task) = Self::with_default_handle_and_ip_list(allowed_ips)?;
handle.spawn(task);
Ok(responder)
}

/// Spawn a `Responder` on the default tokio handle.
pub fn with_default_handle() -> io::Result<(Responder, ResponderTask)> {
Self::with_default_handle_and_ip_list(Vec::new())
}

/// Spawn a `Responder` on the default tokio handle.
/// DNS response records will have the reported IPs limited to those passed in here.
/// This can be particularly useful on machines with lots of networks created by tools such as docker.
pub fn with_default_handle_and_ip_list(
allowed_ips: Vec<IpAddr>,
) -> io::Result<(Responder, ResponderTask)> {
let mut hostname = hostname::get()?.into_string().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidData, "Hostname not valid unicode")
})?;
Expand All @@ -94,8 +117,8 @@ impl Responder {

let services = Arc::new(RwLock::new(ServicesInner::new(hostname)));

let v4 = FSM::<Inet>::new(&services);
let v6 = FSM::<Inet6>::new(&services);
let v4 = FSM::<Inet>::new(&services, allowed_ips.clone());
let v6 = FSM::<Inet6>::new(&services, allowed_ips);

let (task, commands): (ResponderTask, _) = match (v4, v6) {
(Ok((v4_task, v4_command)), Ok((v6_task, v6_command))) => {
Expand Down

0 comments on commit 870cd8d

Please sign in to comment.