trust_dns_proto/rr/rdata/
soa.rs

1/*
2 * Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! start of authority record defining ownership and defaults for the zone
18
19use std::fmt;
20
21#[cfg(feature = "serde-config")]
22use serde::{Deserialize, Serialize};
23
24use crate::error::*;
25use crate::rr::domain::Name;
26use crate::serialize::binary::*;
27
28/// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
29///
30/// ```text
31/// 3.3.13. SOA RDATA format
32///
33///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
34///     /                     MNAME                     /
35///     /                                               /
36///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
37///     /                     RNAME                     /
38///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
39///     |                    SERIAL                     |
40///     |                                               |
41///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
42///     |                    REFRESH                    |
43///     |                                               |
44///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
45///     |                     RETRY                     |
46///     |                                               |
47///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
48///     |                    EXPIRE                     |
49///     |                                               |
50///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
51///     |                    MINIMUM                    |
52///     |                                               |
53///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
54///
55/// where:
56///
57/// SOA records cause no additional section processing.
58///
59/// All times are in units of seconds.
60///
61/// Most of these fields are pertinent only for name server maintenance
62/// operations.  However, MINIMUM is used in all query operations that
63/// retrieve RRs from a zone.  Whenever a RR is sent in a response to a
64/// query, the TTL field is set to the maximum of the TTL field from the RR
65/// and the MINIMUM field in the appropriate SOA.  Thus MINIMUM is a lower
66/// bound on the TTL field for all RRs in a zone.  Note that this use of
67/// MINIMUM should occur when the RRs are copied into the response and not
68/// when the zone is loaded from a Zone File or via a zone transfer.  The
69/// reason for this provison is to allow future dynamic update facilities to
70/// change the SOA RR with known semantics.
71/// ```
72#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
73#[derive(Debug, PartialEq, Eq, Hash, Clone)]
74pub struct SOA {
75    mname: Name,
76    rname: Name,
77    serial: u32,
78    refresh: i32,
79    retry: i32,
80    expire: i32,
81    minimum: u32,
82}
83
84impl SOA {
85    /// Creates a new SOA record data.
86    ///
87    /// # Arguments
88    ///
89    /// * `mname` - the name of the primary or authority for this zone.
90    /// * `rname` - the name of the responsible party for this zone, e.g. an email address.
91    /// * `serial` - the serial number of the zone, used for caching purposes.
92    /// * `refresh` - the amount of time to wait before a zone is resynched.
93    /// * `retry` - the minimum period to wait if there is a failure during refresh.
94    /// * `expire` - the time until this primary is no longer authoritative for the zone.
95    /// * `minimum` - no zone records should have time-to-live values less than this minimum.
96    ///
97    /// # Return value
98    ///
99    /// The newly created SOA record data.
100    pub fn new(
101        mname: Name,
102        rname: Name,
103        serial: u32,
104        refresh: i32,
105        retry: i32,
106        expire: i32,
107        minimum: u32,
108    ) -> Self {
109        Self {
110            mname,
111            rname,
112            serial,
113            refresh,
114            retry,
115            expire,
116            minimum,
117        }
118    }
119
120    /// Increments the serial number by one
121    pub fn increment_serial(&mut self) {
122        self.serial += 1; // TODO: what to do on overflow?
123    }
124
125    /// ```text
126    /// MNAME           The <domain-name> of the name server that was the
127    ///                 original or primary source of data for this zone.
128    /// ```
129    ///
130    /// # Return value
131    ///
132    /// The `domain-name` of the name server that was the original or primary source of data for
133    /// this zone, i.e. the Primary Name Server.
134    pub fn mname(&self) -> &Name {
135        &self.mname
136    }
137
138    /// ```text
139    /// RNAME           A <domain-name> which specifies the mailbox of the
140    ///                 person responsible for this zone.
141    /// ```
142    ///
143    /// # Return value
144    ///
145    /// A `domain-name` which specifies the mailbox of the person responsible for this zone, i.e.
146    /// the responsible name.
147    pub fn rname(&self) -> &Name {
148        &self.rname
149    }
150
151    /// ```text
152    /// SERIAL          The unsigned 32 bit version number of the original copy
153    ///                 of the zone.  Zone transfers preserve this value.  This
154    ///                 value wraps and should be compared using sequence space
155    ///                 arithmetic.
156    /// ```
157    ///
158    /// # Return value
159    ///
160    /// The unsigned 32 bit version number of the original copy of the zone. Zone transfers
161    /// preserve this value. This value wraps and should be compared using sequence space arithmetic.
162    pub fn serial(&self) -> u32 {
163        self.serial
164    }
165
166    /// ```text
167    /// REFRESH         A 32 bit time interval before the zone should be
168    ///                 refreshed.
169    /// ```
170    ///
171    /// # Return value
172    ///
173    /// A 32 bit time interval before the zone should be refreshed, in seconds.
174    pub fn refresh(&self) -> i32 {
175        self.refresh
176    }
177
178    /// ```text
179    /// RETRY           A 32 bit time interval that should elapse before a
180    ///                 failed refresh should be retried.
181    /// ```
182    ///
183    /// # Return value
184    ///
185    /// A 32 bit time interval that should elapse before a failed refresh should be retried,
186    /// in seconds.
187    pub fn retry(&self) -> i32 {
188        self.retry
189    }
190
191    /// ```text
192    /// EXPIRE          A 32 bit time value that specifies the upper limit on
193    ///                 the time interval that can elapse before the zone is no
194    ///                 longer authoritative.
195    /// ```
196    ///
197    /// # Return value
198    ///
199    /// A 32 bit time value that specifies the upper limit on the time interval that can elapse
200    /// before the zone is no longer authoritative, in seconds
201    pub fn expire(&self) -> i32 {
202        self.expire
203    }
204
205    /// ```text
206    /// MINIMUM         The unsigned 32 bit minimum TTL field that should be
207    ///                 exported with any RR from this zone.
208    /// ```
209    ///
210    /// # Return value
211    ///
212    /// The unsigned 32 bit minimum TTL field that should be exported with any RR from this zone.
213    pub fn minimum(&self) -> u32 {
214        self.minimum
215    }
216}
217
218/// Read the RData from the given Decoder
219pub fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<SOA> {
220    Ok(SOA {
221        mname: Name::read(decoder)?,
222        rname: Name::read(decoder)?,
223        serial: decoder.read_u32()?.unverified(/*any u32 is valid*/),
224        refresh: decoder.read_i32()?.unverified(/*any i32 is valid*/),
225        retry: decoder.read_i32()?.unverified(/*any i32 is valid*/),
226        expire: decoder.read_i32()?.unverified(/*any i32 is valid*/),
227        minimum: decoder.read_u32()?.unverified(/*any u32 is valid*/),
228    })
229}
230
231/// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-6), DNSSEC Resource Records, March 2005
232///
233/// This is accurate for all currently known name records.
234///
235/// ```text
236/// 6.2.  Canonical RR Form
237///
238///    For the purposes of DNS security, the canonical form of an RR is the
239///    wire format of the RR where:
240///
241///    ...
242///
243///    3.  if the type of the RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
244///        HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
245///        SRV, DNAME, A6, RRSIG, or (rfc6840 removes NSEC), all uppercase
246///        US-ASCII letters in the DNS names contained within the RDATA are replaced
247///        by the corresponding lowercase US-ASCII letters;
248/// ```
249pub fn emit(encoder: &mut BinEncoder<'_>, soa: &SOA) -> ProtoResult<()> {
250    let is_canonical_names = encoder.is_canonical_names();
251
252    soa.mname.emit_with_lowercase(encoder, is_canonical_names)?;
253    soa.rname.emit_with_lowercase(encoder, is_canonical_names)?;
254    encoder.emit_u32(soa.serial)?;
255    encoder.emit_i32(soa.refresh)?;
256    encoder.emit_i32(soa.retry)?;
257    encoder.emit_i32(soa.expire)?;
258    encoder.emit_u32(soa.minimum)?;
259    Ok(())
260}
261
262/// [RFC 1033](https://tools.ietf.org/html/rfc1033), DOMAIN OPERATIONS GUIDE, November 1987
263///
264/// ```text
265/// SOA  (Start Of Authority)
266///
267/// <name>  [<ttl>]  [<class>]  SOA  <origin>  <person>  (
268///    <serial>
269///    <refresh>
270///    <retry>
271///    <expire>
272///    <minimum> )
273///
274/// The Start Of Authority record designates the start of a zone.  The
275/// zone ends at the next SOA record.
276///
277/// <name> is the name of the zone.
278///
279/// <origin> is the name of the host on which the master zone file
280/// resides.
281///
282/// <person> is a mailbox for the person responsible for the zone.  It is
283/// formatted like a mailing address but the at-sign that normally
284/// separates the user from the host name is replaced with a dot.
285///
286/// <serial> is the version number of the zone file.  It should be
287/// incremented anytime a change is made to data in the zone.
288///
289/// <refresh> is how long, in seconds, a secondary name server is to
290/// check with the primary name server to see if an update is needed.  A
291/// good value here would be one hour (3600).
292///
293/// <retry> is how long, in seconds, a secondary name server is to retry
294/// after a failure to check for a refresh.  A good value here would be
295/// 10 minutes (600).
296///
297/// <expire> is the upper limit, in seconds, that a secondary name server
298/// is to use the data before it expires for lack of getting a refresh.
299/// You want this to be rather large, and a nice value is 3600000, about
300/// 42 days.
301///
302/// <minimum> is the minimum number of seconds to be used for TTL values
303/// in RRs.  A minimum of at least a day is a good value here (86400).
304///
305/// There should only be one SOA record per zone.  A sample SOA record
306/// would look something like:
307///
308/// @   IN   SOA   SRI-NIC.ARPA.   HOSTMASTER.SRI-NIC.ARPA. (
309///     45         ;serial
310///     3600       ;refresh
311///     600        ;retry
312///     3600000    ;expire
313///     86400 )    ;minimum
314/// ```
315impl fmt::Display for SOA {
316    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
317        write!(
318            f,
319            "{mname} {rname} {serial} {refresh} {retry} {expire} {min}",
320            mname = self.mname,
321            rname = self.rname,
322            serial = self.serial,
323            refresh = self.refresh,
324            retry = self.retry,
325            expire = self.expire,
326            min = self.minimum
327        )
328    }
329}
330
331#[cfg(test)]
332mod tests {
333    #![allow(clippy::dbg_macro, clippy::print_stdout)]
334
335    use super::*;
336
337    #[test]
338    fn test() {
339        use std::str::FromStr;
340
341        let rdata = SOA::new(
342            Name::from_str("m.example.com").unwrap(),
343            Name::from_str("r.example.com").unwrap(),
344            1,
345            2,
346            3,
347            4,
348            5,
349        );
350
351        let mut bytes = Vec::new();
352        let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
353        assert!(emit(&mut encoder, &rdata).is_ok());
354        let bytes = encoder.into_bytes();
355
356        println!("bytes: {:?}", bytes);
357
358        let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
359        let read_rdata = read(&mut decoder).expect("Decoding error");
360        assert_eq!(rdata, read_rdata);
361    }
362}