domain/rdata/rfc1035/
a.rs

1//! Record data for the A record.
2//!
3//! This is a private module. It’s content is re-exported by the parent.
4
5use crate::base::cmp::CanonicalOrd;
6use crate::base::iana::Rtype;
7use crate::base::net::Ipv4Addr;
8use crate::base::rdata::{ComposeRecordData, ParseRecordData, RecordData};
9use crate::base::scan::{Scanner, ScannerError};
10use crate::base::wire::{Composer, Parse, ParseError};
11use crate::base::zonefile_fmt::{self, Formatter, ZonefileFmt};
12use core::cmp::Ordering;
13use core::convert::Infallible;
14use core::str::FromStr;
15use core::{fmt, str};
16use octseq::octets::OctetsFrom;
17use octseq::parse::Parser;
18
19//------------ A ------------------------------------------------------------
20
21/// A record data.
22///
23/// A records convey the IPv4 address of a host. The wire format is the 32
24/// bit IPv4 address in network byte order. The representation file format
25/// is the usual dotted notation.
26///
27/// The A record type is defined in [RFC 1035, section 3.4.1][1].
28///
29/// [1]: https://tools.ietf.org/html/rfc1035#section-3.4.1
30#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct A {
33    addr: Ipv4Addr,
34}
35
36impl A {
37    /// The rtype of this record data type.
38    pub(crate) const RTYPE: Rtype = Rtype::A;
39}
40
41impl A {
42    /// Creates a new A record data from an IPv4 address.
43    #[must_use]
44    pub fn new(addr: Ipv4Addr) -> A {
45        A { addr }
46    }
47
48    /// Creates a new A record from the IPv4 address components.
49    #[must_use]
50    pub fn from_octets(a: u8, b: u8, c: u8, d: u8) -> A {
51        A::new(Ipv4Addr::new(a, b, c, d))
52    }
53
54    #[must_use]
55    pub fn addr(&self) -> Ipv4Addr {
56        self.addr
57    }
58    pub fn set_addr(&mut self, addr: Ipv4Addr) {
59        self.addr = addr
60    }
61
62    pub(in crate::rdata) fn convert_octets<E>(self) -> Result<Self, E> {
63        Ok(self)
64    }
65
66    pub(in crate::rdata) fn flatten<E>(self) -> Result<Self, E> {
67        Ok(self)
68    }
69
70    pub fn parse<Octs: AsRef<[u8]> + ?Sized>(
71        parser: &mut Parser<Octs>,
72    ) -> Result<Self, ParseError> {
73        Ipv4Addr::parse(parser).map(Self::new)
74    }
75
76    pub fn scan<S: Scanner>(scanner: &mut S) -> Result<Self, S::Error> {
77        let token = scanner.scan_octets()?;
78        let token = str::from_utf8(token.as_ref())
79            .map_err(|_| S::Error::custom("expected IPv4 address"))?;
80        A::from_str(token)
81            .map_err(|_| S::Error::custom("expected IPv4 address"))
82    }
83}
84
85//--- OctetsFrom
86
87impl OctetsFrom<A> for A {
88    type Error = Infallible;
89
90    fn try_octets_from(source: A) -> Result<Self, Self::Error> {
91        Ok(source)
92    }
93}
94
95//--- From and FromStr
96
97impl From<Ipv4Addr> for A {
98    fn from(addr: Ipv4Addr) -> Self {
99        Self::new(addr)
100    }
101}
102
103impl From<A> for Ipv4Addr {
104    fn from(a: A) -> Self {
105        a.addr
106    }
107}
108
109impl FromStr for A {
110    type Err = <Ipv4Addr as FromStr>::Err;
111
112    fn from_str(s: &str) -> Result<Self, Self::Err> {
113        Ipv4Addr::from_str(s).map(A::new)
114    }
115}
116
117//--- CanonicalOrd
118
119impl CanonicalOrd for A {
120    fn canonical_cmp(&self, other: &Self) -> Ordering {
121        self.cmp(other)
122    }
123}
124
125//--- RecordData, ParseRecordData, ComposeRecordData
126
127impl RecordData for A {
128    fn rtype(&self) -> Rtype {
129        A::RTYPE
130    }
131}
132
133impl<'a, Octs: AsRef<[u8]> + ?Sized> ParseRecordData<'a, Octs> for A {
134    fn parse_rdata(
135        rtype: Rtype,
136        parser: &mut Parser<'a, Octs>,
137    ) -> Result<Option<Self>, ParseError> {
138        if rtype == A::RTYPE {
139            Self::parse(parser).map(Some)
140        } else {
141            Ok(None)
142        }
143    }
144}
145
146impl ComposeRecordData for A {
147    fn rdlen(&self, _compress: bool) -> Option<u16> {
148        Some(4)
149    }
150
151    fn compose_rdata<Target: Composer + ?Sized>(
152        &self,
153        target: &mut Target,
154    ) -> Result<(), Target::AppendError> {
155        target.append_slice(&self.addr.octets())
156    }
157
158    fn compose_canonical_rdata<Target: Composer + ?Sized>(
159        &self,
160        target: &mut Target,
161    ) -> Result<(), Target::AppendError> {
162        self.compose_rdata(target)
163    }
164}
165
166//--- Display
167
168impl fmt::Display for A {
169    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170        self.addr.fmt(f)
171    }
172}
173
174//--- ZonefileFmt
175
176impl ZonefileFmt for A {
177    fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
178        p.write_token(self.addr)
179    }
180}
181
182//--- AsRef and AsMut
183
184impl AsRef<Ipv4Addr> for A {
185    fn as_ref(&self) -> &Ipv4Addr {
186        &self.addr
187    }
188}
189
190impl AsMut<Ipv4Addr> for A {
191    fn as_mut(&mut self) -> &mut Ipv4Addr {
192        &mut self.addr
193    }
194}
195
196//============ Testing =======================================================
197
198#[cfg(test)]
199#[cfg(all(feature = "std", feature = "bytes"))]
200mod test {
201    use super::*;
202    use crate::base::rdata::test::{
203        test_compose_parse, test_rdlen, test_scan,
204    };
205
206    #[test]
207    fn a_compose_parse_scan() {
208        let rdata = A::from_octets(1, 2, 3, 4);
209        test_rdlen(&rdata);
210        test_compose_parse(&rdata, A::parse);
211        test_scan(&["1.2.3.4"], A::scan, &rdata);
212    }
213}