1use crate::{
2 read_exact, read_u16_be, read_u32_be, write_bytes, write_u16_be, write_u32_be, DnsClass,
3 DnsError, DnsName, DnsType,
4};
5use core::fmt::{Debug, Formatter};
6use fixed_buffer::FixedBuf;
7use std::convert::TryFrom;
8use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
9
10#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
55pub enum DnsRecord {
56 A(DnsName, std::net::Ipv4Addr),
57 AAAA(DnsName, std::net::Ipv6Addr),
58 CNAME(DnsName, DnsName),
59 Unknown(DnsName, DnsType),
60}
61impl DnsRecord {
62 pub fn read_rdata<const N: usize>(buf: &mut FixedBuf<N>) -> Result<FixedBuf<65535>, DnsError> {
65 let len = read_u16_be(buf)?;
66 if buf.len() < (len as usize) {
67 return Err(DnsError::Truncated);
68 }
69 let borrowed_rdata = buf.read_bytes(len as usize);
70 let mut rdata: FixedBuf<65535> = FixedBuf::new();
71 rdata
72 .write_bytes(borrowed_rdata)
73 .map_err(|_| DnsError::Unreachable(file!(), line!()))?;
74 Ok(rdata)
75 }
76
77 pub fn write_rdata<const N: usize>(
80 bytes: &[u8],
81 out: &mut FixedBuf<N>,
82 ) -> Result<(), DnsError> {
83 let len =
84 u16::try_from(bytes.len()).map_err(|_| DnsError::Unreachable(file!(), line!()))?;
85 write_u16_be(out, len)?;
86 write_bytes(out, bytes)?;
87 Ok(())
88 }
89
90 pub fn new_a(name: &str, ipv4_addr: &str) -> Result<Self, String> {
94 let dns_name = DnsName::new(name)?;
95 let ip_addr: IpAddr = ipv4_addr
96 .parse()
97 .map_err(|e| format!("failed parsing {ipv4_addr:?} as an IP address: {e}"))?;
98 match ip_addr {
99 IpAddr::V4(addr) => Ok(Self::A(dns_name, addr)),
100 IpAddr::V6(addr) => Err(format!(
101 "cannot create an A record with ipv6 address {addr:?}"
102 )),
103 }
104 }
105
106 pub fn new_aaaa(name: &str, ipv6_addr: &str) -> Result<Self, String> {
110 let dns_name = DnsName::new(name)?;
111 let ip_addr: IpAddr = ipv6_addr
112 .parse()
113 .map_err(|e| format!("failed parsing {ipv6_addr:?} as an IP address: {e}"))?;
114 match ip_addr {
115 IpAddr::V4(addr) => Err(format!(
116 "cannot create an AAAA record with ipv4 address {addr:?}"
117 )),
118 IpAddr::V6(addr) => Ok(Self::AAAA(dns_name, addr)),
119 }
120 }
121
122 pub fn new_cname(name: &str, target: &str) -> Result<Self, String> {
125 let dns_name = DnsName::new(name)?;
126 let dns_name_target = DnsName::new(target)?;
127 Ok(Self::CNAME(dns_name, dns_name_target))
128 }
129
130 #[must_use]
131 pub fn name(&self) -> &DnsName {
132 match self {
133 DnsRecord::A(dns_name, _)
134 | DnsRecord::AAAA(dns_name, _)
135 | DnsRecord::CNAME(dns_name, _)
136 | DnsRecord::Unknown(dns_name, _) => dns_name,
137 }
138 }
139
140 #[must_use]
141 pub fn typ(&self) -> DnsType {
142 match self {
143 DnsRecord::A(_, _) => DnsType::A,
144 DnsRecord::AAAA(_, _) => DnsType::AAAA,
145 DnsRecord::CNAME(_, _) => DnsType::CNAME,
146 DnsRecord::Unknown(_, typ) => DnsType::Unknown(typ.num()),
147 }
148 }
149
150 pub fn read<const N: usize>(buf: &mut FixedBuf<N>) -> Result<Self, DnsError> {
153 let name = DnsName::read(buf)?;
154 let typ = DnsType::read(buf)?;
155 let class = DnsClass::read(buf)?;
156 if class != DnsClass::Internet && class != DnsClass::Any {
157 return Err(DnsError::InvalidClass);
158 }
159 let _ttl_seconds = read_u32_be(buf)?;
160 let mut rdata = Self::read_rdata(buf)?;
161 match typ {
162 DnsType::A => {
163 let octets: [u8; 4] = read_exact(&mut rdata)?;
164 Ok(DnsRecord::A(name, Ipv4Addr::from(octets)))
165 }
166 DnsType::AAAA => {
167 let octets: [u8; 16] = read_exact(&mut rdata)?;
168 Ok(DnsRecord::AAAA(name, Ipv6Addr::from(octets)))
169 }
170 DnsType::CNAME => Ok(DnsRecord::CNAME(name, DnsName::read(&mut rdata)?)),
171 DnsType::MX
172 | DnsType::NS
173 | DnsType::PTR
174 | DnsType::SOA
175 | DnsType::TXT
176 | DnsType::ANY
177 | DnsType::Unknown(_) => Ok(DnsRecord::Unknown(name, typ)),
178 }
179 }
180
181 pub fn write<const N: usize>(&self, out: &mut FixedBuf<N>) -> Result<(), DnsError> {
184 self.name().write(out)?;
185 self.typ().write(out)?;
186 DnsClass::Internet.write(out)?;
187 write_u32_be(out, 300)?; match self {
189 DnsRecord::A(_, ipv4_addr) => Self::write_rdata(&ipv4_addr.octets(), out),
190 DnsRecord::AAAA(_, ipv6_addr) => Self::write_rdata(&ipv6_addr.octets(), out),
191 DnsRecord::CNAME(_, target_name) => {
192 Self::write_rdata(target_name.as_bytes()?.readable(), out)
193 }
194 DnsRecord::Unknown(_, _) => {
195 Err(DnsError::Internal(format!("cannot write record {self:?}")))
196 }
197 }
198 }
199}
200impl Debug for DnsRecord {
201 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
202 match self {
203 DnsRecord::A(name, addr) => write!(f, "DnsRecord::A({name},{addr})"),
204 DnsRecord::AAAA(name, addr) => write!(f, "DnsRecord::AAAA({name},{addr})"),
205 DnsRecord::CNAME(name, target) => write!(f, "DnsRecord::CNAME({name},{target})"),
206 DnsRecord::Unknown(name, typ) => write!(f, "DnsRecord::Unknown({name},{typ})"),
207 }
208 }
209}
210
211#[cfg(test)]
212#[test]
213fn test_dns_record() {
214 use std::net::{Ipv4Addr, Ipv6Addr};
215 assert_eq!(
217 DnsRecord::A(DnsName::new("a.b").unwrap(), Ipv4Addr::new(1, 2, 3, 4)),
218 DnsRecord::new_a("a.b", "1.2.3.4").unwrap()
219 );
220 assert_eq!(
221 DnsRecord::AAAA(
222 DnsName::new("a.b").unwrap(),
223 Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)
224 ),
225 DnsRecord::new_aaaa("a.b", "2001:db8::").unwrap()
226 );
227 assert_eq!(
228 DnsRecord::CNAME(DnsName::new("a.b").unwrap(), DnsName::new("c.d").unwrap()),
229 DnsRecord::new_cname("a.b", "c.d").unwrap()
230 );
231 assert_eq!(
233 "DnsRecord::A(a.b,1.2.3.4)",
234 format!(
235 "{:?}",
236 DnsRecord::A(DnsName::new("a.b").unwrap(), Ipv4Addr::new(1, 2, 3, 4))
237 )
238 );
239 assert_eq!(
240 "DnsRecord::AAAA(a.b,2001:db8::)",
241 format!(
242 "{:?}",
243 DnsRecord::AAAA(
244 DnsName::new("a.b").unwrap(),
245 Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)
246 )
247 )
248 );
249 assert_eq!(
250 "DnsRecord::CNAME(a.b,c.d)",
251 format!(
252 "{:?}",
253 DnsRecord::CNAME(DnsName::new("a.b").unwrap(), DnsName::new("c.d").unwrap())
254 )
255 );
256}