trust_dns_proto/rr/rdata/mx.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//! mail exchange, email, record
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.9. MX RDATA format
32///
33/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
34/// | PREFERENCE |
35/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
36/// / EXCHANGE /
37/// / /
38/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
39///
40/// MX records cause type A additional section processing for the host
41/// specified by EXCHANGE. The use of MX RRs is explained in detail in
42/// [RFC-974].
43///
44/// ```
45#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
46#[derive(Debug, PartialEq, Eq, Hash, Clone)]
47pub struct MX {
48 preference: u16,
49 exchange: Name,
50}
51
52impl MX {
53 /// Constructs a new MX RData
54 ///
55 /// # Arguments
56 ///
57 /// * `preference` - weight of this MX record as opposed to others, lower values have the higher preference
58 /// * `exchange` - Name labels for the mail server
59 ///
60 /// # Returns
61 ///
62 /// A new MX RData for use in a Resource Record
63 pub fn new(preference: u16, exchange: Name) -> Self {
64 Self {
65 preference,
66 exchange,
67 }
68 }
69
70 /// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
71 ///
72 /// ```text
73 /// PREFERENCE A 16 bit integer which specifies the preference given to
74 /// this RR among others at the same owner. Lower values
75 /// are preferred.
76 /// ```
77 pub fn preference(&self) -> u16 {
78 self.preference
79 }
80
81 /// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
82 ///
83 /// ```text
84 /// EXCHANGE A <domain-name> which specifies a host willing to act as
85 /// a mail exchange for the owner name.
86 /// ```
87 pub fn exchange(&self) -> &Name {
88 &self.exchange
89 }
90}
91
92/// Read the RData from the given Decoder
93pub fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<MX> {
94 Ok(MX::new(
95 decoder.read_u16()?.unverified(/*any u16 is valid*/),
96 Name::read(decoder)?,
97 ))
98}
99
100/// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-6), DNSSEC Resource Records, March 2005
101///
102/// ```text
103/// 6.2. Canonical RR Form
104///
105/// For the purposes of DNS security, the canonical form of an RR is the
106/// wire format of the RR where:
107///
108/// ...
109///
110/// 3. if the type of the RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
111/// HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
112/// SRV, DNAME, A6, RRSIG, or NSEC (rfc6840 removes NSEC), all uppercase
113/// US-ASCII letters in the DNS names contained within the RDATA are replaced
114/// by the corresponding lowercase US-ASCII letters;
115/// ```
116pub fn emit(encoder: &mut BinEncoder<'_>, mx: &MX) -> ProtoResult<()> {
117 let is_canonical_names = encoder.is_canonical_names();
118 encoder.emit_u16(mx.preference())?;
119 mx.exchange()
120 .emit_with_lowercase(encoder, is_canonical_names)?;
121 Ok(())
122}
123
124/// [RFC 1033](https://tools.ietf.org/html/rfc1033), DOMAIN OPERATIONS GUIDE, November 1987
125
126/// ```text
127/// MX (Mail Exchanger) (See RFC-974 for more details.)
128///
129/// <name> [<ttl>] [<class>] MX <preference> <host>
130///
131/// MX records specify where mail for a domain name should be delivered.
132/// There may be multiple MX records for a particular name. The
133/// preference value specifies the order a mailer should try multiple MX
134/// records when delivering mail. Zero is the highest preference.
135/// Multiple records for the same name may have the same preference.
136///
137/// A host BAR.FOO.COM may want its mail to be delivered to the host
138/// PO.FOO.COM and would then use the MX record:
139///
140/// BAR.FOO.COM. MX 10 PO.FOO.COM.
141///
142/// A host BAZ.FOO.COM may want its mail to be delivered to one of three
143/// different machines, in the following order:
144///
145/// BAZ.FOO.COM. MX 10 PO1.FOO.COM.
146/// MX 20 PO2.FOO.COM.
147/// MX 30 PO3.FOO.COM.
148///
149/// An entire domain of hosts not connected to the Internet may want
150/// their mail to go through a mail gateway that knows how to deliver
151/// mail to them. If they would like mail addressed to any host in the
152/// domain FOO.COM to go through the mail gateway they might use:
153///
154/// FOO.COM. MX 10 RELAY.CS.NET.
155/// *.FOO.COM. MX 20 RELAY.CS.NET.
156///
157/// Note that you can specify a wildcard in the MX record to match on
158/// anything in FOO.COM, but that it won't match a plain FOO.COM.
159impl fmt::Display for MX {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
161 write!(f, "{pref} {ex}", pref = self.preference, ex = self.exchange)
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 #![allow(clippy::dbg_macro, clippy::print_stdout)]
168
169 use super::*;
170
171 #[test]
172 fn test() {
173 use std::str::FromStr;
174
175 let rdata = MX::new(16, Name::from_str("mail.example.com").unwrap());
176
177 let mut bytes = Vec::new();
178 let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
179 assert!(emit(&mut encoder, &rdata).is_ok());
180 let bytes = encoder.into_bytes();
181
182 println!("bytes: {:?}", bytes);
183
184 let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
185 let read_rdata = read(&mut decoder).expect("Decoding error");
186 assert_eq!(rdata, read_rdata);
187 }
188}