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)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize))]
33pub struct Expire(Option<u32>);
34
35impl Expire {
36    /// The option code for this option.
37    pub(super) const CODE: OptionCode = OptionCode::EXPIRE;
38    
39    /// Creates a new expire option with the given optional expire value.
40    #[must_use]
41    pub fn new(expire: Option<u32>) -> Self {
42        Expire(expire)
43    }
44
45    /// Returns the content of the optional expire value.
46    #[must_use]
47    pub fn expire(self) -> Option<u32> {
48        self.0
49    }
50
51    /// Parses a value from its wire format.
52    pub fn parse<Octs: AsRef<[u8]>>(
53        parser: &mut Parser<Octs>
54    ) -> Result<Self, ParseError> {
55        if parser.remaining() == 0 {
56            Ok(Expire::new(None))
57        }
58        else {
59            u32::parse(parser).map(|res| Expire::new(Some(res)))
60        }
61    }
62
63    /// Placeholder for unnecessary octets conversion.
64    ///
65    /// This method only exists for the `AllOptData` macro.
66    pub(super) fn try_octets_from<E>(src: Self) -> Result<Self, E> {
67        Ok(src)
68    }
69}
70
71//--- OptData
72
73impl OptData for Expire {
74    fn code(&self) -> OptionCode {
75        OptionCode::EXPIRE
76    }
77}
78
79impl<'a, Octs: AsRef<[u8]>> ParseOptData<'a, Octs> for Expire {
80    fn parse_option(
81        code: OptionCode,
82        parser: &mut Parser<'a, Octs>,
83    ) -> Result<Option<Self>, ParseError> {
84        if code == OptionCode::EXPIRE {
85            Self::parse(parser).map(Some)
86        }
87        else {
88            Ok(None)
89        }
90    }
91}
92
93impl ComposeOptData for Expire {
94    fn compose_len(&self) -> u16 {
95        match self.0 {
96            Some(_) => u32::COMPOSE_LEN,
97            None => 0,
98        }
99    }
100
101    fn compose_option<Target: OctetsBuilder + ?Sized>(
102        &self, target: &mut Target
103    ) -> Result<(), Target::AppendError> {
104        if let Some(value) = self.0 {
105            value.compose(target)?;
106        }
107        Ok(())
108    }
109}
110
111//--- Display
112
113impl fmt::Display for Expire {
114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115        match self.0 {
116            Some(expire) => expire.fmt(f),
117            None => Ok(())
118        }
119    }
120}
121
122//--- Extended Opt and OptBuilder
123
124impl<Octs: Octets> Opt<Octs> {
125    /// Returns the content of the Expire option if present.
126    ///
127    /// The Expire option allows an authoritative server to signal its own
128    /// expiry time of a zone.
129    pub fn expire(&self) -> Option<Expire> {
130        self.first()
131    }
132}
133
134impl<Target: Composer> OptBuilder<'_, Target> {
135    /// Appends the Expire option.
136    ///
137    /// The Expire option allows an authoritative server to signal its own
138    /// expiry time of a zone.
139    pub fn expire(
140        &mut self, expire: Option<u32>
141    ) -> Result<(), Target::AppendError> {
142        self.push(&Expire::new(expire))
143    }
144}
145
146
147//============ Testing ======================================================
148
149#[cfg(test)]
150#[cfg(all(feature = "std", feature = "bytes"))]
151mod test {
152    use super::*;
153    use super::super::test::test_option_compose_parse;
154    
155    #[test]
156    #[allow(clippy::redundant_closure)] // lifetimes ...
157    fn expire_compose_parse() {
158        test_option_compose_parse(
159            &Expire::new(None),
160            |parser| Expire::parse(parser)
161        );
162        test_option_compose_parse(
163            &Expire::new(Some(12)),
164            |parser| Expire::parse(parser)
165        );
166    }
167}
168