Rework serials in domain::new#691
Conversation
13d6f66 to
585424b
Compare
| Some(Ordering::Equal) | ||
| } else if lhs.abs_diff(rhs) == 1 << 31 { | ||
| None | ||
| } else if (lhs < rhs) ^ (lhs.abs_diff(rhs) > (1 << 31)) { |
There was a problem hiding this comment.
I think this could do with either some explanation or be written out more explicitly. Using the bitwise OR with booleans is a bit surprising, though I think it's correct! Maybe even just a small note about that you're aware that it's bitwise or and not exponentiation already helps 😄
There was a problem hiding this comment.
I added some usage examples which should illustrate the underlying logic.
f146341 to
8393971
Compare
domain::new
bal-e
left a comment
There was a problem hiding this comment.
This is really cool! Well done @withjannisNLnetLabs! I have a bunch of comments (of course :p), but overall, I really like the functionality and the way you've laid out the code. The commits were nicely organized and easy to review.
Minor nits:
-
You often refer to
the [`SoaSerial`]orthe [`SeqNumberU32`]; maybe you intend these to be read as "the SOA serial type" and "theSeqNumberU32type", but since the word "type" does not appear in there, IMO it's better to omit "the" and just say e.g.[`SoaSerial`] commonly follows one of the following strategies .... -
There are a few typos here and there, e.g.
Comparision,huaman. I'm not sure if there's a good automated way to detect these, I suspect spellcheck will not deal well with Rust or Markdown.
Top-level feedback:
They Timestamp and SoaSerial type have no logical connection and shall not be convertible.
I'm not sure that's true; one of the common conventions for the SOA serial is as the number of seconds since the Unix epoch modulo 2^32, exactly like Timestamp. I can see some value in allowing conversions between the two, when users choose to use that convention. To avoid users relying on this convention in the wrong cases, maybe it should be implemented as inherent methods with clear names rather than From/Into.
- define SeqNumberU32 as public or private?
- SoaSerial / Timestamp convertible into SeqSequenceU32 and vise versa implemented?
I think SeqNumberU32 should be public; it is an implementation of an RFC and so I'd argue it's more than an implementation detail of SoaSerial / Timestamp. Following that, I think it makes sense to have conversions to and from the wrapped types. These can be simple From / Into impls (while inherent methods that are const would be nice, I don't think there's too much need for them; we can add them if we have a need to).
| /// - Date including counter, on change the number gets set to the current | ||
| /// date in the format (`YYYYMMDD00`) if the number is smaller than the | ||
| /// previous version, the old serial gets increased by one. |
There was a problem hiding this comment.
if the number is smaller than the previous version, the old serial gets increased by one
I don't think this edge case needs to be mentioned in these high-level docs.
| /// Construct [`SoaSerial`] from [`u32`]. | ||
| /// | ||
| /// Equivalent to [`SoaSerial::get()`]. | ||
| impl From<SoaSerial> for u32 { | ||
| fn from(_value: SoaSerial) -> u32 { | ||
| todo!() | ||
| } | ||
| } | ||
|
|
||
| /// The raw [`u32`] underlying this [`SoaSerial`]. | ||
| /// | ||
| /// Equivalent to [`SoaSerial::new()`]. | ||
| impl From<u32> for SoaSerial { | ||
| fn from(_value: u32) -> Self { | ||
| todo!() | ||
| } | ||
| } |
There was a problem hiding this comment.
The top-level descriptions for these two impls appear to be swapped; the first one should say "the raw u32 underlying this SoaSerial." Note that the references to get() and new() do not need to be swapped.
| /// The [`Timestamp`] stores the seconds since Unix Epoch modulo 2^32. It is | ||
| /// used in the [`Rrsig`] to keep track of `inception` and `expiration` time | ||
| /// and in the edns [`Cookie`] `timestamp`. |
There was a problem hiding this comment.
- "edns" => "EDNS"?
- I see you're referring to the
timestampfield ofCookie; maybe refer to the method directly, i.e.[`Cookie::timestamp()`]? - The same could be done for the RRSIG fields.
There was a problem hiding this comment.
Linking to the timestamp method is possible, but I think it is not possible to link to certain fields.
| /// [RFC1982]: https://datatracker.ietf.org/doc/html/rfc1982 | ||
| /// [RFC4034]: https://datatracker.ietf.org/doc/html/rfc4034#section-3.1.5 | ||
| /// [RFC9018]: https://datatracker.ietf.org/doc/html/rfc9018#name-the-timestamp-sub-field |
There was a problem hiding this comment.
I'm worried docs added here in the future might reference [RFC4034] or [RFC9018] without realizing they point to specific sections of those documents. Maybe explicitly state them as [RFC 4034, section 3.1.5] and [RFC 9018, "The Timestamp Sub-Field"]?
| } | ||
|
|
||
| /// Underlying seconds since Unix Epoch modulo 2^32. | ||
| // This type has a specific unit, therefore the `get()` function was |
There was a problem hiding this comment.
Maybe add a blank // line before this comment so it is more clearly distinguished from the public docs?
There was a problem hiding this comment.
I swapped the doc comment and the comment to have the doc comment closer to the function.
| /// Basic operations performed with a [`SoaSerial`]. | ||
| /// ``` | ||
| /// # use domain::new::base::SoaSerial; | ||
| /// let soa_serial: SoaSerial = SoaSerial::new(u32::MAX).increment(); |
There was a problem hiding this comment.
Maybe separate the .new() and the .increment() into separate statements so it's easier for the user to see what they should focus on? With the line as it is right now, the user might miss new() and only see increment().
Also, I don't think the : SoaSerial type annotation is necessary. It would be if new() returned something weirder, but that is not the case here.
| } | ||
|
|
||
| /// Measure system time since Unix Epoch modulo 2^32. | ||
| /// ``` |
There was a problem hiding this comment.
Maybe add a blank line before this doc test?
| /// let now = Timestamp::now(); | ||
| /// assert_eq!( | ||
| /// now.as_seconds(), | ||
| /// (UNIX_EPOCH.elapsed().unwrap().as_secs() % 0x1_0000_0000) as u32 |
There was a problem hiding this comment.
Since as u32 automatically performs the modulo operation, do you want to omit the % 0x1_0000_0000?
Also, this doc test is a little bit racy... is there some way to make it deterministically succeed? I really want to avoid spurious failures.
| /// or | ||
| /// this < other and (this - other) > 2^31 | ||
| /// // 10 < u32::MAX and u32::MAX - 1 > 2^31 | ||
| ///``` |
There was a problem hiding this comment.
There should be a blank line before the doc test, and a space between /// and ```.
| /// # // Get all the edge cases | ||
| /// # assert_eq!( | ||
| /// # SeqNumberU32::new(0).partial_cmp(&SeqNumberU32::new((1 << 31) - 1)), | ||
| /// # Some(Ordering::Less) | ||
| /// # ); | ||
| /// | ||
| /// # assert_eq!( | ||
| /// # SeqNumberU32::new(0).partial_cmp(&SeqNumberU32::new((1 << 31) + 1)), | ||
| /// # Some(Ordering::Greater) | ||
| /// # ); | ||
| /// | ||
| /// # assert_eq!( | ||
| /// # SeqNumberU32::new(0).partial_cmp(&SeqNumberU32::new((1 << 31))), | ||
| /// # None | ||
| /// # ); |
There was a problem hiding this comment.
I think these belong in a dedicated test function, not in the doc test.
5fa3faf to
0c8c2e8
Compare
| UnsizedCopy, | ||
| )] | ||
| #[repr(transparent)] | ||
| pub struct Timestamp(SeqNumberU32); |
There was a problem hiding this comment.
add from timestamp
0c8c2e8 to
74d850f
Compare
e13e2ec to
95a009f
Compare
95a009f to
4d77fe0
Compare
306c96b to
21dc900
Compare
49a1937 to
89c43a8
Compare
89c43a8 to
67bb0d3
Compare
TLDR; Improve existing
Serialtype with more declarative types;Timestamp,SoaSerialandSeqNumberU32, which represent the underlying data more appropriately.In DNS the SOA Serial number and the RRSIG inception/expiration have a 32-bit number which represents them. The maximum number is around 4 billion. The number may overflow and therefore special consideration has to be taken to increment and compare such types. The arithmetical definition for comparison, addition, etc. is in RFC1982.
TimestampRepresents the time since epoch in seconds. If the number is larger thanu32::MAXit will wrap around. The types 'unit' is seconds because it's in the nature of the represented data. This type should not be used for normale time and date operation because of its special properties for comparison and modification. ForRRSIGthe value may be represented in theYYYYMMDDHHmmSSformat see RFC4034.Careful consideration has to be taken when defining the
Displayimplementation. Another usage of the type is theCookieused for DNS messages. This type may be generated from an accurateTimestamp(jiff::Timestamp), for example after calculating the inception and expiration time of aRRSIGrecord.Displayformat should be used for theTimestamp? -> use the seconds representation, because this format is valid in all known cases.SoaSerialRepresents the version of a zone by being present in theSOArecords serial field. The version number is mostly used in three different formats; a counter without special meaning, seconds since epoch or the date of the day including a two digit counter in the formatYYYYMMDDxx. Additionally to the SOA record the type is used in theZoneMDrecord. TheDisplayformat is simple because in any case the number representation matches the string representation.SeqNumberU32Represents the type described in RFC1982 with a 32-bit number. This type is used for the above types as the underlying, wrapped data structure. This type should not be used directly but may well be public for usage by the end user.SeqNumberU32as public or private?The types must be creatable from the primitive type
u32.TimestampandSoaSerialtype have no logical connection and shall not be convertible. The use case for the conversion to the wrapped type is questionable.SoaSerial/Timestampconvertible intoSeqSequenceU32and vise versa implemented?Desired changes:
SeqNumberU32,SoaSerial,Timestampsignatures with documentation.SeqNumberU32,SoaSerial,Timestampfunctionality.SeqNumberU32,SoaSerial,Timestampfunctionality.SoaSerial,Timestamptypes.Important
This PR requires Cascade to change, because the types in
RrisgandSoachange.