Open WesleyAC opened 7 months ago
Thank you for creating the issue! I’ve been tip-toeing around this for quite a while. The reason is that I’m not sure whether there is a single best way to model an RRset. Every time I think about it I end up with more doubt and keep changing my mind, so maybe it is best if I write down my thoughts and we can discuss a way forward.
Conceptionally, an RRset is a list of records that have the same owner, record type, and class. The problem arises from domain names ignoring ASCII case and RFC 1034 saying that “when you receive a domain name […] you should preserve its case.” This suggests that the RRset is best be kept as a list of records – you could save a couple bytes by leaving out type and class but that’s probably not worth it. Especially since in our design the type is intermingled with the record data type.
But then there are use cases where preserving case is unnecessary and we should do away with all the duplicate owner names. This can become relevant in memory constrained system which is a use case I do want to cater for.
One way forward might be to keep an optional owner name per record. If it is left at None
, the RRset’s owner name is used. Depending on the octets type used, this can boil down to just keeping a single pointer. But that’s still a pointer per record that is never used in some scenarios and it all adds up.
We can, of course, have both, a case-preserving RRset and a compact RRset. If they both implement a well-defined set of traits, then they may be usable interchangeably in generic code without having to a have special trait.
Moreover, if you use the RRset in some sort of zone tree, you might not even want to have the owner be part of the data at all – you’ll have multiple RRsets for a given owner and if you don’t want to preserve case, it’s good enough to just keep it once. But perhaps the zone tree should just have its own types, anyway.
Finally, DNSSEC: An RRset should also have one or more RSIGs associated with it. Is that something we want to just have in the basic types or should we have special types for Signed RRsets?
dnspython handles this by having an Rdataset
class which is a set of record data which all share the same class, type, and TTL. The RRset
class then extends Rdataset
with an owner name. (The Rdataset
class can also be used in other contexts, e.g. when mapping from a tree, as you mentioned.)
In other words, dnspython does not have an owner name per record (and not even a TTL per record!), and it seems to work well. I'd think that if you go this way, you won't run into big problems, as otherwise they would have occurred already.
I'm not sure what dnspython does when there's sibling RRset whose key only differs in owner name capitalization -- perhaps worth looking into.
In other words, dnspython does not have an owner name per record (and not even a TTL per record!), and it seems to work well.
Knot * servers also represent RRsets that way internally. We don't preserve case, and even name compression is allowed to lead to changing case.
RFC 1034 saying that “when you receive a domain name […] you should preserve its case.
It has since become quite clear that 1034 was not set-focused enough. I definitely would not worry about case variations within an RRset, thus you can use a single name in a single case to hold to an RRset.
It is reasonable to preserve case at the RRset level, but not per-record. Anyone who expects case-variation within an RRset has unreasonable expectations. RRsets should be coherent (same name, type, class and TTL).
SInce NSEC RRs and associated RRSIGs for the same name and class can appear on both sides of a zone cut, there's some room for debate on whether when seen together these are one RRset or two! I rather expect this exotic example would not be handled gracefully by most resolvers...
Thanks, everyone! This paints a pretty clear picture. I like the idea to have two types, an inner one with a sequence of TTL/record data pairs, plus class and overall TTL, and an outer one that adds the owner name to it. Might even have a third one that then also adds the RRSIGs.
(As an aside: We should probably start a document that collects all of these gotchas and tribal knowledge.)
Currently, the only type for a RRSet is in the
sign
package, which is unstable. It seems strange to me that this is the case, since RRSets are a fairly core concept.Would it be possible to provide a RRSet type for more general use? Perhaps the
sign::records::RRSet
is suitable for general use, in which case it could simply be moved, I haven't tried to evaluate whether that's the case or not.