simple_dns/dns/rdata/
soa.rs1use std::{collections::HashMap, convert::TryInto};
2
3use crate::dns::{name::Label, Name, WireFormat};
4
5use super::RR;
6
7#[derive(Debug, PartialEq, Eq, Hash, Clone)]
9pub struct SOA<'a> {
10 pub mname: Name<'a>,
12 pub rname: Name<'a>,
14 pub serial: u32,
17 pub refresh: i32,
19 pub retry: i32,
21 pub expire: i32,
23 pub minimum: u32,
25}
26
27impl<'a> RR for SOA<'a> {
28 const TYPE_CODE: u16 = 6;
29}
30
31impl<'a> SOA<'a> {
32 pub fn into_owned<'b>(self) -> SOA<'b> {
34 SOA {
35 mname: self.mname.into_owned(),
36 rname: self.rname.into_owned(),
37 serial: self.serial,
38 refresh: self.refresh,
39 retry: self.retry,
40 expire: self.expire,
41 minimum: self.minimum,
42 }
43 }
44
45 fn write_common<T: std::io::Write>(&self, out: &mut T) -> crate::Result<()> {
46 out.write_all(&self.serial.to_be_bytes())?;
47 out.write_all(&self.refresh.to_be_bytes())?;
48 out.write_all(&self.retry.to_be_bytes())?;
49 out.write_all(&self.expire.to_be_bytes())?;
50 out.write_all(&self.minimum.to_be_bytes())?;
51
52 Ok(())
53 }
54}
55
56impl<'a> WireFormat<'a> for SOA<'a> {
57 fn parse(data: &'a [u8], position: &mut usize) -> crate::Result<Self>
58 where
59 Self: Sized,
60 {
61 let mname = Name::parse(data, position)?;
62 let rname = Name::parse(data, position)?;
63
64 let serial = u32::from_be_bytes(data[*position..*position + 4].try_into()?);
65 let refresh = i32::from_be_bytes(data[*position + 4..*position + 8].try_into()?);
66 let retry = i32::from_be_bytes(data[*position + 8..*position + 12].try_into()?);
67 let expire = i32::from_be_bytes(data[*position + 12..*position + 16].try_into()?);
68 let minimum = u32::from_be_bytes(data[*position + 16..*position + 20].try_into()?);
69
70 *position += 20;
71
72 Ok(Self {
73 mname,
74 rname,
75 serial,
76 refresh,
77 retry,
78 expire,
79 minimum,
80 })
81 }
82
83 fn write_to<T: std::io::Write>(&self, out: &mut T) -> crate::Result<()> {
84 self.mname.write_to(out)?;
85 self.rname.write_to(out)?;
86 self.write_common(out)
87 }
88
89 fn write_compressed_to<T: std::io::Write + std::io::Seek>(
90 &'a self,
91 out: &mut T,
92 name_refs: &mut HashMap<&'a [Label<'a>], usize>,
93 ) -> crate::Result<()> {
94 self.mname.write_compressed_to(out, name_refs)?;
95 self.rname.write_compressed_to(out, name_refs)?;
96 self.write_common(out)
97 }
98
99 fn len(&self) -> usize {
100 self.mname.len() + self.rname.len() + 20
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use crate::{rdata::RData, ResourceRecord};
107
108 use super::*;
109 #[test]
110 fn parse_and_write_soa() {
111 let soa = SOA {
112 mname: Name::new("mname.soa.com").unwrap(),
113 rname: Name::new("rname.soa.com").unwrap(),
114 serial: 1,
115 refresh: 2,
116 retry: 3,
117 expire: 4,
118 minimum: 5,
119 };
120
121 let mut data = Vec::new();
122 assert!(soa.write_to(&mut data).is_ok());
123
124 let soa = SOA::parse(&data, &mut 0);
125 assert!(soa.is_ok());
126 let soa = soa.unwrap();
127
128 assert_eq!(data.len(), soa.len());
129 }
130
131 #[test]
132 fn parse_sample() -> Result<(), Box<dyn std::error::Error>> {
133 let sample_file = std::fs::read("samples/zonefile/SOA.sample")?;
134
135 let sample_rdata = match ResourceRecord::parse(&sample_file, &mut 0)?.rdata {
136 RData::SOA(rdata) => rdata,
137 _ => unreachable!(),
138 };
139
140 assert_eq!(sample_rdata.mname, "VENERA.sample".try_into()?);
141 assert_eq!(sample_rdata.rname, "Action\\.domains.sample".try_into()?);
142 assert_eq!(sample_rdata.serial, 20);
143 assert_eq!(sample_rdata.refresh, 7200);
144 assert_eq!(sample_rdata.retry, 600);
145 assert_eq!(sample_rdata.expire, 3600000);
146 assert_eq!(sample_rdata.minimum, 60);
147
148 Ok(())
149 }
150}