Skip to main content

dnsbox/rdata/
mx.rs

1//! MX record data (RFC 1035 §3.3.9).
2
3use core::fmt;
4
5use super::{ComposeRdata, ParseRdata, ParseRdataText};
6use crate::name::Name;
7use crate::wire::{Composer, NameEncoding, OutBuf, WireReader};
8use crate::zone::Scanner;
9use crate::{Result, Rtype};
10
11/// `MX` record data: a mail exchange (RFC 1035 §3.3.9).
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
13pub struct Mx<'a> {
14    /// Preference; lower values are preferred.
15    pub preference: u16,
16    /// The mail exchange host.
17    pub exchange: Name<'a>,
18}
19
20impl<'a> ParseRdata<'a> for Mx<'a> {
21    const RTYPE: Rtype = Rtype::MX;
22
23    fn parse_rdata(rdata: &mut WireReader<'a>) -> Result<Self> {
24        Ok(Mx {
25            preference: rdata.read_u16()?,
26            exchange: rdata.read_name()?,
27        })
28    }
29}
30
31impl ComposeRdata for Mx<'_> {
32    fn rtype(&self) -> Rtype {
33        Rtype::MX
34    }
35
36    fn compose_rdata<C: Composer + ?Sized>(&self, c: &mut C) -> Result<()> {
37        c.put_u16(self.preference)?;
38        c.put_name(self.exchange, NameEncoding::Compressible)
39    }
40}
41
42impl fmt::Display for Mx<'_> {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        write!(f, "{} {}", self.preference, self.exchange)
45    }
46}
47
48impl ParseRdataText for Mx<'_> {
49    /// `<preference> <exchange>` (RFC 1035 §3.3.9).
50    fn parse_text<B: OutBuf + ?Sized>(s: &mut Scanner<'_>, out: &mut B) -> Result<()> {
51        out.put_u16(s.u16()?)?;
52        s.name_into(out, NameEncoding::Compressible)
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use crate::rdata::tests::{text_error, text_round_trip};
59    use crate::{Error, Rtype};
60
61    #[test]
62    fn text() {
63        // RFC 1035 §5.3: "MX 10 VENERA" with origin ISI.EDU.
64        text_round_trip(
65            Rtype::MX,
66            "10 VENERA",
67            b"\x00\x0a\x06VENERA\x07example\x00",
68            "10 VENERA.example.",
69        );
70        text_round_trip(Rtype::MX, "0 .", b"\x00\x00\x00", "0 .");
71        text_round_trip(
72            Rtype::MX,
73            "( 65535\n mx.example.net. ) ; comment",
74            b"\xff\xff\x02mx\x07example\x03net\x00",
75            "65535 mx.example.net.",
76        );
77        assert_eq!(text_error(Rtype::MX, "65536 a."), Error::InvalidText);
78        assert_eq!(text_error(Rtype::MX, "-1 a."), Error::InvalidText);
79        assert_eq!(text_error(Rtype::MX, "10"), Error::UnexpectedEof);
80        assert_eq!(text_error(Rtype::MX, "10 a. b."), Error::InvalidText);
81        assert_eq!(text_error(Rtype::MX, "10 a..b"), Error::EmptyLabel);
82        assert_eq!(text_error(Rtype::MX, "x a."), Error::InvalidText);
83    }
84}