Skip to content

Commit

Permalink
add HINFO record type
Browse files Browse the repository at this point in the history
  • Loading branch information
vlad20012 committed Jan 19, 2021
1 parent ec8f839 commit ea49ece
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 1 deletion.
1 change: 1 addition & 0 deletions crates/client/src/serialize/txt/parse_rdata.rs
Expand Up @@ -45,6 +45,7 @@ impl RDataParser for RData {
RecordType::AXFR => panic!("parsing AXFR doesn't make sense"), // valid panic, never should happen
RecordType::CAA => caa::parse(tokens).map(RData::CAA)?,
RecordType::CNAME => RData::CNAME(name::parse(tokens, origin)?),
RecordType::HINFO => RData::HINFO(hinfo::parse(tokens)?),
RecordType::IXFR => panic!("parsing IXFR doesn't make sense"), // valid panic, never should happen
RecordType::MX => RData::MX(mx::parse(tokens, origin)?),
RecordType::NAPTR => RData::NAPTR(naptr::parse(tokens, origin)?),
Expand Down
51 changes: 51 additions & 0 deletions crates/client/src/serialize/txt/rdata_parsers/hinfo.rs
@@ -0,0 +1,51 @@
// Copyright 2015-2021 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! HINFO record for storing host information

use crate::error::*;
use crate::rr::rdata::HINFO;

/// Parse the RData from a set of Tokens
///
/// ```text
/// IN HINFO DEC-2060 TOPS20
/// IN HINFO VAX-11/780 UNIX
/// ```
pub fn parse<'i, I: Iterator<Item = &'i str>>(mut tokens: I) -> ParseResult<HINFO> {
let cpu = tokens
.next()
.ok_or_else(|| ParseError::from(ParseErrorKind::MissingToken("cpu".to_string())))
.map(ToString::to_string)?;
let os = tokens
.next()
.ok_or_else(|| ParseError::from(ParseErrorKind::MissingToken("os".to_string())))
.map(ToString::to_string)?;
Ok(HINFO::new(cpu, os))
}

#[test]
fn test_parsing() {
// IN HINFO DEC-2060 TOPS20

assert_eq!(
parse(vec!["DEC-2060", "TOPS20"].into_iter())
.expect("failed to parse NAPTR"),
HINFO::new(
"DEC-2060".to_string(),
"TOPS20".to_string()
),
);
}

#[test]
fn test_parsing_fails() {
// IN HINFO DEC-2060 TOPS20

assert!(parse(vec!["DEC-2060"].into_iter()).is_err());
assert!(parse(vec![].into_iter()).is_err());
}
1 change: 1 addition & 0 deletions crates/client/src/serialize/txt/rdata_parsers/mod.rs
Expand Up @@ -22,6 +22,7 @@
pub mod a;
pub mod aaaa;
pub mod caa;
pub mod hinfo;
pub mod mx;
pub mod name;
pub mod naptr;
Expand Down
165 changes: 165 additions & 0 deletions crates/proto/src/rr/rdata/hinfo.rs
@@ -0,0 +1,165 @@
// Copyright 2015-2021 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! HINFO record for storing host information

use std::fmt;

use crate::error::*;
use crate::serialize::binary::*;

/// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987][rfc1035]
///
/// ```text
/// 3.3.2. HINFO RDATA format
///
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / CPU /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / OS /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// where:
///
/// CPU A <character-string> which specifies the CPU type.
///
/// OS A <character-string> which specifies the operating
/// system type.
///
/// Standard values for CPU and OS can be found in [RFC-1010].
///
/// HINFO records are used to acquire general information about a host. The
/// main use is for protocols such as FTP that can use special procedures
/// when talking between machines or operating systems of the same type.
/// ```
///
/// [rfc1035]: https://tools.ietf.org/html/rfc1035
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct HINFO {
cpu: Box<[u8]>,
os: Box<[u8]>,
}

impl HINFO {
pub fn new(cpu: String, os: String) -> HINFO {
HINFO {
cpu: cpu.into_bytes().into_boxed_slice(),
os: os.into_bytes().into_boxed_slice(),
}
}

pub fn from_bytes(cpu: Box<[u8]>, os: Box<[u8]>) -> HINFO {
HINFO { cpu, os }
}

pub fn cpu(&self) -> &Box<[u8]> {
&self.cpu
}

pub fn os(&self) -> &Box<[u8]> {
&self.os
}
}

/// Read the RData from the given Decoder
pub fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<HINFO> {
let cpu = decoder.read_character_data()?
.unverified(/*any data should be validate in HINFO CPU usage*/)
.to_vec()
.into_boxed_slice();
let os = decoder.read_character_data()?
.unverified(/*any data should be validate in HINFO OS usage*/)
.to_vec()
.into_boxed_slice();

Ok(HINFO { cpu, os })
}

/// Write the RData from the given Decoder
pub fn emit(encoder: &mut BinEncoder<'_>, hinfo: &HINFO) -> ProtoResult<()> {
encoder.emit_character_data(&hinfo.cpu)?;
encoder.emit_character_data(&hinfo.os)?;

Ok(())
}

/// [RFC 1033](https://tools.ietf.org/html/rfc1033), DOMAIN OPERATIONS GUIDE, November 1987
///
/// ```text
/// HINFO (Host Info)
///
/// <host> [<ttl>] [<class>] HINFO <hardware> <software>
///
/// The HINFO record gives information about a particular host. The data
/// is two strings separated by whitespace. The first string is a
/// hardware description and the second is software. The hardware is
/// usually a manufacturer name followed by a dash and model designation.
/// The software string is usually the name of the operating system.
///
/// Official HINFO types can be found in the latest Assigned Numbers RFC,
/// the latest of which is RFC-1010. The Hardware type is called the
/// Machine name and the Software type is called the System name.
///
/// Some sample HINFO records:
///
/// SRI-NIC.ARPA. HINFO DEC-2060 TOPS20
/// UCBARPA.Berkeley.EDU. HINFO VAX-11/780 UNIX
/// ```
impl fmt::Display for HINFO {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"{cpu} {os}",
cpu = &String::from_utf8_lossy(&self.cpu),
os = &String::from_utf8_lossy(&self.os)
)?;
Ok(())
}
}

