simple_dns/dns/rdata/
rrsig.rs

1use crate::{
2    bytes_buffer::BytesBuffer,
3    dns::{Name, WireFormat},
4    lib::Cow,
5    lib::Write,
6};
7
8use super::RR;
9
10/// An RRSIG record see [rfc4034](https://www.rfc-editor.org/rfc/rfc4034#section-3)
11#[derive(Debug, PartialEq, Eq, Hash, Clone)]
12pub struct RRSIG<'a> {
13    /// The type of RR that is covered by this RRSIG
14    pub type_covered: u16,
15    /// The cryptographic algorithm used for the signature
16    pub algorithm: u8,
17    /// The number of labels in the original RRSIG RR owner name
18    pub labels: u8,
19    /// The original TTL value of the covered record
20    pub original_ttl: u32,
21    /// When the signature expires (seconds since Jan 1 1970)
22    pub signature_expiration: u32,
23    /// When the signature was created (seconds since Jan 1 1970)
24    pub signature_inception: u32,
25    /// Key tag value of the DNSKEY RR that validates this signature
26    pub key_tag: u16,
27    /// The domain name of the zone that contains the signed RRset
28    pub signer_name: Name<'a>,
29    /// The cryptographic signature that covers the RRSIG RDATA
30    pub signature: Cow<'a, [u8]>,
31}
32
33impl RR for RRSIG<'_> {
34    const TYPE_CODE: u16 = 46;
35}
36
37impl<'a> WireFormat<'a> for RRSIG<'a> {
38    const MINIMUM_LEN: usize = 18;
39
40    fn parse(data: &mut BytesBuffer<'a>) -> crate::Result<Self>
41    where
42        Self: Sized,
43    {
44        let type_covered = data.get_u16()?;
45        let algorithm = data.get_u8()?;
46        let labels = data.get_u8()?;
47        let original_ttl = data.get_u32()?;
48        let signature_expiration = data.get_u32()?;
49        let signature_inception = data.get_u32()?;
50        let key_tag = data.get_u16()?;
51
52        let signer_name = Name::parse(data)?;
53        let signature = Cow::Borrowed(data.get_remaining());
54
55        Ok(Self {
56            type_covered,
57            algorithm,
58            labels,
59            original_ttl,
60            signature_expiration,
61            signature_inception,
62            key_tag,
63            signer_name,
64            signature,
65        })
66    }
67
68    fn write_to<T: Write>(&self, out: &mut T) -> crate::Result<()> {
69        out.write_all(&self.type_covered.to_be_bytes())?;
70        out.write_all(&[self.algorithm])?;
71        out.write_all(&[self.labels])?;
72        out.write_all(&self.original_ttl.to_be_bytes())?;
73        out.write_all(&self.signature_expiration.to_be_bytes())?;
74        out.write_all(&self.signature_inception.to_be_bytes())?;
75        out.write_all(&self.key_tag.to_be_bytes())?;
76        self.signer_name.write_to(out)?;
77        out.write_all(&self.signature)?;
78
79        Ok(())
80    }
81
82    fn len(&self) -> usize {
83        self.signer_name.len() + self.signature.len() + Self::MINIMUM_LEN
84    }
85}
86
87impl RRSIG<'_> {
88    /// Transforms the inner data into its owned type
89    pub fn into_owned<'b>(self) -> RRSIG<'b> {
90        RRSIG {
91            type_covered: self.type_covered,
92            algorithm: self.algorithm,
93            labels: self.labels,
94            original_ttl: self.original_ttl,
95            signature_expiration: self.signature_expiration,
96            signature_inception: self.signature_inception,
97            key_tag: self.key_tag,
98            signer_name: self.signer_name.into_owned(),
99            signature: Cow::Owned(self.signature.into_owned()),
100        }
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use crate::{lib::Vec, rdata::A};
108
109    #[test]
110    fn parse_and_write_rrsig() {
111        let rrsig = RRSIG {
112            type_covered: A::TYPE_CODE,
113            algorithm: 5,
114            labels: 3,
115            original_ttl: 86400,
116            signature_expiration: 1045762263,
117            signature_inception: 1048354263,
118            key_tag: 2642,
119            signer_name: Name::new("example.com.").unwrap(),
120            signature: b"TEST".to_vec().into(),
121        };
122
123        let mut data = Vec::new();
124        rrsig.write_to(&mut data).unwrap();
125        let rrsig2 = RRSIG::parse(&mut data[..].into()).unwrap();
126        assert_eq!(rrsig, rrsig2);
127    }
128
129    #[test]
130    #[cfg(feature = "std")]
131    fn parse_sample() -> Result<(), Box<dyn std::error::Error>> {
132        use crate::{rdata::RData, ResourceRecord};
133        let sample_file = std::fs::read("samples/zonefile/RRSIG.sample")?;
134
135        let sample_rdata = match ResourceRecord::parse(&mut sample_file[..].into())?.rdata {
136            RData::RRSIG(rdata) => rdata,
137            _ => unreachable!(),
138        };
139
140        assert_eq!(sample_rdata.type_covered, A::TYPE_CODE);
141        assert_eq!(sample_rdata.algorithm, 5);
142        assert_eq!(sample_rdata.labels, 3);
143        assert_eq!(sample_rdata.original_ttl, 86400);
144        assert_eq!(sample_rdata.signature_expiration, 1048354263);
145        assert_eq!(sample_rdata.signature_inception, 1045762263);
146        assert_eq!(sample_rdata.key_tag, 2642);
147        assert_eq!(sample_rdata.signer_name, Name::new("example.com.")?);
148        assert_eq!(*sample_rdata.signature, *b"\xa0\x90\x75\x5b\xa5\x8d\x1a\xff\xa5\x76\xf4\x37\x58\x31\xb4\x31\x09\x20\xe4\x81\x21\x8d\x18\xa9\xf1\x64\xeb\x3d\x81\xaf\xd3\xb8\x75\xd3\xc7\x54\x28\x63\x1e\x0c\xf2\xa2\x8d\x50\x87\x5f\x70\xc3\x29\xd7\xdb\xfa\xfe\xa8\x07\xdc\x1f\xba\x1d\xc3\x4c\x95\xd4\x01\xf2\x3f\x33\x4c\xe6\x3b\xfc\xf3\xf1\xb5\xb4\x47\x39\xe5\xf0\xed\xed\x18\xd6\xb3\x3f\x04\x0a\x91\x13\x76\xd1\x73\xd7\x57\xa9\xf0\xc1\xfa\x17\x98\x94\x1b\xb0\xb3\x6b\x2d\xf9\x06\x27\x90\xfa\x7f\x01\x66\xf2\x73\x7e\xea\x90\x73\x78\x34\x1f\xb1\x2d\xc0\xa7\x7a");
149
150        Ok(())
151    }
152}