Limit recursion when reading name labels from other packet data
[dnssec-prover] / src / ser.rs
1 //! Logic to read and write resource record (streams)
2
3 use alloc::vec::Vec;
4 use alloc::string::String;
5
6 use crate::rr::*;
7
8 pub(crate) fn read_u8(inp: &mut &[u8]) -> Result<u8, ()> {
9         let res = *inp.get(0).ok_or(())?;
10         *inp = &inp[1..];
11         Ok(res)
12 }
13 pub(crate) fn read_u16(inp: &mut &[u8]) -> Result<u16, ()> {
14         if inp.len() < 2 { return Err(()); }
15         let mut bytes = [0; 2];
16         bytes.copy_from_slice(&inp[..2]);
17         *inp = &inp[2..];
18         Ok(u16::from_be_bytes(bytes))
19 }
20 pub(crate) fn read_u32(inp: &mut &[u8]) -> Result<u32, ()> {
21         if inp.len() < 4 { return Err(()); }
22         let mut bytes = [0; 4];
23         bytes.copy_from_slice(&inp[..4]);
24         *inp = &inp[4..];
25         Ok(u32::from_be_bytes(bytes))
26 }
27
28 fn do_read_wire_packet_labels(inp: &mut &[u8], wire_packet: &[u8], name: &mut String, recursion_limit: usize) -> Result<(), ()> {
29         loop {
30                 let len = read_u8(inp)? as usize;
31                 if len == 0 {
32                         if name.is_empty() { *name += "."; }
33                         break;
34                 } else if len >= 0xc0 && recursion_limit > 0 {
35                         let offs = ((len & !0xc0) << 8) | read_u8(inp)? as usize;
36                         if offs >= wire_packet.len() { return Err(()); }
37                         do_read_wire_packet_labels(&mut &wire_packet[offs..], wire_packet, name, recursion_limit - 1)?;
38                         break;
39                 }
40                 if inp.len() <= len { return Err(()); }
41                 *name += core::str::from_utf8(&inp[..len]).map_err(|_| ())?;
42                 *name += ".";
43                 *inp = &inp[len..];
44                 if name.len() > 255 { return Err(()); }
45         }
46         Ok(())
47 }
48
49 fn read_wire_packet_labels(inp: &mut &[u8], wire_packet: &[u8], name: &mut String) -> Result<(), ()> {
50         do_read_wire_packet_labels(inp, wire_packet, name, 255)
51 }
52
53 pub(crate) fn read_wire_packet_name(inp: &mut &[u8], wire_packet: &[u8]) -> Result<Name, ()> {
54         let mut name = String::with_capacity(1024);
55         read_wire_packet_labels(inp, wire_packet, &mut name)?;
56         Ok(name.try_into()?)
57 }
58
59 pub(crate) trait Writer { fn write(&mut self, buf: &[u8]); }
60 impl Writer for Vec<u8> { fn write(&mut self, buf: &[u8]) { self.extend_from_slice(buf); } }
61 #[cfg(feature = "validation")]
62 impl Writer for ring::digest::Context { fn write(&mut self, buf: &[u8]) { self.update(buf); } }
63 pub(crate) fn write_name<W: Writer>(out: &mut W, name: &str) {
64         let canonical_name = name.to_ascii_lowercase();
65         if canonical_name == "." {
66                 out.write(&[0]);
67         } else {
68                 for label in canonical_name.split(".") {
69                         out.write(&(label.len() as u8).to_be_bytes());
70                         out.write(label.as_bytes());
71                 }
72         }
73 }
74 pub(crate) fn name_len(name: &Name) -> u16 {
75         if name.as_str() == "." {
76                 1
77         } else {
78                 let mut res = 0;
79                 for label in name.split(".") {
80                         res += 1 + label.len();
81                 }
82                 res as u16
83         }
84 }
85
86 pub(crate) fn parse_wire_packet_rr(inp: &mut &[u8], wire_packet: &[u8]) -> Result<(RR, u32), ()> {
87         let name = read_wire_packet_name(inp, wire_packet)?;
88         let ty = read_u16(inp)?;
89         let class = read_u16(inp)?;
90         if class != 1 { return Err(()); } // We only support the INternet
91         let ttl = read_u32(inp)?;
92         let data_len = read_u16(inp)? as usize;
93         if inp.len() < data_len { return Err(()); }
94         let data = &inp[..data_len];
95         *inp = &inp[data_len..];
96
97         let rr = match ty {
98                 A::TYPE => RR::A(A::read_from_data(name, data, wire_packet)?),
99                 AAAA::TYPE => RR::AAAA(AAAA::read_from_data(name, data, wire_packet)?),
100                 NS::TYPE => RR::NS(NS::read_from_data(name, data, wire_packet)?),
101                 Txt::TYPE => RR::Txt(Txt::read_from_data(name, data, wire_packet)?),
102                 CName::TYPE => RR::CName(CName::read_from_data(name, data, wire_packet)?),
103                 TLSA::TYPE => RR::TLSA(TLSA::read_from_data(name, data, wire_packet)?),
104                 DnsKey::TYPE => RR::DnsKey(DnsKey::read_from_data(name, data, wire_packet)?),
105                 DS::TYPE => RR::DS(DS::read_from_data(name, data, wire_packet)?),
106                 RRSig::TYPE => RR::RRSig(RRSig::read_from_data(name, data, wire_packet)?),
107                 _ => return Err(()),
108         };
109         Ok((rr, ttl))
110 }
111
112 pub(crate) fn parse_rr(inp: &mut &[u8]) -> Result<RR, ()> {
113         parse_wire_packet_rr(inp, &[]).map(|(rr, _)| rr)
114 }
115
116 /// Parse a stream of [`RR`]s from the format described in [RFC 9102](https://www.rfc-editor.org/rfc/rfc9102.html).
117 ///
118 /// Note that this is only the series of `AuthenticationChain` records, and does not read the
119 /// `ExtSupportLifetime` field at the start of a `DnssecChainExtension`.
120 pub fn parse_rr_stream(mut inp: &[u8]) -> Result<Vec<RR>, ()> {
121         let mut res = Vec::with_capacity(32);
122         while !inp.is_empty() {
123                 res.push(parse_rr(&mut inp)?);
124         }
125         Ok(res)
126 }
127
128 /// Writes the given resource record in its wire encoding to the given `Vec`.
129 ///
130 /// An [RFC 9102](https://www.rfc-editor.org/rfc/rfc9102.html) `AuthenticationChain` is simply a
131 /// series of such records with no additional bytes in between.
132 pub fn write_rr<RR: Record>(rr: &RR, ttl: u32, out: &mut Vec<u8>) {
133         write_name(out, rr.name());
134         out.extend_from_slice(&rr.ty().to_be_bytes());
135         out.extend_from_slice(&1u16.to_be_bytes()); // The INternet class
136         out.extend_from_slice(&ttl.to_be_bytes());
137         rr.write_u16_len_prefixed_data(out);
138 }