|
@@ -0,0 +1,117 @@
|
|
|
+use log::*;
|
|
|
+
|
|
|
+use crate::wire::*;
|
|
|
+
|
|
|
+
|
|
|
+/// A (an?) **HINFO** _(host information)_ record, which contains the CPU and
|
|
|
+/// OS information about a host.
|
|
|
+///
|
|
|
+/// It also gets used as the response for an `ANY` query, if it is blocked.
|
|
|
+///
|
|
|
+/// # References
|
|
|
+///
|
|
|
+/// - [RFC 1035 §3.3.2](https://tools.ietf.org/html/rfc1035) — Domain Names, Implementation and Specification (November 1987)
|
|
|
+/// - [RFC 8482 §6](https://tools.ietf.org/html/rfc8482#section-6) — Providing Minimal-Sized Responses to DNS Queries That Have QTYPE=ANY (January 2019)
|
|
|
+#[derive(PartialEq, Debug)]
|
|
|
+pub struct HINFO {
|
|
|
+
|
|
|
+ /// The CPU field, specifying the CPU type.
|
|
|
+ pub cpu: String,
|
|
|
+
|
|
|
+ /// The OS field, specifying the operating system.
|
|
|
+ pub os: String,
|
|
|
+}
|
|
|
+
|
|
|
+impl Wire for HINFO {
|
|
|
+ const NAME: &'static str = "HINFO";
|
|
|
+ const RR_TYPE: u16 = 13;
|
|
|
+
|
|
|
+ #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
|
|
|
+ fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
|
|
|
+
|
|
|
+ let cpu_length = c.read_u8()?;
|
|
|
+ trace!("Parsed CPU length -> {:?}", cpu_length);
|
|
|
+
|
|
|
+ let mut cpu_buffer = Vec::with_capacity(cpu_length.into());
|
|
|
+ for _ in 0 .. cpu_length {
|
|
|
+ cpu_buffer.push(c.read_u8()?);
|
|
|
+ }
|
|
|
+
|
|
|
+ let cpu = String::from_utf8_lossy(&cpu_buffer).to_string();
|
|
|
+ trace!("Parsed CPU -> {:?}", cpu);
|
|
|
+
|
|
|
+ let os_length = c.read_u8()?;
|
|
|
+ trace!("Parsed OS length -> {:?}", os_length);
|
|
|
+
|
|
|
+ let mut os_buffer = Vec::with_capacity(os_length.into());
|
|
|
+ for _ in 0 .. os_length {
|
|
|
+ os_buffer.push(c.read_u8()?);
|
|
|
+ }
|
|
|
+
|
|
|
+ let os = String::from_utf8_lossy(&os_buffer).to_string();
|
|
|
+ trace!("Parsed OS -> {:?}", cpu);
|
|
|
+
|
|
|
+ let length_after_labels = 1 + u16::from(cpu_length) + 1 + u16::from(os_length);
|
|
|
+ if stated_length == length_after_labels {
|
|
|
+ trace!("Length is correct");
|
|
|
+ Ok(Self { cpu, os })
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ warn!("Length is incorrect (stated length {:?}, cpu plus length {:?}", stated_length, length_after_labels);
|
|
|
+ Err(WireError::WrongLabelLength { stated_length, length_after_labels })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod test {
|
|
|
+ use super::*;
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn parses() {
|
|
|
+ let buf = &[
|
|
|
+ 0x0e, // cpu length
|
|
|
+ 0x73, 0x6f, 0x6d, 0x65, 0x2d, 0x6b, 0x69, 0x6e, 0x64, 0x61, 0x2d,
|
|
|
+ 0x63, 0x70, 0x75, // cpu
|
|
|
+ 0x0d, // os length
|
|
|
+ 0x73, 0x6f, 0x6d, 0x65, 0x2d, 0x6b, 0x69, 0x6e, 0x64, 0x61, 0x2d,
|
|
|
+ 0x6f, 0x73, // os
|
|
|
+ ];
|
|
|
+
|
|
|
+ assert_eq!(HINFO::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
|
|
|
+ HINFO {
|
|
|
+ cpu: String::from("some-kinda-cpu"),
|
|
|
+ os: String::from("some-kinda-os"),
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn incorrect_record_length() {
|
|
|
+ let buf = &[
|
|
|
+ 0x03, // cpu length
|
|
|
+ 0x65, 0x66, 0x67, // cpu
|
|
|
+ 0x03, // os length
|
|
|
+ 0x68, 0x69, 0x70, // os
|
|
|
+ ];
|
|
|
+
|
|
|
+ assert_eq!(HINFO::read(6, &mut Cursor::new(buf)),
|
|
|
+ Err(WireError::WrongLabelLength { stated_length: 6, length_after_labels: 8 }));
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn record_empty() {
|
|
|
+ assert_eq!(HINFO::read(0, &mut Cursor::new(&[])),
|
|
|
+ Err(WireError::IO));
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn buffer_ends_abruptly() {
|
|
|
+ let buf = &[
|
|
|
+ 0x14, 0x0A, 0x0B, 0x0C, // 32-bit CPU
|
|
|
+ ];
|
|
|
+
|
|
|
+ assert_eq!(HINFO::read(23, &mut Cursor::new(buf)),
|
|
|
+ Err(WireError::IO));
|
|
|
+ }
|
|
|
+}
|