For a lot longer than I thought it would take, as all things in software turn out to be , I've been working on adding multicast DNS to the TRust-DNS libraries. I wrote a post about performing multicast requests in Rust related to this work: Multicasting in Rust. Hopefully others will find that information useful for similar tools.
As to DNS, this work is all need to support multicast DNS. Beyond the additional protocol, I've added the beginnings of a DNS-SD, DNS Service Discovery, implementation. This currently requires the experimental mdns
feature to be enabled for both mDNS and DNS-SD to be compiled into the library. DNS-SD does not technically require mDNS and can work against traditional DNS nodes, but mDNS does require DNS-SD for some of it's more interesting features (at some future point we can untether DNS-SD from the mdns
feature). Here's the trait for the DNS-SD extension:
/// An extension for the Resolver to perform DNS Service Discovery
pub trait DnsSdFuture {
/// List all services available
///
/// https://tools.ietf.org/html/rfc6763#section-4.1
///
/// For registered service types, see: https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
fn list_services<N: IntoName>(&self, name: N) -> ListServicesFuture;
/// Retrieve service information
///
/// https://tools.ietf.org/html/rfc6763#section-6
fn service_info<N: IntoName>(&self, name: N) -> ServiceInfoFuture;
}
I've put a single "test" in the library, it can be run with this and if you have any mDNS devices on your network you may get results:
$> cd resolver
$> cargo test --features mdns test_list_services -- --nocapture --ignored
...
running 1 test
service: not\040real\040name._http._tcp.local.
service: SRV {
priority: 0,
weight: 0,
port: 80,
target: Name {
is_fqdn: true,
labels: [
DVR-89B6,
local
]
}
}
info: {
"platform": Some(
"tcd/Series4"
),
"path": Some(
"/index.html"
),
"swversion": Some(
"1.2.3.4"
),
"TSN": Some(
"DEADBEEF"
)
}
ip: 198.51.100.1
test dns_sd::tests::test_list_services ... ok
...
The result above is from the TiVo on my network (yes, I still have a TiVo, don't ask). I've changed some of the private information to bogus details for this post, so some of those things are fake. Breaking this down here's the test:
fn test_list_services() {
let mut io_loop = Core::new().unwrap();
We create a Resolver like normal:
let resolver = ResolverFuture::new(
ResolverConfig::default(),
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv6thenIpv4,
..ResolverOpts::default()
},
&io_loop.handle(),
);
Then we run the new list_services
function to find all services, this actually performs a PTR
lookup. It uses the default timeout to wait for responses, and then returns any that were found:
let response = io_loop
.run(resolver.list_services("_http._tcp.local."))
.expect("failed to run lookup");
for name in response.iter() {
println!("service: {}", name);
Now lookup the service's connection info with the new lookup_srv
function (described below):
let srvs = io_loop
.run(resolver.lookup_srv(name))
.expect("failed to lookup name");
for srv in srvs.iter() {
println!("service: {:#?}", srv);
Now lookup the service's information with the new service_info
function:
let info = io_loop
.run(resolver.service_info(name))
.expect("info failed");
The result of the service lookup returns an type that can return the output as a HashMap
:
let info = info.to_map();
println!("info: {:#?}", info);
}
for ip in srvs.ip_iter() {
println!("ip: {}", ip);
}
}
}
And that's it, mDNS with DNS-SD. Now this isn't a full implementation of the spec. To get here required a lot of work, PR#363. There were many underlying refactorings required in the library. For example, the protocols all used to immediately return on the first response. With the new DnsRequestOptions
object passed into the trust-dns-proto
requests, we optionally specify to allow more than one DnsResponse
. DnsRequest
and DnsResponse
were added to the trust-dns-proto
library, which helps clarify some areas of code where just Message
was being used for inbound and outbound Message
s. Those changes are in addition to the multicast protocol addition. In theory this will also allow for single multi-query DnsRequest
s to be sent and received, but that's not implemented yet (this will allow the SRV
and TXT
lookups to be combined into a single request).
In addition to that, to make the the SRV
response processing better for the lookup_srv
, the DnsResponse
will be searched for IPs matching the target
of SRV
record. As of now this will not perform a recursive query, as CNAME
does, some additional thought needs to be put into when to perform an A
or AAAA
lookup. The response Message
should have this associated, so in most cases this does not matter, but libraries using the Resolver should be aware of this case. This is a general improvement on all SRV
lookups in the library, the new ip_iter
function on SrvLookup
will provide access to these IPs. Also, I've deprecated the lookup_service
and srv_lookup
functions on ResolverFuture
as I realized those were naively and overly simplistic in favor of the new lookup_srv
implementation.
Why am I writing this post? I'd love some feedback on these implementations. I plan to land this as is in the master
branch (after I review the code), and subsequently release in 0.9
(still feature flagged off) when that's ready. Also, I think I've temporarily reached my limit with with mDNS and DNS-SD, so if others are interested in picking up the torch from here, I'd happily try to help mentor any additional changes.
For now, I think I'm going to switch gears and finalize TLS support in the Resolver since the creation of 1.1.1.1
.
Thanks!