1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use std::{collections::HashMap, convert::TryInto};

use crate::dns::{DnsPacketContent, Name};

/// SOA records are used to mark the start of a zone of authority
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct SOA<'a> {
    /// The [Name](`Name`) of the name server that was the original or primary source of data for this zone.
    pub mname: Name<'a>,
    /// A [Name](`Name`) which specifies the mailbox of the person responsible for this zone.
    pub rname: Name<'a>,
    /// The unsigned 32 bit version number of the original copy of the zone.  Zone transfers preserve this value.  
    /// This value wraps and should be compared using sequence space arithmetic.
    pub serial: u32,
    /// A 32 bit time interval before the zone should be refreshed.
    pub refresh: i32,
    /// A 32 bit time interval that should elapse before a failed refresh should be retried.
    pub retry: i32,
    /// A 32 bit time value that specifies the upper limit on the time interval that can elapse before the zone is no longer authoritative.
    pub expire: i32,
    /// The unsigned 32 bit minimum TTL field that should be exported with any RR from this zone.
    pub minimum: u32,
}

impl<'a> SOA<'a> {
    fn append_commom(&self, out: &mut Vec<u8>) -> crate::Result<()> {
        out.extend(self.serial.to_be_bytes());
        out.extend(self.refresh.to_be_bytes());
        out.extend(self.retry.to_be_bytes());
        out.extend(self.expire.to_be_bytes());
        out.extend(self.minimum.to_be_bytes());
        Ok(())
    }

    /// Transforms the inner data into it's owned type
    pub fn into_owned<'b>(self) -> SOA<'b> {
        SOA {
            mname: self.mname.into_owned(),
            rname: self.rname.into_owned(),
            serial: self.serial,
            refresh: self.refresh,
            retry: self.retry,
            expire: self.expire,
            minimum: self.minimum,
        }
    }
}

impl<'a> DnsPacketContent<'a> for SOA<'a> {
    fn parse(data: &'a [u8], position: usize) -> crate::Result<Self>
    where
        Self: Sized,
    {
        let mname = Name::parse(data, position)?;
        let rname = Name::parse(data, position + mname.len())?;
        let offset = mname.len() + rname.len();

        let serial = u32::from_be_bytes(data[offset..offset + 4].try_into()?);
        let refresh = i32::from_be_bytes(data[offset + 4..offset + 8].try_into()?);
        let retry = i32::from_be_bytes(data[offset + 8..offset + 12].try_into()?);
        let expire = i32::from_be_bytes(data[offset + 12..offset + 16].try_into()?);
        let minimum = u32::from_be_bytes(data[offset + 16..offset + 20].try_into()?);

        Ok(Self {
            mname,
            rname,
            serial,
            refresh,
            retry,
            expire,
            minimum,
        })
    }

    fn append_to_vec(&self, out: &mut Vec<u8>) -> crate::Result<()> {
        self.mname.append_to_vec(out)?;
        self.rname.append_to_vec(out)?;
        self.append_commom(out)
    }

    fn len(&self) -> usize {
        self.mname.len() + self.rname.len() + 20
    }

    fn compress_append_to_vec(
        &self,
        out: &mut Vec<u8>,
        name_refs: &mut HashMap<u64, usize>,
    ) -> crate::Result<()> {
        self.mname.compress_append_to_vec(out, name_refs)?;
        self.rname.compress_append_to_vec(out, name_refs)?;
        self.append_commom(out)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn parse_and_write_soa() {
        let soa = SOA {
            mname: Name::new("mname.soa.com").unwrap(),
            rname: Name::new("rname.soa.com").unwrap(),
            serial: 1,
            refresh: 2,
            retry: 3,
            expire: 4,
            minimum: 5,
        };

        let mut data = Vec::new();
        assert!(soa.append_to_vec(&mut data).is_ok());

        let soa = SOA::parse(&data, 0);
        assert!(soa.is_ok());
        let soa = soa.unwrap();

        assert_eq!(data.len(), soa.len());
    }
}