pcap_file/pcapng/blocks/
name_resolution.rs

1//! Name Resolution Block (NRB).
2
3use std::borrow::Cow;
4use std::io::{Result as IoResult, Write};
5
6use byteorder_slice::byteorder::WriteBytesExt;
7use byteorder_slice::result::ReadSlice;
8use byteorder_slice::ByteOrder;
9use derive_into_owned::IntoOwned;
10
11use super::block_common::{Block, PcapNgBlock};
12use super::opt_common::{CustomBinaryOption, CustomUtf8Option, PcapNgOption, UnknownOption, WriteOptTo};
13use crate::errors::PcapError;
14
15
16/// The Name Resolution Block (NRB) is used to support the correlation of numeric addresses
17/// (present in the captured packets) and their corresponding canonical names and it is optional.
18#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
19pub struct NameResolutionBlock<'a> {
20    /// Records
21    pub records: Vec<Record<'a>>,
22    /// Options
23    pub options: Vec<NameResolutionOption<'a>>,
24}
25
26impl<'a> PcapNgBlock<'a> for NameResolutionBlock<'a> {
27    fn from_slice<B: ByteOrder>(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> {
28        let mut records = Vec::new();
29
30        loop {
31            let (slice_tmp, record) = Record::from_slice::<B>(slice)?;
32            slice = slice_tmp;
33
34            match record {
35                Record::End => break,
36                _ => records.push(record),
37            }
38        }
39
40        let (rem, options) = NameResolutionOption::opts_from_slice::<B>(slice)?;
41
42        let block = NameResolutionBlock { records, options };
43
44        Ok((rem, block))
45    }
46
47    fn write_to<B: ByteOrder, W: Write>(&self, writer: &mut W) -> IoResult<usize> {
48        let mut len = 0;
49
50        for record in &self.records {
51            len += record.write_to::<B, _>(writer)?;
52        }
53        len += Record::End.write_to::<B, _>(writer)?;
54
55        len += NameResolutionOption::write_opts_to::<B, _>(&self.options, writer)?;
56
57        Ok(len)
58    }
59
60    fn into_block(self) -> Block<'a> {
61        Block::NameResolution(self)
62    }
63}
64
65/// Resolution block record types
66#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
67pub enum Record<'a> {
68    /// End of the records
69    End,
70    /// Ipv4 records
71    Ipv4(Ipv4Record<'a>),
72    /// Ipv6 records
73    Ipv6(Ipv6Record<'a>),
74    /// Unknown records
75    Unknown(UnknownRecord<'a>),
76}
77
78impl<'a> Record<'a> {
79    /// Parse a [`Record`] from a slice
80    pub fn from_slice<B: ByteOrder>(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> {
81        let type_ = slice.read_u16::<B>().map_err(|_| PcapError::IncompleteBuffer)?;
82        let length = slice.read_u16::<B>().map_err(|_| PcapError::IncompleteBuffer)?;
83        let pad_len = (4 - length % 4) % 4;
84
85        if slice.len() < length as usize {
86            return Err(PcapError::InvalidField("NameResolutionBlock: Record length > slice.len()"));
87        }
88        let value = &slice[..length as usize];
89
90        let record = match type_ {
91            0 => {
92                if length != 0 {
93                    return Err(PcapError::InvalidField("NameResolutionBlock: nrb_record_end length != 0"));
94                }
95                Record::End
96            },
97
98            1 => {
99                let record = Ipv4Record::from_slice(value)?;
100                Record::Ipv4(record)
101            },
102
103            2 => {
104                let record = Ipv6Record::from_slice(value)?;
105                Record::Ipv6(record)
106            },
107
108            _ => {
109                let record = UnknownRecord::new(type_, length, value);
110                Record::Unknown(record)
111            },
112        };
113
114        let len = length as usize + pad_len as usize;
115
116        Ok((&slice[len..], record))
117    }
118
119    /// Write a [`Record`] to a writer
120    pub fn write_to<B: ByteOrder, W: Write>(&self, writer: &mut W) -> IoResult<usize> {
121        match self {
122            Record::End => {
123                writer.write_u16::<B>(0)?;
124                writer.write_u16::<B>(0)?;
125
126                Ok(4)
127            },
128
129            Record::Ipv4(a) => {
130                let len = a.write_to::<B, _>(&mut std::io::sink()).unwrap();
131                let pad_len = (4 - len % 4) % 4;
132
133                writer.write_u16::<B>(1)?;
134                writer.write_u16::<B>(len as u16)?;
135                a.write_to::<B, _>(writer)?;
136                writer.write_all(&[0_u8; 3][..pad_len])?;
137
138                Ok(4 + len + pad_len)
139            },
140
141            Record::Ipv6(a) => {
142                let len = a.write_to::<B, _>(&mut std::io::sink()).unwrap();
143                let pad_len = (4 - len % 4) % 4;
144
145                writer.write_u16::<B>(2)?;
146                writer.write_u16::<B>(len as u16)?;
147                a.write_to::<B, _>(writer)?;
148                writer.write_all(&[0_u8; 3][..pad_len])?;
149
150                Ok(4 + len + pad_len)
151            },
152
153            Record::Unknown(a) => {
154                let len = a.value.len();
155                let pad_len = (4 - len % 4) % 4;
156
157                writer.write_u16::<B>(a.type_)?;
158                writer.write_u16::<B>(a.length)?;
159                writer.write_all(&a.value)?;
160                writer.write_all(&[0_u8; 3][..pad_len])?;
161
162                Ok(4 + len + pad_len)
163            },
164        }
165    }
166}
167
168/// Ipv4 records
169#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
170pub struct Ipv4Record<'a> {
171    /// IPv4 Addr
172    pub ip_addr: Cow<'a, [u8]>,
173    /// Fqdn
174    pub names: Vec<Cow<'a, str>>,
175}
176
177impl<'a> Ipv4Record<'a> {
178    /// Parse a [`Ipv4Record`] from a slice
179    pub fn from_slice(mut slice: &'a [u8]) -> Result<Self, PcapError> {
180        if slice.len() < 6 {
181            return Err(PcapError::InvalidField("NameResolutionBlock: Ipv4Record len < 6"));
182        }
183
184        let ip_addr = &slice[..4];
185        slice = &slice[4..];
186
187        let mut names = vec![];
188        for name in slice.split(|&b| b == 0) {
189            if name.is_empty() {
190                break;
191            }
192            names.push(Cow::Borrowed(std::str::from_utf8(name)?));
193        }
194
195        if names.is_empty() {
196            return Err(PcapError::InvalidField("NameResolutionBlock: Ipv4Record without any name"));
197        }
198
199        let record = Ipv4Record { ip_addr: Cow::Borrowed(ip_addr), names };
200
201        Ok(record)
202    }
203
204    /// Write a [`Ipv4Record`] to a writter
205    pub fn write_to<B: ByteOrder, W: Write>(&self, writer: &mut W) -> IoResult<usize> {
206        let mut len = 4;
207
208        writer.write_all(&self.ip_addr)?;
209        for name in &self.names {
210            writer.write_all(name.as_bytes())?;
211            writer.write_u8(0)?;
212
213            len += name.as_bytes().len();
214            len += 1;
215        }
216
217        Ok(len)
218    }
219}
220
221
222/// Ipv6 records
223#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
224pub struct Ipv6Record<'a> {
225    /// Ipv6 addr
226    pub ip_addr: Cow<'a, [u8]>,
227    /// Fqdn
228    pub names: Vec<Cow<'a, str>>,
229}
230
231impl<'a> Ipv6Record<'a> {
232    /// Parse a [`Ipv6Record`] from a slice
233    pub fn from_slice(mut slice: &'a [u8]) -> Result<Self, PcapError> {
234        if slice.len() < 18 {
235            return Err(PcapError::InvalidField("NameResolutionBlock: Ipv6Record len < 18"));
236        }
237
238        let ip_addr = &slice[..16];
239        slice = &slice[16..];
240
241        let mut names = vec![];
242        for name in slice.split(|&b| b == 0) {
243            if name.is_empty() {
244                break;
245            }
246
247            names.push(Cow::Borrowed(std::str::from_utf8(name)?));
248        }
249
250        if names.is_empty() {
251            return Err(PcapError::InvalidField("NameResolutionBlock: Ipv6Record without any name"));
252        }
253
254        let record = Ipv6Record { ip_addr: Cow::Borrowed(ip_addr), names };
255
256        Ok(record)
257    }
258
259    /// Write a [`Ipv6Record`] to a writter
260    pub fn write_to<B: ByteOrder, W: Write>(&self, writer: &mut W) -> IoResult<usize> {
261        let mut len = 16;
262
263        writer.write_all(&self.ip_addr)?;
264        for name in &self.names {
265            writer.write_all(name.as_bytes())?;
266            writer.write_u8(0)?;
267
268            len += name.as_bytes().len();
269            len += 1;
270        }
271
272        Ok(len)
273    }
274}
275
276/// Unknown records
277#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
278pub struct UnknownRecord<'a> {
279    /// Records type
280    pub type_: u16,
281    /// Record length
282    pub length: u16,
283    /// Record body
284    pub value: Cow<'a, [u8]>,
285}
286
287impl<'a> UnknownRecord<'a> {
288    /// Creates a new [`UnknownRecord`]
289    fn new(type_: u16, length: u16, value: &'a [u8]) -> Self {
290        UnknownRecord { type_, length, value: Cow::Borrowed(value) }
291    }
292}
293
294
295/// The Name Resolution Block (NRB) options
296#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
297pub enum NameResolutionOption<'a> {
298    /// The opt_comment option is a UTF-8 string containing human-readable comment text
299    /// that is associated to the current block.
300    Comment(Cow<'a, str>),
301
302    /// The ns_dnsname option is a UTF-8 string containing the name of the machine (DNS server) used to perform the name resolution.
303    NsDnsName(Cow<'a, str>),
304
305    /// The ns_dnsIP4addr option specifies the IPv4 address of the DNS server.
306    NsDnsIpv4Addr(Cow<'a, [u8]>),
307
308    /// The ns_dnsIP6addr option specifies the IPv6 address of the DNS server.
309    NsDnsIpv6Addr(Cow<'a, [u8]>),
310
311    /// Custom option containing binary octets in the Custom Data portion
312    CustomBinary(CustomBinaryOption<'a>),
313
314    /// Custom option containing a UTF-8 string in the Custom Data portion
315    CustomUtf8(CustomUtf8Option<'a>),
316
317    /// Unknown option
318    Unknown(UnknownOption<'a>),
319}
320
321impl<'a> PcapNgOption<'a> for NameResolutionOption<'a> {
322    fn from_slice<B: ByteOrder>(code: u16, length: u16, slice: &'a [u8]) -> Result<Self, PcapError> {
323        let opt = match code {
324            1 => NameResolutionOption::Comment(Cow::Borrowed(std::str::from_utf8(slice)?)),
325            2 => NameResolutionOption::NsDnsName(Cow::Borrowed(std::str::from_utf8(slice)?)),
326            3 => {
327                if slice.len() != 4 {
328                    return Err(PcapError::InvalidField("NameResolutionOption: NsDnsIpv4Addr length != 4"));
329                }
330                NameResolutionOption::NsDnsIpv4Addr(Cow::Borrowed(slice))
331            },
332            4 => {
333                if slice.len() != 16 {
334                    return Err(PcapError::InvalidField("NameResolutionOption: NsDnsIpv6Addr length != 16"));
335                }
336                NameResolutionOption::NsDnsIpv6Addr(Cow::Borrowed(slice))
337            },
338
339            2988 | 19372 => NameResolutionOption::CustomUtf8(CustomUtf8Option::from_slice::<B>(code, slice)?),
340            2989 | 19373 => NameResolutionOption::CustomBinary(CustomBinaryOption::from_slice::<B>(code, slice)?),
341
342            _ => NameResolutionOption::Unknown(UnknownOption::new(code, length, slice)),
343        };
344
345        Ok(opt)
346    }
347
348    fn write_to<B: ByteOrder, W: Write>(&self, writer: &mut W) -> IoResult<usize> {
349        match self {
350            NameResolutionOption::Comment(a) => a.write_opt_to::<B, W>(1, writer),
351            NameResolutionOption::NsDnsName(a) => a.write_opt_to::<B, W>(2, writer),
352            NameResolutionOption::NsDnsIpv4Addr(a) => a.write_opt_to::<B, W>(3, writer),
353            NameResolutionOption::NsDnsIpv6Addr(a) => a.write_opt_to::<B, W>(4, writer),
354            NameResolutionOption::CustomBinary(a) => a.write_opt_to::<B, W>(a.code, writer),
355            NameResolutionOption::CustomUtf8(a) => a.write_opt_to::<B, W>(a.code, writer),
356            NameResolutionOption::Unknown(a) => a.write_opt_to::<B, W>(a.code, writer),
357        }
358    }
359}