#[cfg(test)]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]

use super::*;

#[test]
pub fn test() {
let rdata = HINFO::new("cpu".to_string(), "os".to_string());

let mut bytes = Vec::new();
let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
assert!(emit(&mut encoder, &rdata).is_ok());
let bytes = encoder.into_bytes();

println!("bytes: {:?}", bytes);

let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
let read_rdata = read(&mut decoder).expect("Decoding error");
assert_eq!(rdata, read_rdata);
}

#[test]
fn test_binary() {
let bin_data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
let rdata = HINFO::from_bytes(
b"cpu".to_vec().into_boxed_slice(),
bin_data.into_boxed_slice(),
);

let mut bytes = Vec::new();
let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
assert!(emit(&mut encoder, &rdata).is_ok());
let bytes = encoder.into_bytes();

println!("bytes: {:?}", bytes);

let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
let read_rdata = read(&mut decoder).expect("Decoding error");
assert_eq!(rdata, read_rdata);
}
}
2 changes: 2 additions & 0 deletions crates/proto/src/rr/rdata/mod.rs
Expand Up @@ -22,6 +22,7 @@
pub mod a;
pub mod aaaa;
pub mod caa;
pub mod hinfo;
pub mod mx;
pub mod name;
pub mod naptr;
Expand All @@ -35,6 +36,7 @@ pub mod tlsa;
pub mod txt;

pub use self::caa::CAA;
pub use self::hinfo::HINFO;
pub use self::mx::MX;
pub use self::naptr::NAPTR;
pub use self::null::NULL;
Expand Down
40 changes: 39 additions & 1 deletion crates/proto/src/rr/record_data.rs
Expand Up @@ -27,7 +27,7 @@ use log::{trace, warn};

