fix(mdns): retain origin and bound daemon state
Vendor the pinned mdns-sd 0.21.1 source so response records retain their observed source IP. Bound unauthenticated cache records to 1024 globally and 128 per source, deduplicate and cap timers at 4096, and poll at least once per second for expiry cleanup. Expose packet provenance through lanspread-mdns, drop originless resolutions, and add a dedicated vendor test recipe while keeping third-party sources outside workspace formatting and Clippy. Test Plan: - just mdns-vendor-test (106 tests passed with socket access) - just test - just fmt - just clippy - cache/per-source, exact-refresh, timer-cap, and serde tests - git diff --check
This commit is contained in:
1 parent
49f8eef7b5
commit
4d5881d6b0
23 files changed
+18879
-41
No files matched your search
Vendored
+100
@@ -0,0 +1,100 @@
|
||||
//! A mDNS query client.
|
||||
//!
|
||||
//! Run with:
|
||||
//!
|
||||
//! cargo run --example query <service_type_without_domain>
|
||||
//!
|
||||
//! Example:
|
||||
//!
|
||||
//! cargo run --example query _my-service._udp
|
||||
//!
|
||||
//! Note: there is no '.' at the end as the program adds ".local."
|
||||
//! automatically.
|
||||
//!
|
||||
//! Keeps listening for new events.
|
||||
|
||||
use mdns_sd::{ServiceDaemon, ServiceEvent};
|
||||
|
||||
fn main() {
|
||||
env_logger::builder().format_timestamp_millis().init();
|
||||
|
||||
let mut service_type = match std::env::args().nth(1) {
|
||||
Some(arg) => arg,
|
||||
None => {
|
||||
print_usage();
|
||||
return;
|
||||
}
|
||||
};
|
||||
service_type.push_str(".local.");
|
||||
|
||||
// Showcase `verify` functionality for IPv4 addresses
|
||||
let should_verify = match std::env::args().nth(2) {
|
||||
Some(arg) if arg == "--verify" => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// Create a daemon
|
||||
let mdns = ServiceDaemon::new().expect("Failed to create daemon");
|
||||
|
||||
// Browse for the service type
|
||||
let receiver = mdns.browse(&service_type).expect("Failed to browse");
|
||||
|
||||
let now = std::time::Instant::now();
|
||||
while let Ok(event) = receiver.recv() {
|
||||
match event {
|
||||
ServiceEvent::ServiceResolved(info) => {
|
||||
println!(
|
||||
"At {:?}: Resolved a new service: {}\n host: {}\n port: {}",
|
||||
now.elapsed(),
|
||||
info.fullname,
|
||||
info.host,
|
||||
info.port,
|
||||
);
|
||||
let mut found_ipv4 = false;
|
||||
for addr in info.addresses.iter() {
|
||||
println!(" Address: {addr}");
|
||||
if addr.is_ipv4() {
|
||||
found_ipv4 = true;
|
||||
}
|
||||
}
|
||||
for prop in info.txt_properties.iter() {
|
||||
println!(" Property: {}", prop);
|
||||
}
|
||||
|
||||
if should_verify && found_ipv4 {
|
||||
println!("Will verify after 3 seconds...");
|
||||
std::thread::sleep(std::time::Duration::from_secs(3));
|
||||
|
||||
let instance_fullname = info.fullname;
|
||||
let timeout = std::time::Duration::from_secs(2);
|
||||
if let Err(e) = mdns.verify(instance_fullname, timeout) {
|
||||
println!("Verify failed: {}", e);
|
||||
} else {
|
||||
println!("Verify started");
|
||||
}
|
||||
}
|
||||
}
|
||||
ServiceEvent::ServiceRemoved(service_type, fullname) => {
|
||||
println!(
|
||||
"At {:?}: ** service removed **: {service_type}: {fullname}",
|
||||
now.elapsed(),
|
||||
);
|
||||
}
|
||||
other_event => {
|
||||
println!("At {:?}: {:?}", now.elapsed(), &other_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_usage() {
|
||||
println!("Usage: cargo run --example query <service_type_without_domain_postfix> [--verify]");
|
||||
println!("Example: ");
|
||||
println!("cargo run --example query _my-service._udp");
|
||||
println!();
|
||||
println!("Options:");
|
||||
println!("--verify: make the client attempt to verify IPv4 addresses of resolved services.");
|
||||
println!();
|
||||
println!("You can also do a meta-query per RFC 6763 to find which services are available:");
|
||||
println!("cargo run --example query _services._dns-sd._udp");
|
||||
}
|
||||
Vendored
+163
@@ -0,0 +1,163 @@
|
||||
//! Registers a mDNS service.
|
||||
//!
|
||||
//! Run with:
|
||||
//!
|
||||
//! cargo run --example register <service_type> <instance_name> <hostname> [options]
|
||||
//!
|
||||
//! Example:
|
||||
//!
|
||||
//! cargo run --example register _my-hello._udp instance1 host1
|
||||
//!
|
||||
//! Options:
|
||||
//! "--unregister": automatically unregister after 2 seconds.
|
||||
//! "--disable-ipv6": not to use IPv6 interfaces.
|
||||
//! "--logfile": write debug log to a file instead of stderr.
|
||||
//!
|
||||
//! For example: to see the debug log, set the RUST_LOG environment variable and write a logfile:
|
||||
//!
|
||||
//! RUST_LOG=mdns_sd=debug cargo run --example register _my-hello._udp instance1 host1 --logfile
|
||||
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
thread,
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use mdns_sd::{DaemonEvent, IfKind, ServiceDaemon, ServiceInfo};
|
||||
|
||||
fn main() {
|
||||
// Simple command line options.
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let mut should_unreg = false;
|
||||
let mut disable_ipv6 = false;
|
||||
let mut use_logfile = false;
|
||||
let mut include_apple_p2p = false;
|
||||
|
||||
for arg in args.iter() {
|
||||
if arg.as_str() == "--unregister" {
|
||||
should_unreg = true;
|
||||
} else if arg.as_str() == "--disable-ipv6" {
|
||||
disable_ipv6 = true;
|
||||
} else if arg.as_str() == "--logfile" {
|
||||
use_logfile = true;
|
||||
} else if arg.as_str() == "--include-apple-p2p" {
|
||||
include_apple_p2p = true;
|
||||
}
|
||||
}
|
||||
|
||||
// setup env_logger
|
||||
let mut builder = env_logger::Builder::from_default_env();
|
||||
if use_logfile {
|
||||
let now = SystemTime::now();
|
||||
let duration = now
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("Time went backwards: failed to get UNIX timestamp");
|
||||
let log_filename = format!("mdns-register-{}.log", duration.as_secs());
|
||||
let file = File::create(&log_filename).unwrap();
|
||||
builder.target(env_logger::Target::Pipe(Box::new(file)));
|
||||
println!("Logging to file: {}\n", log_filename);
|
||||
}
|
||||
|
||||
// more precise timestamp.
|
||||
builder.format_timestamp_millis().init();
|
||||
|
||||
// Create a new mDNS daemon.
|
||||
let mdns = ServiceDaemon::new().expect("Could not create service daemon");
|
||||
|
||||
if disable_ipv6 {
|
||||
mdns.disable_interface(IfKind::IPv6).unwrap();
|
||||
}
|
||||
|
||||
if include_apple_p2p {
|
||||
mdns.include_apple_p2p(true).unwrap();
|
||||
}
|
||||
|
||||
let service_type = match args.get(1) {
|
||||
Some(arg) => format!("{}.local.", arg),
|
||||
None => {
|
||||
print_usage();
|
||||
return;
|
||||
}
|
||||
};
|
||||
let instance_name = match args.get(2) {
|
||||
Some(arg) => arg,
|
||||
None => {
|
||||
print_usage();
|
||||
return;
|
||||
}
|
||||
};
|
||||
let hostname = match args.get(3) {
|
||||
Some(arg) => arg,
|
||||
None => {
|
||||
print_usage();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// With `enable_addr_auto()`, we can give empty addrs and let the lib find them.
|
||||
// If the caller knows specific addrs to use, then assign the addrs here.
|
||||
let my_addrs = "";
|
||||
let service_hostname = format!("{}.local.", hostname);
|
||||
let port = 3456;
|
||||
|
||||
// The key string in TXT properties is case insensitive. Only the first
|
||||
// (key, val) pair will take effect.
|
||||
let properties = [("PATH", "one"), ("Path", "two"), ("PaTh", "three")];
|
||||
|
||||
// Register a service.
|
||||
let service_info = ServiceInfo::new(
|
||||
&service_type,
|
||||
instance_name,
|
||||
&service_hostname,
|
||||
my_addrs,
|
||||
port,
|
||||
&properties[..],
|
||||
)
|
||||
.expect("valid service info")
|
||||
.enable_addr_auto();
|
||||
|
||||
// Optionally, we can monitor the daemon events.
|
||||
let monitor = mdns.monitor().expect("Failed to monitor the daemon");
|
||||
let service_fullname = service_info.get_fullname().to_string();
|
||||
mdns.register(service_info)
|
||||
.expect("Failed to register mDNS service");
|
||||
|
||||
println!("Registered service {}.{}", &instance_name, &service_type);
|
||||
|
||||
if should_unreg {
|
||||
let wait_in_secs = 2;
|
||||
println!("Sleeping {} seconds before unregister", wait_in_secs);
|
||||
thread::sleep(Duration::from_secs(wait_in_secs));
|
||||
|
||||
let receiver = mdns.unregister(&service_fullname).unwrap();
|
||||
while let Ok(event) = receiver.recv() {
|
||||
println!("unregister result: {:?}", &event);
|
||||
}
|
||||
} else {
|
||||
// Monitor the daemon events.
|
||||
while let Ok(event) = monitor.recv() {
|
||||
println!("Daemon event: {:?}", &event);
|
||||
if let DaemonEvent::Error(e) = event {
|
||||
println!("Failed: {}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_usage() {
|
||||
println!("Usage:");
|
||||
println!("cargo run --example register <service_type> <instance_name> <hostname> [options]");
|
||||
println!("\nOptions:\n");
|
||||
println!("--unregister: automatically unregister after 2 seconds");
|
||||
println!("--disable-ipv6: not to use IPv6 interfaces.");
|
||||
println!("--logfile: write debug log to a file instead of stderr. The logfile is named 'mdns-register-<timestamp>.log'.");
|
||||
println!("--include-apple-p2p: include Apple p2p interfaces (e.g., awdl, llw) for mDNS.");
|
||||
println!();
|
||||
println!("For example:");
|
||||
println!("cargo run --example register _my-hello._udp instance1 host1");
|
||||
println!("");
|
||||
println!("To see the debug log, set the RUST_LOG environment variable and write a logfile:");
|
||||
println!("RUST_LOG=mdns_sd=debug cargo run --example register _my-hello._udp instance1 host1 --logfile");
|
||||
}
|
||||
Reference in new issue
Block a user