mirror of
https://github.com/hickory-dns/hickory-dns.git
synced 2026-04-25 03:05:51 +03:00
[GH-ISSUE #1893] Trust-DNS mDNS and DNS-SD Support #810
Labels
No labels
blocked
breaking-change
bug
bug:critical
bug:tests
cleanup
compliance
compliance
compliance
crate:all
crate:client
crate:native-tls
crate:proto
crate:recursor
crate:resolver
crate:resolver
crate:rustls
crate:server
crate:util
dependencies
docs
duplicate
easy
easy
enhance
enhance
enhance
feature:dns-over-https
feature:dns-over-quic
feature:dns-over-tls
feature:dnsssec
feature:global_lb
feature:mdns
feature:tsig
features:edns
has workaround
ops
perf
platform:WASM
platform:android
platform:fuchsia
platform:linux
platform:macos
platform:windows
pull-request
question
test
tools
tools
trust
unclear
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/hickory-dns#810
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @darconeous on GitHub (Feb 7, 2023).
Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/1893
Trust-DNS currently has only experimental support for mDNS and DNS-SD that has not been maintained since its introduction. I intend to do most of the implementation work, but wanted to engage with the community about the design before I get too far into development.
This description is a living document. As the discussion continues, I will update the plan here in the description so that it is easy to get up to speed.
High Level Goals
Usage
Support for DNS-SD is fairly straightforward for both unicast DNS and multicast DNS. In theory you could have multiple instances of DNS-SD running per application and it should work out just fine, as long as those instances have a consistent interface for resolving and publishing records.
On the other hand, mDNS is tricky. There can really only be a single "mDNS responder" running on a machine. In the absence of some sort of IPC allowing applications to communicate with the mDNS responder running on the system. The "legacy unicast-response mDNS resolver mode" allows the mDNS queries to be made without coordination with a centralized mDNS responder, but it has several limitations, such as the inability to receive updates dynamically without polling.
So far, Trust DNS has avoided getting into the IPC game. At this point I recommend we punt on the IPC question, and instead base our design on a "Pseudo IPC" that is shaped similarly to what we would like to see on an ideal system.
Design Proposal
I'm proposing a two-layer design: a record querying/publishing layer and a service discovery layer.
Design: Record Querying/Publishing Layer
Conceptually, there are unicast domains and there are multicast domains (and here there are two different ways of querying them). We can summarize these as DNS, mDNS, and Legacy Unicast mDNS. For each flavor, we need a way to do the following:
Since we want to support DNS, mDNS, and Unicast-Response mDNS, the mechanisms necessary on-the-wire are nicely summarized in the following table:
Key:
Ideally we could expose a trait or some other sort of interface that could allow us to perform these three operations independently of knowing the underlying transport.
This layer would also need to feature a system domain management system for doing things like adding new unicast browsing/registration domains, determining which domains should be used as 'search' domains, default domain browse/registration behavior, etc. Here are the specific list of properties/flags associated with a "system domain":
search— Is this domain used as a suffix when resolving single-label hostnames?uses_punycodednssd_browsable— Is this domain browsable?dnssd_browsable_default— Should this domain be browsed by default?dnssd_registrable— Can services be registered to this domain?dnssd_registrable_default— Should services be registered to this domain by default?TBD: Which Crate?
Design: Service Discovery Layer
The service discovery layer would use the API exposed by the record querying/publishing layer to implement the full DNS-SD spec independently of the underlying mechanism. This would allow a service to be registered on all "default" registration domains and allow for browsing all "default" domains (which might be a mixture of unicast and multicast domains).
Additionally to just being able to publish and resolve services for the current host, we also need to be able to support proxy hosts and local aliases.
A proxy host are service+host records that we manage on behalf of another host. This is especially important when implementing a Discovery Proxy.
A Local Alias is similar to a Proxy Host, except that the hostname always resolves to the local machine. This is important for supporting Matter, due to an unfortunate SHALL requirement on the format of the hostname in that specification.
TBD: Which Crate?
The actual act of discovering services should be broken down into two steps: Browsing and Resolving.
Browsing
Browsing is how you discover services. To browse for a service, you need the following information:
service_type(Ex:_ssh._tcp)interface_iddomain(Ex:local)subtypes(Ex:_printer)exclude_local_services— Flag for excluding locally-hosted services, including those hosted by a local alias.exclude_proxied_services— Flag for excluding services proxied by this device.exclude_ipv4_mdns— Prevents the associated query from sending IPv4 mDNS packets.The browse operation will collect the following information for each service:
Browsing is an inherently asynchronous operation. Rarely does someone want to just return the first result.
Resolving
Once a service has been discovered and the program wants to look up more information (perhaps to connect to it), you must then resolve the service. Resolving a service is analogous to resolving a hostname.
You supply the above three parameters (plus the service type) to the service resolver and, if successful, will give you the following:
MyDevice._ssh._tcp.local.)my-device.local.)Note that resolution always resolves to a single service, but that single service might change dynamically, for example if the TXT records change. Thus, resolution is asynchronous and can return many updates over time. However in most cases the user is only going to care about the first result.
Registration
The registration interface should ideally be something that is shared for local services, proxy hosts, and local aliases.
Additional information on the structure of the registration system is coming shortly.
Open Questions
Internationalized Name Handling
mDNS and DNS use different mechanisms for hostname and domain internationalization. DNS has standardized around punycode for hostnames, whereas mDNS hostname must use only UTF-8. To make matters more complicated, RFC 6763-compliant subtype labels aren't even required to be valid UTF-8:
This is tricky because Trust-DNS seems to integrate IDNA logic at a deep level. All DNS labels internally are punycode ASCII, which is not going to be compatible with the mDNS specification nor DNS-SD specification. Some careful thought will need to be put into how this is handled.
Note that IDNA applies only to host names and domain names. It does not apply to service names, service types, and subtypes. For example, service name labels are always UTF-8 across both DNS and mDNS. It's the hostnames that are the tricky part.
Addendum: Multicast Domains
One might think that there is only one multicast domain:
.local., however, there are actually several outlined in RFC6761:.local..10.in-addr.arpa..[16-31].172.in-addr.arpa..168.192.in-addr.arpa..168.192.in-addr.arpa..0.8.e.f.ip6.arpa.These all need to be supported.
Addendum: References
Immediate Action Items
Below are the list of action items. This list will be expanded as the design takes shape.
Labeltype in the trustdns-proto crate to account for how non-UTF8 non-Punycoded labels will work in practice.@djc commented on GitHub (Feb 8, 2023):
Great news that you will be investing in mDNS/DNS-SD, and welcome to the trust-dns community!
Just to set expectations, @bluejekyll has recently not been able to spend a lot of time on this crate (but I don't know what his outlook is for the near future) and I try to review proactively but I'm mostly unfamiliar with DNS-SD/mDNS. Therefore, review capacity of the changes you propose will be limited. In order to make the most of it, please (a) chunk up the work in small PRs (your action items look pretty good), (b) make sure to keep commits small even within a PR -- separate mechanical and logical changes, separate refactoring vs adding features/fixing bugs and (c) please provide good context for your changes so that even I can follow along.
As for the which crate question, I'm not sure I have good answers here, and I guess it might depend on the volume of code you'll be adding to support mDNS and DNS-SD. Personally I've been thinking that we should consider splitting out networking code (that is, anything involving a UDP socket or TCP stream, including all the async machinery) into a new trust-dns-net crate, which could potentially also accomodate some of this work. This would leave the proto crate to be only about parsing and serializing DNS records (and streams of records, where applicable), which is usually good design.
I'd be interested to hear more about the context in which you're doing this work/what your use case is! (Can I take a guess that Nest devices might be interested in adopting Fuchsia and would need this stuff?)
@darconeous commented on GitHub (Feb 8, 2023):
Will do. My first efforts will be to clean up and solidify the mDNS side of things, making sure resolution works, publishing records, etc. There is a lot of work that needs to be done there.
Perhaps, but I'm not sure what we would get over refactoring that code into a crate rather than into a module. Breaking things up into modules allows us to only include the parts that we need, and the
protocrate seems like it would be needed everywhere this networking crate would be needed. Refactoring the networking code into a submodule inprotoseems reasonable, if it isn't already factored like that (can't remember off the top of my head).As you likely already know, Fuchsia uses Trust-DNS as its system resolver. We were looking into rewriting our mDNS/DNS-SD implementation to be a bit easier to maintain over the long run, and internal discussions led us to consider moving all DNS-related stuff into our DNS resolver component, which is basically just a wrapper around Trust-DNS. The updated component will support a new DNS-SD specific API.
Github Discussions
Any chance we could turn on the "Discussions" feature for this project and migrate this issue to a discussion? Discussions allow for threads and have other nice features.
Caching
One thing I now remember I wanted some insight on was how caching works. Having a look at
dns_lru.rs...If I understand the current DNS cache behavior, it is based on queries. Individual queries are cached along with the responses. But individual records can have their own TTLs. For example, if we do an
SRVlookup for_http._tcp.example.com, we might get back someAandAAAArecords forwww.example.comin the additional records section. But if we later (before the TTL expired) query theAAAArecord forwww.example.com, it looks like the query wouldn't match and we would have to hit the wire again, even though we theoretically have the record in the cache.I ask this because in mDNS we will have responses coming in without explicit queries. Ideally we would cache these so that we can avoid sending out queries when we don't need to. But I'm not sure how to fit that into the way the cache system is currently set up, other than making up a fake query when we get an update on the wire.
I get that some sort of query-cache is necessary in order to have negative responses be cached.
But maybe I'm misunderstanding something?
@djc commented on GitHub (Feb 14, 2023):
Enabling Discussions makes sense to me. @bluejekyll what do you think? @darconeous you might also be interested in joining our Discord server.
Probably not, this is likely just an oversight/missed optimization.
@bluejekyll commented on GitHub (Feb 14, 2023):
On the discussions enablement, unless Discord is an issue, I'd prefer to keep the conversation in Discord, if people have a strong preference, then we can do that.
As to the caching of the additional records, yes, we should improve that. I can't remember the exact reasons why I didn't do that originally. I think I was concerned about validating authenticity...