use super::domain::Name;
use super::rdata;
use super::rdata::{CAA, MX, NAPTR, NULL, OPENPGPKEY, OPT, SOA, SRV, SSHFP, TLSA, TXT};
use super::rdata::{CAA, HINFO, MX, NAPTR, NULL, OPENPGPKEY, OPT, SOA, SRV, SSHFP, TLSA, TXT};
use super::record_type::RecordType;
use crate::error::*;
use crate::serialize::binary::*;
Expand Down Expand Up @@ -169,6 +169,32 @@ pub enum RData {
/// ```
CNAME(Name),

/// ```text
/// 3.3.2. HINFO RDATA format
///
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / CPU /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// / OS /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// where:
///
/// CPU A <character-string> which specifies the CPU type.
///
/// OS A <character-string> which specifies the operating
/// system type.
///
/// Standard values for CPU and OS can be found in [RFC-1010].
///
/// HINFO records are used to acquire general information about a host. The
/// main use is for protocols such as FTP that can use special procedures
/// when talking between machines or operating systems of the same type.
/// ```
///
/// `HINFO` is also used by [RFC 8482](https://tools.ietf.org/html/rfc8482)
HINFO(HINFO),

/// ```text
/// 3.3.9. MX RDATA format
///
Expand Down Expand Up @@ -642,6 +668,10 @@ impl RData {
trace!("reading CNAME");
rdata::name::read(decoder).map(RData::CNAME)
}
RecordType::HINFO => {
trace!("reading HINFO");
rdata::hinfo::read(decoder).map(RData::HINFO)
}
RecordType::ZERO => {
trace!("reading EMPTY");
return Ok(RData::ZERO);
Expand Down Expand Up @@ -800,6 +830,7 @@ impl RData {
RData::CNAME(ref name) | RData::NS(ref name) | RData::PTR(ref name) => {
rdata::name::emit(encoder, name)
}
RData::HINFO(ref hinfo) => rdata::hinfo::emit(encoder, hinfo),
RData::ZERO => Ok(()),
// to_lowercase for rfc4034 and rfc6840
RData::MX(ref mx) => rdata::mx::emit(encoder, mx),
Expand Down Expand Up @@ -838,6 +869,7 @@ impl RData {
RData::ANAME(..) => RecordType::ANAME,
RData::CAA(..) => RecordType::CAA,
RData::CNAME(..) => RecordType::CNAME,
RData::HINFO(..) => RecordType::HINFO,
RData::MX(..) => RecordType::MX,
RData::NAPTR(..) => RecordType::NAPTR,
RData::NS(..) => RecordType::NS,
Expand Down Expand Up @@ -880,6 +912,7 @@ impl fmt::Display for RData {
RData::CAA(ref caa) => w(f, caa),
// to_lowercase for rfc4034 and rfc6840
RData::CNAME(ref name) | RData::NS(ref name) | RData::PTR(ref name) => w(f, name),
RData::HINFO(ref hinfo) => w(f, hinfo),
RData::ZERO => Ok(()),
// to_lowercase for rfc4034 and rfc6840
RData::MX(ref mx) => w(f, mx),
Expand Down Expand Up @@ -1025,6 +1058,10 @@ mod tests {
b'm', b'p', b'l', b'e', 3, b'c', b'o', b'm', 0,
],
),
(
RData::HINFO(HINFO::new("cpu".to_string(), "os".to_string())),
vec![3, b'c', b'p', b'u', 2, b'o', b's'],
),
]
}

Expand Down Expand Up @@ -1120,6 +1157,7 @@ mod tests {
RData::ANAME(..) => RecordType::ANAME,
RData::CAA(..) => RecordType::CAA,
RData::CNAME(..) => RecordType::CNAME,
RData::HINFO(..) => RecordType::HINFO,
RData::MX(..) => RecordType::MX,
RData::NAPTR(..) => RecordType::NAPTR,
RData::NS(..) => RecordType::NS,
Expand Down

0 comments on commit ea49ece

Please sign in to comment.