domain/base/opt/
expire.rs

1//! EDNS Options for signalling zone expire times.
2//!
3//! The option in this module, [`Expire`], allows a authoritative server to
4//! signal when a zone expires independently of the SOA’s expire field. This
5//! allows to determine the expire time when a secondary server is updating
6//! a zone from another secondary server rather than directly from the
7//! primary.
8//!
9//! This option is defined in [RFC 7314](https://tools.ietf.org/html/rfc7314).
10
11use core::fmt;
12use super::super::iana::OptionCode;
13use super::super::message_builder::OptBuilder;
14use super::super::wire::{Compose, Composer, Parse, ParseError};
15use super::{Opt, OptData, ComposeOptData, ParseOptData};
16use octseq::builder::OctetsBuilder;
17use octseq::octets::Octets;
18use octseq::parse::Parser;
19
20
21//------------ Expire --------------------------------------------------------
22
23/// Option data for the Expire EDNS option.
24///
25/// The option’s data consists of an optional `u32`. The value is omitted if
26/// the option is added to a query to request it being included by the server
27/// in an answer. In this answer the value should be present and indicates the
28/// expire time of the zone on the server.
29///
30/// See [RFC 7314](https://tools.ietf.org/html/rfc7314) for details.
31#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
32pub struct Expire(Option<u32>);
33
34impl Expire {
35    /// Creates a new expire option with the given optional expire value.
36    #[must_use]
37    pub fn new(expire: Option<u32>) -> Self {
38        Expire(expire)
39    }
40
41    /// Returns the content of the optional expire value.
42    #[must_use]
43    pub fn expire(self) -> Option<u32> {
44        self.0
45    }
46
47    /// Parses a value from its wire format.
48    pub fn parse<Octs: AsRef<[u8]>>(
49        parser: &mut Parser<Octs>
50    ) -> Result<Self, ParseError> {
51        if parser.remaining() == 0 {
52            Ok(Expire::new(None))
53        }
54        else {
55            u32::parse(parser).map(|res| Expire::new(Some(res)))
56        }
57    }
58}
59
60//--- OptData
61
62impl OptData for Expire {
63    fn code(&self) -> OptionCode {
64        OptionCode::Expire
65    }
66}
67
68impl<'a, Octs: AsRef<[u8]>> ParseOptData<'a, Octs> for Expire {
69    fn parse_option(
70        code: OptionCode,
71        parser: &mut Parser<'a, Octs>,
72    ) -> Result<Option<Self>, ParseError> {
73        if code == OptionCode::Expire {
74            Self::parse(parser).map(Some)
75        }
76        else {
77            Ok(None)
78        }
79    }
80}
81
82impl ComposeOptData for Expire {
83    fn compose_len(&self) -> u16 {
84        match self.0 {
85            Some(_) => u32::COMPOSE_LEN,
86            None => 0,
87        }
88    }
89
90    fn compose_option<Target: OctetsBuilder + ?Sized>(
91        &self, target: &mut Target
92    ) -> Result<(), Target::AppendError> {
93        if let Some(value) = self.0 {
94            value.compose(target)?;
95        }
96        Ok(())
97    }
98}
99
100//--- Display
101
102impl fmt::Display for Expire {
103    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104        match self.0 {
105            Some(expire) => expire.fmt(f),
106            None => Ok(())
107        }
108    }
109}
110
111//--- Extended Opt and OptBuilder
112
113impl<Octs: Octets> Opt<Octs> {
114    /// Returns the content of the Expire option if present.
115    ///
116    /// The Expire option allows an authoritative server to signal its own
117    /// expiry time of a zone.
118    pub fn expire(&self) -> Option<Expire> {
119        self.first()
120    }
121}
122
123impl<'a, Target: Composer> OptBuilder<'a, Target> {
124    /// Appends the Expire option.
125    ///
126    /// The Expire option allows an authoritative server to signal its own
127    /// expiry time of a zone.
128    pub fn expire(
129        &mut self, expire: Option<u32>
130    ) -> Result<(), Target::AppendError> {
131        self.push(&Expire::new(expire))
132    }
133}
134
135
136//============ Testing ======================================================
137
138#[cfg(test)]
139#[cfg(all(feature = "std", feature = "bytes"))]
140mod test {
141    use super::*;
142    use super::super::test::test_option_compose_parse;
143    
144    #[test]
145    #[allow(clippy::redundant_closure)] // lifetimes ...
146    fn expire_compose_parse() {
147        test_option_compose_parse(
148            &Expire::new(None),
149            |parser| Expire::parse(parser)
150        );
151        test_option_compose_parse(
152            &Expire::new(Some(12)),
153            |parser| Expire::parse(parser)
154        );
155    }
156}
157