use log::*; use crate::strings::{Labels, ReadLabels}; use crate::wire::*; /// A **SOA** _(start of authority)_ record, which contains administrative /// information about the zone the domain is in. These are returned when a /// server does not have a record for a domain. /// /// # References /// /// - [RFC 1035 §3.3.13](https://tools.ietf.org/html/rfc1035) — Domain Names, Implementation and Specification (November 1987) #[derive(PartialEq, Debug, Clone)] pub struct SOA { /// The primary master name for this server. pub mname: Labels, /// The e-mail address of the administrator responsible for this DNS zone. pub rname: Labels, /// A serial number for this DNS zone. pub serial: u32, /// Duration, in seconds, after which secondary nameservers should query /// the master for _its_ SOA record. pub refresh_interval: u32, /// Duration, in seconds, after which secondary nameservers should retry /// requesting the serial number from the master if it does not respond. /// It should be less than `refresh`. pub retry_interval: u32, /// Duration, in seconds, after which secondary nameservers should stop /// answering requests for this zone if the master does not respond. /// It should be greater than the sum of `refresh` and `retry`. pub expire_limit: u32, /// Duration, in seconds, of the minimum time-to-live. pub minimum_ttl: u32, } impl Wire for SOA { const NAME: &'static str = "SOA"; const RR_TYPE: u16 = 6; #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)] fn read(len: u16, c: &mut Cursor<&[u8]>) -> Result { let (mname, mname_len) = c.read_labels()?; trace!("Parsed mname -> {:?}", mname); let (rname, rname_len) = c.read_labels()?; trace!("Parsed rname -> {:?}", rname); let serial = c.read_u32::()?; trace!("Parsed serial -> {:?}", serial); let refresh_interval = c.read_u32::()?; trace!("Parsed refresh interval -> {:?}", refresh_interval); let retry_interval = c.read_u32::()?; trace!("Parsed retry interval -> {:?}", retry_interval); let expire_limit = c.read_u32::()?; trace!("Parsed expire limit -> {:?}", expire_limit); let minimum_ttl = c.read_u32::()?; trace!("Parsed minimum TTL -> {:?}", minimum_ttl); let got_len = 4 * 5 + mname_len + rname_len; if len == got_len { trace!("Length is correct"); Ok(Self { mname, rname, serial, refresh_interval, retry_interval, expire_limit, minimum_ttl, }) } else { warn!("Length is incorrect (record length {:?}, mname plus rname plus fields length {:?})", len, got_len); Err(WireError::WrongLabelLength { expected: len, got: got_len }) } } } #[cfg(test)] mod test { use super::*; #[test] fn parses() { let buf = &[ 0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65, // mname 0x00, // mname terminator 0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65, // rname 0x00, // rname terminator 0x5d, 0x3c, 0xef, 0x02, // Serial 0x00, 0x01, 0x51, 0x80, // Refresh interval 0x00, 0x00, 0x1c, 0x20, // Retry interval 0x00, 0x09, 0x3a, 0x80, // Expire limit 0x00, 0x00, 0x01, 0x2c, // Minimum TTL ]; assert_eq!(SOA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), SOA { mname: Labels::encode("bsago.me").unwrap(), rname: Labels::encode("bsago.me").unwrap(), serial: 1564274434, refresh_interval: 86400, retry_interval: 7200, expire_limit: 604800, minimum_ttl: 300, }); } #[test] fn record_empty() { assert_eq!(SOA::read(0, &mut Cursor::new(&[])), Err(WireError::IO)); } #[test] fn buffer_ends_abruptly() { let buf = &[ 0x05, 0x62, // the start of an mname ]; assert_eq!(SOA::read(23, &mut Cursor::new(buf)), Err(WireError::IO)); } }