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}